首页 > 代码库 > 使用着色器模拟雾效果

使用着色器模拟雾效果

上一篇关于天空盒的blog谈到了雾效果,那么这次来讨论一下用着色器实现雾效果的具体实现方法.

雾在大自然中是一种常见的天气现象,比如清晨时分在山上就能看到这种效果.我们可以使用OpenGL轻松地模拟出来,使用固定管线设置GL_FOG_COLOR,GL_FOG_DENSITY,GL_FOG_START,GL_FOG_END,GL_FOG_MODE等GL_FOG系列参数,然后调用glEnable(GL_FOG)最后渲染场景即能看到这种效果了.事实上这种固定管线的实现只是向API传递一些参数而已,而且在OpenGL2.x版本以后就不推荐使用固定管线,甚至在3.x版本之后逐渐废弃了.使用固定管线看似很简单,事实上我们并不清楚它的工作原理,更要命的是在一些新的显卡上使用固定管线会出现一些奇怪的问题,甚至在OpenGL ES 2.0任何固定管线的东西都不能再用了.那么怎么办捏? 嗯...用着色器实现!

首先让我们来看下雾效果的公式:

GL_LINEAR: fogFactor=(end-z)/(end-start)

GL_EXP:        fogFactor=e^(-(density*z))

GL_EXP2:      fogFactor=e^(-(density*z)^2)

雾效果有三种实现公式,EXP2优于EXP,EXP优于LINEAR.这里density相当于GL_FOG_DENSITY即雾的浓度,可以根据个人口味调整,z是摄像机到顶点?片段?(根据雾效果的细致程度而定)的距离(也可以用两者z值之差近似,效果其实差不多,后者没有距离运算效率更高些),e就是数学常量与对数运算密切相关如同PI之于圆.

既然知道了公式,那么是时候上干货了:


Vertex Shader:

uniform float fogDensity; 
varying float fogFactor;

float gainFogFactor(vec4 vecPos) {
	float LOG2E = 1.442695;
	float fogDist = abs(vecPos.z);
	float result = exp2( -fogDensity * fogDensity *
		fogDist * fogDist * LOG2E );
	result = clamp(result, 0.0, 1.0);
	return result;
}

void main() {
        ...
	gl_Position = ftransform();
	fogFactor = gainFogFactor(gl_Position);
}

看明白了么? 嗯,这段代码是EXP2公式的实现,解释一下,exp2(n)用于计算2的n次方,我们把它重新组合一下:2^(-density^2 * z^2 * log2e)=(2^(log2e))^(-density^2 * z^2),又

2^log2e=e那么最终的结果是e^((-density^2 * z^2))=e^(-(density*z)^2)也就是上面的EXP2算法,这里fogDist就是摄像机到顶点在z轴方向上的距离即z.

这边怎么能把经过投影变换的顶点坐标给当作参数传递给gainFogFactor函数捏?其实在雾的计算当中我们只要用到z坐标就行了,投影变换并不会改变z坐标的大小,换句话说,这边的gl_Position.z依然是摄像机到顶点在z轴方向上的距离之差.

接着是Fragment Shader:

uniform samplerCube texCube;
uniform vec4 fogColor;
uniform int isFog;

varying vec3 texCoord;
varying float fogFactor;

vec4 gainFinalColor(vec4 srcColor) {
	vec4 result = mix(fogColor, srcColor, fogFactor);
	return result;
}

void main() {
	vec4 texColor = textureCube(texCube, texCoord);
    if(isFog == 1)
    	gl_FragColor = gainFinalColor(texColor);
	else 
		gl_FragColor = texColor;
}

根据雾化因子把雾的颜色与片段颜色混合一下即得到最终的结果.

不开雾:



开启雾:



哈,效果出来了! 嗯,移动摄像机看看.

这就是雾模拟的一种实现.看懂了么? 什么?数学还给老师了? 赶紧敲一遍代码体会一下!

使用着色器模拟雾效果