webgl自学笔记——深度监测与混合
这一章中关于webgl中颜色的使用我们将深入研究。我们将从研究颜色在webgl和essl中如何被组装和获取开始。然后我们讨论在物体、光照和场景中颜色的使用。这之后我们将看到当一个物体在另一个物体前面是webgl如何来实现物体碰撞,这是通过深度检测来实现的。相反透明度混合允许我们结合所有物体的颜色当一个物体与另一个物体啮合时。我们将用透明度混合来创建透明物体。
这一章主要讨论:
- 在物体上使用颜色
- 为光源分配颜色
- 在ESSL中使用多光源
- 深度检测和z缓冲区
- 混合方法和公式
- 使用face culling来创建透明物体
webgl在RGB颜色模型中还包含第四个属性,这个属性被称为开端通道。扩展后的模型称为RGBA模型,A是为支持alpha。a的取值范围时0.0-1.0,跟其他三个一样。下图代表颜色空间。横轴代表结合rgb能够得到的颜色,纵轴代表alpha通道。
alpha为颜色带来了额外的信息。这个信息影响颜色渲染在屏幕上的方式。通常来说alpha影响颜色的透明度。一般来说我们的颜色都是不透明的,但有一些情况我们得考虑获得半透明颜色。
在webgl 3D场景中我们到处都在使用颜色:
- Objects: 3d物体可以通过为每一个顶点选择一个颜色来上色,或者为整个物体选择一个颜色。这通常由材料的diffuse属性决定
- Lights:我们可以使用颜色不是白色的环境光和反射光属性。
- Scene:场景的背景色可以通过gl.clearColor方法来改变。稍后我们将看到当我们使用透明物体时需要做一些特殊的操作。
·最终的颜色在片元着色器中通过设置特殊的gl_FragColor来得到。如果这个物体的所有片元都拥有同样的颜色,我们可以说这个问题有一个常量颜色。否则这个物体有per-vertex 颜色
Constant coloring
为了获得常量颜色我们把需要得到的颜色放在一个uniform存储器中,这个变量直接传递给片元着色器。这个uniform通常被称为物体的漫反射材料属性。我们也可以结合物体的法线和光源信息来获取Lambert反射系数。我们使用兰伯特系数通过依赖反射光与光源的夹角来改变反射颜色。
下图第一个是没有结合反射系数的物体,第二个是结合反射系数后的颜色。
Per-vertex coloring
在医学和工程可视化应用中,通常能够找到跟他们要渲染的物体的顶点对应的颜色地图。这些地图为每一个顶点分配一个单独的颜色。为了实现per-vertex 着色我们需要在顶点着色器中定义一个attribute来存储顶点的颜色。
attribute vec4 aVertexColor
下一步是将aVertexColor属性分配给一个varying变量,以便能够进入片元着色器中。varying变量是可以被自动插值的。因此每一个片元的颜色都是根据为绕它的顶点的颜色按权重分配的。
如果我们想让我们的颜色地图能够反映光照条件,我们可以让每一个顶点颜色乘以光照的漫反射部分。得到的结果将被分配到可以将它传递到片元着色器的varying变量中。
左侧图片没有反映光源位置信息,右侧图片反映了光源位置信息
Per-fragment coloring
我们可以为每一个像素分配一个随机的颜色,但是ESSL中并没有一个内置的随机数函数。我们可以通过其他方式来获得。这里不展开
注意:当启用alpha通道时需要的是 "attribute vec4 aVertexColor",然后要启用混合功能gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
Use of color in lights
颜色也是光照的属性;在第3章中我们看到光照属性的数量取决于为场景选择的光照模型。比如使用兰伯特反射模型我们只需要一个着色器uniform量:diffuse;相反在Phong反射模型中每一个光源需要拥有三个属性:环境光、漫反射光、镜面光。
(另外,光源位置通常也在着色器中作为一个unifrom变量,以便知道光源位置,所以Phong模型在点位置光源方式中由四个uniform:ambient、diffuse、specular、position;对于方向光这个uniform变量表示的是光源的方向)
Using multiple lights and the scalability problem
Phong反射模型下一个光源需要四个uniform变量,如果我们有多个光源意味着我们需要的uniform会成倍数上升,而每台机器能够获取的最大unifrom数目是有限的;我们可以通过gl.getParameter()中传入gl.MAX_VERTEX_UNIFORM_VECTORS和gl.MAX_FRAGMENT_UNIFORM_VECTORS参数来获取能够使用的最大uniform数目。
Using uniform arrays to handle multiple lights
将多光源的uniform分别存储增加代码量并且难以维护,ESSL中允许我们使用unifrom 数组。这项技术可以允许我们在着色器中引入光照数组来管理多光源。通过这种方式我们可以在着色器总通过便利光照数组来计算光照颜色。虽然我们仍在需要在JavaScript中定义多个光源对象,但是传递给着色器时就变得简单了。
所以在着色器总我们需要定义:
unifrom vec3 uPositionLight[3]
注意:ESSL中不支持动态初始化unifrom 数组
也就意味着我们无法动态创建多光源
然后传递给着色器:
directional point lights
方向点光源,创建这种光源的秘诀在于利用顶点法线和光源位置向量做减法生成新的法线向量,将新法线向量传递给片元着色器:
所有的颜色都是通过光照模型算出来的
Use of color in the scene
前面我们提到过alpha通道可以存放关于被绘制的物体颜色的透明度信息。但是除非alpha混合被激活否则无法创建透明物体。当我们在场景中有多个物体时,事情就变得复杂起来。我们将学会如何在场景中绘制透明物体和非透明物体。
Transparency
获得透明物体的第一步要使用Polygon stepping(多边形点刻)技术。这项技术会抛弃一些片元所以能够穿过物体看到东西。就像在你的物体表面刻出许多小孔。
在webgl中我们需要使用ESSL的discard命令在片元着色器中去除掉一些片元。直接设置alpha厚webgl并不能自动产生透明效果。
创建透明物体意味着我们要改表已经写入帧缓冲区的片元。比如在一个场景中,这里有一个透明的物体在一个非透明物体的前面。如果这个场景被正确渲染出来,那么我们需要能够透过透明物体看到非透明物体。因此在远处和近处物体叠盖处的片元需要以某种方式来创建出透明效果。
为了实现透明效果我们需要学习两个总要概念:深度检测、alpha混合。
Updated rendering pipeline
对于片元来说一旦他们被片元着色器处理之后,深度检测和alpha混合就是两个可选的阶段。如果深度检测没有被激活,所有的片元对alpha混合来说都自动可用。如果深度检测开启了,这些在深度检测结果为false的片元都将被渲染管线自动抛弃在其他操作中不在可用。这意味着被抛弃的片元将不会被渲染。
添加了深度检测或alpha混合后的渲染管线如下图所示:
Depth testing
每一个被片元着色器处理后的片元都携带深度信息。尽管片元显示在屏幕上是二维的,深度值保留了这个片元距离相机屏幕的距离。深度值被存储在一个特殊的webgl缓冲区中称为深度缓冲区或z-buffer。
如果深度检测被启用了,在片元着色器计算完片元之后,下一步将进行深度检测。开启深度检测:
gl.enable(gl.DEPTH_TEST)
深度检测通过将一个片元色深度信息与相同片元位置处已经保存的在深度缓冲区中的深度值进行比较。深度检测将决定这个片元是否将进入渲染管线未来的处理中。只有通过深度检测的片元会被进一步处理,没有通过深度检测的片元将被抛弃。
一般情况下,深度值小的片元将被接受(距离屏幕更近)。
深度测试是一种呈现顺序的交换操作。这意味着无论先呈现哪个对象,只要启用深度测试,我们总是有一个一致的场景
开启深度检测后,距离屏幕近(深度值)小的物体被呈现出来。距离屏幕远的物体在这一叠盖区中被抛弃
默认情况下深度检测是禁用的,我们可以启用深度检测,默认情况下用gl.LESS方法来做深度测试,我们可以通过更改深度检测的方式来做一些特殊效果:gl.depthFunc(gl.LESS)
Alpha blending
通过深度检测的片元可以进入alpha混合阶段,然而如果深度检测被关闭,所有片元都可以进入alpha混合阶段。alpha通过:gl.enable(gl.BLEND)开启。
对于每一个合格的片元alpha混合操作读取在帧缓冲中的当前位置片元的颜色,然后通过在片元着色器预先计算好的颜色和目前帧缓冲中已有的颜色进行线性差值来得到一个的颜色。
Blending function
启用alpha混合只有下一步就是定义一个混合函数。这个函数将决定如何将我们将渲染的物体的颜色与当前已经存在帧缓冲中的颜色进行混合处理。
我们使用如下公式来做alpha混合
注意渲染顺序将决定在上面公式中的source和destination片元;将要渲染到frame buffer中的片元称为源,已经在framebuffer中的片元称为目标。
Separate blending functions
我们也可以将rgb与alpha单独计算,使用gl.blendFuncSeperate函数,比如我们可以为rgb和alpha使用如下两个独立公式
二者单独使用不同方式来计算:
函数:
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ZERO)
Blend equation
我们还可以为颜色混合使用不同的混合公式:
alpha 混合相关API:
gl.enable(gl.BLEND)// 启用混合
gl.blendFunc(sw, dw) // 决定对颜色模型中哪一部分进行操作
gl.blendFuncSeperate(sw_rgb, dw_rgb, sw_a, dw_a);// 决定如何对rgb和alpha分离计算
gl.blendEquation(mode)// 决定源和目标颜色的结合方式
gl.blendEquationSeparate(modeRGB, modeAlpha)// 决定分离方式中源和目标的结合方式
gl.blendColor设置混合颜色
gl.getParameter(pname)// 获取混合相关参数
Creating transparent objects
为了创建透明物体,我们需要:
- 启用alpha混合并选择混合插值函数
- 从远到近的渲染物体(物体的绘制顺序决定谁是源谁是目标)
对于同一个物体,我们使用面剔除方法来创建透明度。(因为一个物体没有其他物体来做颜色混合,所以处理思路是将一个物体的前景面和后镜面分别当成两个单独物体来绘制,这样就可以用两个物体做alpha混合。)
个人理解
深度检测通过将一个片元色深度信息与相同片元位置处已经保存的在深度缓冲区中的深度值进行比较。深度检测将决定这个片元是否将进入渲染管线未来的处理中。只有通过深度检测的片元会被进一步处理,没有通过深度检测的片元将被抛弃。
一般情况下,深度值小的片元将被接受(距离屏幕更近)。
深度测试是一种呈现顺序的交换操作。这意味着无论先呈现哪个对象,只要启用深度测试,我们总是有一个一致的场景
开启深度检测后,距离屏幕近(深度值)小的物体被呈现出来。距离屏幕远的物体在这一叠盖区中被抛弃
如果没有开启深度监测那么就跟渲染顺序相关;后渲染的物体会遮挡住先渲染的物体
确切来说深度监测判断物体远近不是以世界坐标为基准来判断的而是以投影坐标来判断的
颜色混合发生在深度监测之后,只有通过深度检测的片元才会进入到颜色混合阶段。
如果没有开启blend,那么透明物体也会把不透明物体给遮挡住
如果开启颜色混合,那么只有通过深度监测的片元才会进入颜色混合阶段,没有通过深度监测的片元将会被抛弃
这时候因为先绘制了cone,而在这个矩阵下,cone离着相机更近,所以有sphere部分片元没有通过深度测试,直接被抛弃
如果先绘制sphere,cone所有片元都会通过深度测试,与已有的颜色缓冲发生颜色混合。
如果开启颜色混合,物体的绘制顺序最终会影响颜色混合的呈现效果
如果关闭深度监测,那么混合效果完全跟渲染顺序相关(整体效果会跟物体位置相关,如下面两图都是先绘制椎体混合颜色是相同的,但是有近大远小效果,所以球形和椎体的最终呈现效果有所差异)
下面是先绘制球形
webgl自学笔记——深度监测与混合的更多相关文章
- webgl自学笔记——矩阵变换
这章主要探讨矩阵,这些矩阵代表了应用在我们场景上的变换,允许我们移动物体.然而在webGL api中并没有一个专门的camera对象,只有矩阵.好消息是使用矩阵来取代相机对象能让webgl在很多复杂动 ...
- webgl自学笔记——几何图形
3D应用的基础元素: 1.canvas,它是渲染场景的占位符.标准html的canvas元素 2.Objects,这里指的是组成一个场景的所有3d实体.这些实体都由三角形组成.webgl中使用Buff ...
- webgl自学笔记——光照
在Webgl中我们使用顶点着色器和片元着色器来为我们的场景创建光照模型.着色器允许我们使用数学模型来控制如何照亮我们的场景. 最好有线性代数的相关知识. 本章中: 光源.法线.材料 光照和着色的区别 ...
- 《Linux内核设计与实现》课本第四章自学笔记——20135203齐岳
<Linux内核设计与实现>课本第四章自学笔记 进程调度 By20135203齐岳 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统.多任务操作系统使多个进程处于堵 ...
- 《Linux内核设计与实现》课本第三章自学笔记——20135203齐岳
<Linux内核设计与实现>课本第三章自学笔记 进程管理 By20135203齐岳 进程 进程:处于执行期的程序.包括代码段和打开的文件.挂起的信号.内核内部数据.处理器状态一个或多个具有 ...
- 《Linux内核设计与实现》课本第十八章自学笔记——20135203齐岳
<Linux内核设计与实现>课本第十八章自学笔记 By20135203齐岳 通过打印来调试 printk()是内核提供的格式化打印函数,除了和C库提供的printf()函数功能相同外还有一 ...
- python自学笔记
python自学笔记 python自学笔记 1.输出 2.输入 3.零碎 4.数据结构 4.1 list 类比于java中的数组 4.2 tuple 元祖 5.条件判断和循环 5.1 条件判断 5.2 ...
- ssh自学笔记
Ssh自学笔记 Ssh简介 传统的网络服务程序,如:ftp.pop和telnet在本质上都是不安全的,因为它们在网络上用明文传送口令和数据,别有用心的人非常容易就可以截获这些口令和数据.而且,这些服务 ...
- JavaScript高级程序设计之自学笔记(一)————Array类型
以下为自学笔记. 一.Array类型 创建数组的基本方式有两种: 1.1第一种是使用Array构造函数(可省略new操作符). 1.2第二种是使用数组字面量表示法. 二.数组的访问 2.1访问方法 在 ...
随机推荐
- SSM-MyBatis-09:Mybatis中SqlSession的close为什么能造成事务的回滚
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 经过上几次的查找,笔者我就简单的说一下查找的思路,留给读者自己实践 同样找到sqlsession的实现类,-- ...
- [ Java面试题 ]持久层篇
1.什么是ORM? 对象关系映射(Object-Relational Mapping,简称ORM)是一种为了解决程序的面向对象模型与数据库的关系模型互不匹配问题的技术: 简单的说,ORM是通过使用描 ...
- Fedora Linux中解决“xxx不在sudoers文件中”
问题描述: 在Fedora中执行一些操作时需要使用root权限,当我使用命令: sudo 想在普通用户中临时获得root权限时,却被提示: "xxx 不在 sudoers 文件中.此事将被报 ...
- Javascript高级程序设计复习——第五章引用类型 【原创】
5.1 Object类型 1:创建Object实例的两种方式 ①new构造法 var obj1 = new Object(); 注意大写!不传递参数时可以省略圆括号 obj1.hehe = &quo ...
- java面向对象中四种权限(private,protected,public,友好型)详解
转自http://blog.csdn.net/a1237871112/article/details/50926975 及http://blog.csdn.net/blackmanren/articl ...
- bzoj 1098 poi2007 办公楼 bfs+链表
题意很好理解,求给出图反图的联通块个数. 考虑这样一个事情:一个联通块里的点,最多只会被遍历一次,再遍历时没有任何意义 所以用链表来存,每遍历到一个点就将该点删掉 #include<cstdio ...
- 华盛顿邮报:FBI 屡次夸大了“手机加密威胁”的数字
<华盛顿邮报>周二报道称,美国联邦调查局(FBI)严重夸大了由加密手机所造成的问题.以去年为例,该机构调查人员声称被大约 7800 部涉嫌犯罪活动的加密设备挡在了门外,而准确的数字应该在 ...
- Java中clone方法的使用
什么是clone 在实际编程过程中,我们常常要遇到这种情况:有一个对象object1,在某一时刻object1中已经包含了一些有效值,此时可能会需要一个和object1完全相同新对象object2,并 ...
- Protobuf 语言指南(proto3)
Protobuf 语言指南(proto3) Protocol Buffer是Google的语言中立的,平台中立的,可扩展机制的,用于序列化结构化数据 - 对比XML,但更小,更快,更简单.您可以定义数 ...
- 在.NET Core中使用Exceptionless分布式日志收集框架
一.Exceptionless简介 Exceptionless 是一个开源的实时的日志收集框架,它可以应用在基于 ASP.NET,ASP.NET Core,Web Api,Web Forms,WPF, ...