1.如何实现雾化
实现雾化的方式由多种,这里使用最简单的一种:线性雾化(linear fog)。在线性雾化中,某一点的雾化程度取决于它与视点之间的距离,距离越远雾化程度越高。线性雾化有起点和终点,起点表示开始雾化之处,终点表示完全雾化之处两点之间某一点的雾化程度与该点与视点的距离呈线性关系。比终点更远的点完全雾化了,即完全看不见了。
某一点雾化的程度可以被定义为雾化因子(fog factor),并在线性雾化公式中被计算出来:
<雾化因子> = (<终点> - <当前点与视点间的距离>) / (<终点> - <起点>)
这里:
<起点> <= <当前点与视点间的距离> <= <终点>
雾化因子为1.0,表示该点完全没有被雾化,可以很清晰地看到此处的物体。如果为0.0, 就表示改点完全被雾化了。起点因子的线形图如下所示:
2.片元着色器中包含雾化因子的片元颜色计算
计算公式如下:
<片元颜色> = <物体表面颜色> * <雾化因子> + <雾化颜色> * (1 - <雾化因子>)
3.使用顶点的w分量作为当前点与视点间的距离
之前,我们并未显示使用过gl_Position的w分量,实际上,这个w分量的值就是顶点的视图坐标的z分量乘以-1。在视图坐标系中,视点在原点,视线沿着Z轴负方向,观察者看到的物体其视图坐标系值z分量都是负的,而gl_Position的w分量正好是z分量值乘以-1,所以可以直接使用该值来近似顶点和视点建的距离。
4.绘制圆形的点
为了将矩形削成圆形,需要知道每个片元在光栅化过程中的坐标。在第5章的一个示例程序中,在片元着色器中通过内置变量gl_FragCoord来访问片元的坐标。实际上,片元着色器还提供了另外一个内置变量gl_PointCoord。这个变量可以帮助我们绘制圆形的点。片元着色器内置变量:
变量类型和名称/描述
vec4 gl_FragCoord/片元在窗口坐标
vec4 gl_PointCoord/片元在被绘制的点内的坐标(从0.0到1.0)
gl_PointCoord坐标值的区间从0.0到1.0,如下图所示。为了将矩形削成圆形,需要将与中心点(0.5, 0.5)距离超过0.5,也就是将圆外的片元剔除掉。在片元着色器中,可以使用discard语句来放弃当前片元。
在片元着色器中的代码实现方式如下:
var FSHADER_SOURCE = '#ifdef GL_ES\n' + 'precision mediump float;\n' + '#endif\n' + 'void main(){\n' + ' float distance = distance(gl_PointCoord, vec2(0.5, 0.5));\n' + ' if (distance <= 0.5){\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + ' }else{\n' + ' discard;' + ' }\n' + '}\n';
5.α混合
我们要让物体实现半透明,实现这种效果需要用到颜色的a分量。该功能被称为a混合(alpha blending)或混合(blending),WebGL已经内置了该功能,值需要开启即可。
如何实现a混合?
1.开启混合功能:gl.enable(gl.BLEND)。
2.指定混合函数:gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)。
如果我们只设置了颜色的第四个分量,是看不到透明效果的,必须要执行上面两个步骤。
6.gl.blendFunc(src_factor, dst_factor)
通过参数src_factor和dst_factor指定进行混合操作的函数,混合后的颜色如下计算:
<混合后的颜色> = <源颜色> * src_factor + <目标颜色> * dst_factor
参数:
src_factor:指定源颜色在混合颜色重的权重因子,如下表所示
dst_factor:指定目标颜色在混合后颜色重的权重因子,如下表所示
常量/R分量的系数/G分量的系数/B分量的系数
gl.ZERO/0.0/0.0/0.0
gl.ONE/1.0/1.0/1.0
gl.SRC_COLOR/Rs/Gs/Bs
gl.ONE_MINUS_SRC_COLOR/(1-Rs)/(1-Gs)/(1-Bs)
gl.DST_COLOR/Rd/Gd/Bd
gl.ONE_MINUS_DST_COLOR/(1-Rd)/(1-Gd)/(1-Bd)
gl.SRC_ALPHA/As/As/As
gl.ONE_MINUS_SRC_ALPHA/(1-As)/(1-As)/(1-As)
gl.DST_ALPHA/Ad/Ad/Ad
gl.ONE_MINUS_DST_ALPHA/(1-Ad)/(1-Ad)/(1-Ad)
gl.SRC_ALPHA_SATUREATE/min(As,Ad)/min(As,Ad)/min(As,Ad)
上表中,(Rs,Gs,Bs, As)和(Rd,Gd,Bd,Ad)表示源颜色和目标颜色的各个分量。
假如源颜色是半透明的绿色(0.0, 1.0, 0.0, 0.4),目标颜色是普通的黄色(1.0, 1.0, 0.0, 1.0),调用函数:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
计算过程如下图所示:
7.透明和不透明物体共存在使用a混合功能时,我们屏蔽掉了隐藏面消除功能(去掉了代码gl.enable(gl.DEPTH_TEST)。关闭隐藏面消除功能只是一个粗暴的解决方案,并不能满足实际的需求。实际上,通过某些机制,可以同时实现隐藏面消除和半透明效果。我们只需要:
1.开启隐藏面消除功能:gl.enable(gl.DEPTH_TEST)。
2.绘制所有不透明的物体(a为1.0)。
3.锁定用于进行隐藏面消除的深度缓冲区的写入操作,使之只读。调用:
gl.depthMask(false);
4.绘制所有半透明的物体(a小于1.0),注意他们应当按照深度排序,然后从后向前绘制。
5.释放深度缓冲区,使之可读可写。调用:
gl.depthMask(true)
8.gl.depthMask(mask)
锁定或释放深度缓冲区的写入操作。参数:
mask:指定是锁定深度缓冲区的写入操作(false),还是释放之(true)
9.渲染到文理