雾化/圆点
什么是雾化?
答: 在三维图形学中,雾化(fog) 指的是远处的物体看上去较为模糊的现象。
如何实现雾化?
线性雾化(linear fog)
线性雾化表示 雾化程度与该点与视点的距离呈线性关系。
雾化程度取决于 它与视点之间的距离。
- 雾化起点 开始雾化之处
- 雾化终点 完全雾化之处
雾化因子(fog factor)
某一点雾化的程度可以被定义为 雾化因子(fog factor)
雾化因子计算公式
雾化因子 = ( 终点 - 当前点与视点间的距离 )/(终点 - 起点 )
雾化因子与视点距离线性关系图
物体离视点越远,雾化因子越小,物体越模糊。
根据雾化因子计算片元颜色公式
片元颜色 = 【物体表面颜色 * 雾化因子】 + 【雾的颜色 * (1 - 雾化因子)】
雾化程序
实现思路
注意此程序的雾化因子是在世界坐标系下计算的。
- 顶点着色器计算出当前顶点与视点距离,传入片元着色器。
- 片元着色器根据 片元与视点的距离,计算出 雾化因子。
Fog Code
1 | let VSHADER_SOURCE = ` |
1 | function main() { |
1 | function keydown(ev, gl, n, u_FogDist, fogDist) { |
1 | function initVertexBuffers(gl) { |
程序解析
如何使用distance()计算顶点与视点间的距离?
1 | // 顶点与视点间距 = distance( 顶点坐标, 视点坐标) |
- u_ModelMatrix: 将顶点转换到 世界坐标系 下的模型矩阵
- u_ModelMatrix*a_Position: 世界坐标系下的 顶点坐标
- u_Eye: 世界坐标系下的 视点坐标
如何使用clamp()计算雾化因子大小?
1 | float fogFactor = clamp( |
- u_FogDist 雾的范围
- u_FogDist.x 雾的起点
- u_FogDist.y 雾的终点
- u_FogDist.y - u_FogDist.x 起点与终点之间的距离
- v_Dist 顶点与视点间的距离
clamp函数
- 功能 对计算结果进行区间限制
- 参数
- 参数1 计算出的雾化因子
- 参数2 区间的最小值
- 参数3 区间的最大值
如何使用mix()计算片元颜色?
1 | vec3 color = mix(u_FogColor, vec3(v_Color), fogFactor); |
在这里我们可以这样设置:
- x = u_FogColor 表示雾的颜色
- y = vec3(v_Color) 表示物体表面颜色
- z = fogFactor 表示雾化因子
1 | mix(x,y,z) = x*(1-z) + y*z; |
- x与z成 反比
- y与z成 正比
除了线性雾化,还有其他哪些雾化算法?
答: 还有指数雾化算法等等。
想要使用其他的雾化算法,只需 修改雾化指数的计算方法 。
使用w分量
近似估算法
Fog.js中shader程序的缺陷
即在 顶点着色器 中计算 顶点与视点的距离 ,
会造成较大的开销,影响性能。
近似估算法需要为视点和视线设置 特殊条件:
- 视点 在 原点
- 视线 沿 Z轴负方向
由此可以推出几个结论:
- 在观察者坐标系下,物体及视图的 z分量都是负值
- w分量可以直接看做 顶点与视点的距离
- w分量是vec4类型的顶点坐标的最后一个分量
- w分量的值刚好是 z分量值乘以-1
比较两种方法的vertex shader程序
通过distance计算距离
1 | void main(){ |
通过w分量估算距离
1 | void main(){ |
绘制圆形的点
如何绘制圆点?
答: 像做饼干一样,需要将原先的方点 削减 成圆形。
在做饼干的时候,饼干模具会帮助我们切割甜饼。
但是在计算机中没有模具,要借助什么工具判断切割边界呢?
答案就是坐标系。
gl_PointCoord
- 步骤1 检查当前片元是否距离点中心 (0.5,0.5)距离超过0.5
- 步骤2 如果是,使用 discard 语句放弃此片元
RoundedPoint Code
1 | let VSHADER_SOURCE = ` |
1 | function main() { |
1 | function initVertexBuffers(gl) { |
绘制逻辑简述
实际上所有的绘制逻辑在 片元着色器 中实现:
- 计算片元距离所属点中心的距离
1
float d = distance(gl_PointCoord,vec2(0.5,0.5));
- 如果距离小于0.5就绘制,否则舍弃
1
2if(d<0.5) gl_FragColor = vec4(0.0,1.0,0.0,1.0);
else discard;