# 1.雾化

用来描述远处的物体看上去较为模糊的现象。

# 2.线性雾化

在线性雾化中,某一点的雾化程度取决于它与视点之间的距离,距离越远雾化程度越高。线性雾化可以使用下面的公式来进行计算。某一点的雾化程度可以定义成雾化因子。并且可以在线性雾化公式中被计算出来。起点表示开始雾化之处,终点表示完全雾化之处。比终点更远的点是完全雾化了的,即完全看不见了。

雾化因子 = (终点 - 当前点与视点间的距离)/(终点-起点)
并且  起点<=当前点与视点间的距离<=终点
1
2

那么物体的颜色就可以用下面的公式来进行计算。

片元颜色 = 物体表面颜色 * 雾化因子 + 雾的颜色*(1 - 雾化因子)
1

主要步骤如下:

  • 在顶点着色器中计算当前顶点与视点之间的距离,并传入片元着色器
  • 在片元着色器中根据片元与视点之间的距离,计算雾化因子,最终得出片元的颜色。

注意: 着色器中传入的是视点在世界坐标系下的坐标,所以雾化因子是在世界坐标系下计算的。 顶点着色器中的代码如下所示

attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_MvpMatrix; //投影视图模型矩阵
uniform mat4 u_ModelMatrix;// 模型矩阵
uniform vec4 u_Eye;    // 视点在世界坐标系下的坐标
varying vec4 v_Color;
varying float v_Dist; // 顶点到视点之间的距离
void main() {
  gl_Position = u_MvpMatrix * a_Position;
  v_Color = a_Color;
     // 计算顶点到视点之间的距离
  v_Dist = distance(u_ModelMatrix * a_Position, u_Eye);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

片元着色器中的代码如下所示

#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 u_FogColor; // 雾的颜色
uniform vec2 u_FogDist;  // Distance of Fog (starting point, end point)
varying vec4 v_Color;
varying float v_Dist;
void main() {
     // Calculation of fog factor (factor becomes smaller as it goes further away from eye point)
    // 根据雾化因子的计算公式计算雾化因子 值在0和1之间 在起点是1,在终点是0,
    float fogFactor = clamp((u_FogDist.y - v_Dist) / (u_FogDist.y - u_FogDist.x), 0.0, 1.0);
     // Stronger fog as it gets further: u_FogColor * (1 - fogFactor) + v_Color * fogFactor
     // 根据公式得到片元颜色
     //  u_FogColor * (1 - fogFactor) + v_Color * fogFactor 跟下面的方法等价
    vec3 color = mix(u_FogColor, vec3(v_Color), fogFactor);
    gl_FragColor = vec4(color, v_Color.a);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

js中的代码如下所示

// 雾的颜色
  const fogColor = new Float32Array([0.137, 0.231, 0.423]);
  // 雾的起点和终点与视点之间的距离
  const fogDist = new Float32Array([55, 80]);
  //  视点的位置,在世界坐标系下的位置
  const eye = new Float32Array([25, 65, 35, 1.0]);

  // Get the storage locations of uniform variables
  const u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix');
  const u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  const u_Eye = gl.getUniformLocation(gl.program, 'u_Eye');
  const u_FogColor = gl.getUniformLocation(gl.program, 'u_FogColor');
  const u_FogDist = gl.getUniformLocation(gl.program, 'u_FogDist');
  if (!u_MvpMatrix || !u_ModelMatrix || !u_Eye || !u_FogColor || !u_FogDist) {
    console.log('Failed to get the storage location');
    return;
  }
	
  // Pass fog color, distances, and eye point to uniform variable
  gl.uniform3fv(u_FogColor, fogColor); // Colors
  gl.uniform2fv(u_FogDist, fogDist);   // Starting point and end point
  gl.uniform4fv(u_Eye, eye);           // Eye point

  // 设置背景色,并开启深度测试
  gl.clearColor(fogColor[0], fogColor[1], fogColor[2], 1.0); // Color of Fog
  gl.enable(gl.DEPTH_TEST);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

下面是雾化效果的示意图

demo地址 (opens new window)

# 3.使用w分量的线性雾

在顶点着色器中计算顶点与视点之间的距离,会造成较大的开销,可能还会影响性能。我们可以使用顶点经过模型视图投影变换后的的坐标(gl_Position.w)w分量来近似估算出这个距离。

实际上,这个w分量的值就是顶点的视图坐标z分量乘以-1。在视图坐标系中,视点在原点,视线沿着Z轴负方向,观察者看到的其他物体其视图坐标系值z分量都是负的。而gl_Position.w)w分量值正好是z分量值乘以-1,所以可以直接使用该值来近视与视点的距离。 在顶点着色器中的代码

attribute vec4 a_Position;
attribute vec4 a_Color;
uniform mat4 u_MvpMatrix; //投影视图模型矩阵
uniform mat4 u_ModelMatrix;// 模型矩阵
uniform vec4 u_Eye;    // 视点在世界坐标系下的坐标
varying vec4 v_Color;
varying float v_Dist; // 顶点到视点之间的距离
void main() {
  gl_Position = u_MvpMatrix * a_Position;
  v_Color = a_Color;
     // 使用视图坐标系下的负z值
  v_Dist = gl_Position.w;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

在片元着色器中的坐标

#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 u_FogColor;// Color of Fog
uniform vec2 u_FogDist; // Distance of Fog (starting point, end point)
varying vec4 v_Color;
varying float v_Dist;
void main() {
    // Calculation of fog factor (factor becomes smaller as it goes further away from eye point)
    float fogFactor = (u_FogDist.y - v_Dist) / (u_FogDist.y - u_FogDist.x);
        // Stronger fog as it gets further: u_FogColor * (1 - fogFactor) + v_Color * fogFactor
    vec3 color = mix(u_FogColor, vec3(v_Color), clamp(fogFactor, 0.0, 1.0));
    gl_FragColor = vec4(color, v_Color.a);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注意: 使用深度很简单但是有一个问题。假设围绕相机有一圈物体。我们根据到相机 z平面的距离计算雾量。这意味着你转动相机,当它在视图空间中的 z 值趋近于 0 ,物体会越来越不受雾的影响。

demo地址 (opens new window)

# 4.指数雾

它根据距观察者距离的平方变厚。一个常见的 指数雾公式 是:

fogAmount = 1. - exp2(-fogDensity * fogDensity * fogDistance * fogDistance * LOG2));
fogAmount = clamp(fogAmount, 0., 1.);
1
2

注意:基于密度的雾没有最近值和最远值设置。 点元着色器代码

attribute vec4 a_position;
attribute vec2 a_texcoord;

uniform mat4 u_worldView;
uniform mat4 u_projection;

varying vec2 v_texcoord;
varying vec3 v_position;

void main() {
  // Multiply the position by the matrix.
  gl_Position = u_projection * u_worldView * a_position;

  // Pass the texcoord to the fragment shader.
  v_texcoord = a_texcoord;

  // Pass the view position to the fragment shader
  v_position = (u_worldView * a_position).xyz;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

片元着色器代码

precision mediump float;

// Passed in from the vertex shader.
varying vec2 v_texcoord;
varying vec3 v_position;

// The texture.
uniform sampler2D u_texture;
uniform vec4 u_fogColor;
uniform float u_fogDensity;

void main() {
  vec4 color = texture2D(u_texture, v_texcoord);

  #define LOG2 1.442695

  float fogDistance = length(v_position);
  float fogAmount = 1. - exp2(-u_fogDensity * u_fogDensity * fogDistance * fogDistance * LOG2);
  fogAmount = clamp(fogAmount, 0., 1.);

  gl_FragColor = mix(color, u_fogColor, fogAmount);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

demo地址 (opens new window)

参考文档

WebGL 雾 (opens new window)

评 论:

Last Updated: 6/24/2024, 6:04:56 PM