学习OpenGL ES之教你制作迷雾

原理

雾效果的制作并不需要额外的几何体或者是粒子发射器,当然如果可以配合一些粒子效果会更好。雾的实现主要依赖于修改已有模型的颜色,根据渲染的顶点与观察者的距离,将雾的颜色与当前颜色混合。下面是2次exp计算模型的效果图,可以先感受一下。地形和天空都是上一篇文章使用的。

我们先来看雾的线性计算模型,在这个模型下,我们要指定雾的起点,终点。也就是说雾在距离你多少的地方开始,距离多少的地方结束。接着根据当前渲染的fragment在该区域内的位置计算雾的占比因子,距离越远,雾应该越浓。所以随着和观察者距离的增加,占比因子应该从0到1。下面是公式和曲线图。

上面的公式开始的距离值是2,结束的距离值是5,在0到2的区域,是没有雾的,因此雾的占比因子是都是0。超过5的区域,雾占比100%。设开始位置为fogStart,结束位置为fogEnd,当前位置距离观察者distance。可以得到雾的占比因子fogFactor线性计算公式。clamp函数将fogFactor约束在0到1的范围中。

1
2
fogFactor =  1  - (fogEnd - distance) / (fogEnd - fogStart)
fogFactor = clamp(fogFactor,  0 1 )

接下来我们可以就很轻松的在Shader中实现了。下面的方法利用上面的公式计算了雾的占比因子。

1
2
3
4
5
6
7
8
float linearFogFactor(float fogStart, float fogEnd) {
     vec4 worldVertexPosition = modelMatrix * vec4(fragPosition,  1.0 );
     float distanceToEye = distance(eyePosition, worldVertexPosition.xyz);
     // linear
     float fogFactor = (fogEnd - distanceToEye) / (fogEnd - fogStart);  // 1.0 ~ 0.0
     fogFactor =  1.0  - clamp(fogFactor,  0.0 1.0 );   // 0.0 ~ 1.0
     return  fogFactor;
}

计算完占比因子后,使用mix方法将原来的fragment颜色和雾的颜色按照因子混合。最后将混合后的颜色返回给gl_FragColor就大功告成了。

1
mix(inputColor, fog.fogColor, fogFactor);

exp计算模型

exp计算模型是利用常量e来参与公式计算的一个模型。公式和曲线如下。

公式中的0.5可以表示为雾的浓度,这个值越小,曲线上升的越慢,表现出来的现象就是雾变浓的速度越来越慢。设雾的浓度为fogDensity,当前位置距离观察者distance,得到的公式如下。

1
2
fogFactor =  1  1  / e^(distance * fogDensity)
fogFactor = clamp(fogFactor,  0 1 )

在Shader中实现如下。计算出来的和上面一样,依然是雾的占比因子。

1
2
3
4
5
6
7
float exponentialFogFactor(float fogDensity) {
     vec4 worldVertexPosition = modelMatrix * vec4(fragPosition,  1.0 );
     float distanceToEye = distance(eyePosition, worldVertexPosition.xyz);
     float fogFactor =  1.0  / exp(distanceToEye * fogDensity);
     fogFactor =  1.0  - clamp(fogFactor,  0.0 1.0 );   // 0.0 ~ 1.0
     return  fogFactor;
}

2次exp计算模型

相比于exp计算模型,这个模型只是将公式增加了一个2次方。

1
2
fogFactor =  1  1  / e^((distance * fogDensity)^ 2 )
fogFactor = clamp(fogFactor,  0 1 )

公式曲线如下。

Shader中实现如下。

1
2
3
4
5
6
7
float exponentialSquareFogFactor(float fogDensity) {
     vec4 worldVertexPosition = modelMatrix * vec4(fragPosition,  1.0 );
     float distanceToEye = distance(eyePosition, worldVertexPosition.xyz);
     float fogFactor =  1.0  / exp(pow(distanceToEye * fogDensity,  2.0 ));
     fogFactor =  1.0  - clamp(fogFactor,  0.0 1.0 );   // 0.0 ~ 1.0
     return  fogFactor;
}

最终的Shader

Shader中包含3种雾的计算方式,通过fogType决定使用哪种。fogType和其他雾的相关参数被配置在Fog结构体中。

1
2
3
4
5
6
7
struct Fog {
     int  fogType;  // 0: 线性,1: exp 2: 2次exp
     float fogStart;
     float fogEnd;
     float fogIndensity;
     vec3 fogColor;
};

在colorWithFog统一处理雾的计算。

1
2
3
4
5
6
7
8
9
10
11
vec3 colorWithFog(vec3 inputColor) {
     float fogFactor =  0.0 ;
     if  (fog.fogType ==  0 ) {
         fogFactor = linearFogFactor(fog.fogStart, fog.fogEnd);
     else  if  (fog.fogType ==  1 ) {
         fogFactor = exponentialFogFactor(fog.fogIndensity);
     else  if  (fog.fogType ==  2 ) {
         fogFactor = exponentialSquareFogFactor(fog.fogIndensity);
     }
     return  mix(inputColor, fog.fogColor, fogFactor);
}

传值给Fog

在OC代码中,定义了新的结构体Fog描述雾的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef enum : NSUInteger {
     FogTypeLinear =  0 ,
     FogTypeExp =  1 ,
     FogTypeExpSquare  =  2 ,
} FogType;
 
typedef struct {
     FogType fogType;
     // for linear
     GLfloat fogStart;
     GLfloat fogEnd;
     // for exp & exp square
     GLfloat fogIndensity;
     GLKVector3 fogColor;
} Fog;

接着初始化Fog对象。

1
2
3
4
5
6
7
Fog fog;
fog.fogColor = GLKVector3Make( 1 1 , 1 );
fog.fogStart =  0 ;
fog.fogEnd =  200 ;
fog.fogIndensity =  0.02 ;
fog.fogType = FogTypeExpSquare;
self.fog = fog;

最后传递给Shader,注意,所有被渲染物体使用的Shader都必须实现Fog。本文中只在frag_terrain.glsl和frag_skybox.glsl中实现了,因为只渲染了天空盒和地形。

1
2
3
4
5
6
7
- ( void )bindFog:(GLContext *)context {
     [context setUniform1i:@ "fog.fogType"  value:self.fog.fogType];
     [context setUniform1f:@ "fog.fogStart"  value:self.fog.fogStart];
     [context setUniform1f:@ "fog.fogEnd"  value:self.fog.fogEnd];
     [context setUniform1f:@ "fog.fogIndensity"  value:self.fog.fogIndensity];
     [context setUniform3fv:@ "fog.fogColor"  value:self.fog.fogColor];
}

总结

雾除了这三种基础的计算模型外,还有其他改进型的方案。比如体积雾,Atmospheric Fog等等,但是基本原理都是一样的,只不过使用了更加仿真的公式。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值