乐闻世界logo
搜索文章和话题

How is fog effect implemented in WebGL?

3月7日 12:04

WebGL Fog Overview

Fog is a rendering technique that simulates atmospheric scattering effects, making distant objects gradually blend into the background color. Fog not only increases scene realism but also hides distant clipping boundaries and optimizes performance.

Basic Fog Principle

The core idea of fog is to perform linear or exponential interpolation between object color and fog color based on the distance between the object and the camera:

shell
Final Color = Object Color × (1 - Fog Factor) + Fog Color × Fog Factor

Types of Fog

1. Linear Fog

Fog density increases linearly with distance.

Formula:

shell
fogFactor = (end - distance) / (end - start)
  • start: Fog start distance
  • end: Fog complete coverage distance
  • distance: Distance from fragment to camera
glsl
// Vertex shader varying float v_fogDepth; void main() { vec4 worldPos = u_modelMatrix * vec4(a_position, 1.0); vec4 viewPos = u_viewMatrix * worldPos; // Calculate view space depth (positive value) v_fogDepth = -viewPos.z; gl_Position = u_projectionMatrix * viewPos; } // Fragment shader uniform vec3 u_fogColor; uniform float u_fogStart; uniform float u_fogEnd; varying float v_fogDepth; void main() { vec4 objectColor = texture2D(u_texture, v_texCoord); // Calculate linear fog factor float fogFactor = (u_fogEnd - v_fogDepth) / (u_fogEnd - u_fogStart); fogFactor = clamp(fogFactor, 0.0, 1.0); // Blend object color with fog color vec3 finalColor = mix(u_fogColor, objectColor.rgb, fogFactor); gl_FragColor = vec4(finalColor, objectColor.a); }

2. Exponential Fog

Fog density increases exponentially with distance, more natural effect.

Formula:

shell
fogFactor = exp(-density × distance)
glsl
uniform vec3 u_fogColor; uniform float u_fogDensity; varying float v_fogDepth; void main() { vec4 objectColor = texture2D(u_texture, v_texCoord); // Calculate exponential fog factor float fogFactor = exp(-u_fogDensity * v_fogDepth); fogFactor = clamp(fogFactor, 0.0, 1.0); vec3 finalColor = mix(u_fogColor, objectColor.rgb, fogFactor); gl_FragColor = vec4(finalColor, objectColor.a); }

3. Exponential Squared Fog (Exp2)

Fog density increases with the square of distance, softer effect.

Formula:

shell
fogFactor = exp(-(density × distance)²)
glsl
void main() { vec4 objectColor = texture2D(u_texture, v_texCoord); // Calculate exponential squared fog factor float fogFactor = exp(-pow(u_fogDensity * v_fogDepth, 2.0)); fogFactor = clamp(fogFactor, 0.0, 1.0); vec3 finalColor = mix(u_fogColor, objectColor.rgb, fogFactor); gl_FragColor = vec4(finalColor, objectColor.a); }

Complete Fog Implementation

Vertex Shader

glsl
attribute vec3 a_position; attribute vec2 a_texCoord; uniform mat4 u_modelMatrix; uniform mat4 u_viewMatrix; uniform mat4 u_projectionMatrix; varying vec2 v_texCoord; varying float v_fogDepth; void main() { vec4 worldPos = u_modelMatrix * vec4(a_position, 1.0); vec4 viewPos = u_viewMatrix * worldPos; v_texCoord = a_texCoord; v_fogDepth = length(viewPos.xyz); // Use actual distance rather than just Z value gl_Position = u_projectionMatrix * viewPos; }

Fragment Shader

glsl
precision mediump float; uniform sampler2D u_texture; uniform vec3 u_fogColor; uniform float u_fogDensity; uniform int u_fogType; // 0: Linear, 1: Exponential, 2: Exponential Squared uniform float u_fogStart; uniform float u_fogEnd; varying vec2 v_texCoord; varying float v_fogDepth; float calculateFogFactor() { float fogFactor = 0.0; if (u_fogType == 0) { // Linear fog fogFactor = (u_fogEnd - v_fogDepth) / (u_fogEnd - u_fogStart); } else if (u_fogType == 1) { // Exponential fog fogFactor = exp(-u_fogDensity * v_fogDepth); } else if (u_fogType == 2) { // Exponential squared fog fogFactor = exp(-pow(u_fogDensity * v_fogDepth, 2.0)); } return clamp(fogFactor, 0.0, 1.0); } void main() { vec4 objectColor = texture2D(u_texture, v_texCoord); float fogFactor = calculateFogFactor(); // mix(fogColor, objectColor, fogFactor) vec3 finalColor = mix(u_fogColor, objectColor.rgb, fogFactor); gl_FragColor = vec4(finalColor, objectColor.a); }

JavaScript Control

javascript
class Fog { constructor(gl, program) { this.gl = gl; this.program = program; // Get uniform locations this.fogColorLoc = gl.getUniformLocation(program, 'u_fogColor'); this.fogDensityLoc = gl.getUniformLocation(program, 'u_fogDensity'); this.fogTypeLoc = gl.getUniformLocation(program, 'u_fogType'); this.fogStartLoc = gl.getUniformLocation(program, 'u_fogStart'); this.fogEndLoc = gl.getUniformLocation(program, 'u_fogEnd'); // Default settings this.color = [0.7, 0.8, 0.9]; // Light blue fog this.density = 0.02; this.type = 2; // Exponential squared fog this.start = 10.0; this.end = 50.0; } apply() { const gl = this.gl; gl.uniform3fv(this.fogColorLoc, this.color); gl.uniform1f(this.fogDensityLoc, this.density); gl.uniform1i(this.fogTypeLoc, this.type); gl.uniform1f(this.fogStartLoc, this.start); gl.uniform1f(this.fogEndLoc, this.end); } // Set fog type setLinear(start, end) { this.type = 0; this.start = start; this.end = end; } setExponential(density) { this.type = 1; this.density = density; } setExponentialSquared(density) { this.type = 2; this.density = density; } } // Usage example const fog = new Fog(gl, program); fog.setExponentialSquared(0.015); fog.apply();

Height Fog

Simulates fog effects that vary with height, such as fog in valleys.

glsl
// Vertex shader varying vec3 v_worldPos; void main() { vec4 worldPos = u_modelMatrix * vec4(a_position, 1.0); v_worldPos = worldPos.xyz; gl_Position = u_projectionMatrix * u_viewMatrix * worldPos; } // Fragment shader uniform float u_fogHeight; uniform float u_fogHeightFalloff; varying vec3 v_worldPos; float calculateHeightFog() { // Height-based fog density float heightFactor = (u_fogHeight - v_worldPos.y) * u_fogHeightFalloff; heightFactor = clamp(heightFactor, 0.0, 1.0); // Combine with distance fog float distanceFactor = exp(-u_fogDensity * length(v_worldPos - u_cameraPos)); return distanceFactor * (1.0 - heightFactor); }

Fog Combined with Lighting

glsl
void main() { // Calculate lighting vec3 ambient = ...; vec3 diffuse = ...; vec3 specular = ...; vec3 lighting = ambient + diffuse + specular; vec4 texColor = texture2D(u_texture, v_texCoord); vec3 objectColor = lighting * texColor.rgb; // Apply fog float fogFactor = calculateFogFactor(); vec3 finalColor = mix(u_fogColor, objectColor, fogFactor); gl_FragColor = vec4(finalColor, texColor.a); }

Fog Performance Optimization

1. Vertex-level Fog Calculation

When there are fewer vertices, fog factor can be calculated in the vertex shader:

glsl
// Vertex shader varying float v_fogFactor; void main() { // ... calculate position float fogDepth = length(viewPos.xyz); v_fogFactor = exp(-u_fogDensity * fogDepth); v_fogFactor = clamp(v_fogFactor, 0.0, 1.0); } // Fragment shader varying float v_fogFactor; void main() { vec3 finalColor = mix(u_fogColor, objectColor, v_fogFactor); }

2. Using Depth Texture

Apply fog in post-processing stage:

javascript
// 1. Render scene to color texture and depth texture renderSceneToTextures(); // 2. Apply fog in post-processing stage applyFogPostProcess();
glsl
// Post-process fog shader uniform sampler2D u_colorTexture; uniform sampler2D u_depthTexture; void main() { vec3 color = texture2D(u_colorTexture, v_texCoord).rgb; float depth = texture2D(u_depthTexture, v_texCoord).r; // Convert depth to world space distance float linearDepth = linearizeDepth(depth); // Calculate fog factor float fogFactor = exp(-u_fogDensity * linearDepth); vec3 finalColor = mix(u_fogColor, color, fogFactor); gl_FragColor = vec4(finalColor, 1.0); }

Comparison of Different Fog Types

Fog TypeFormulaCharacteristics
Linear(end - d) / (end - start)Simple, obvious fog boundary
Exponentialexp(-density × d)Natural, suitable for most scenes
Exponential Squaredexp(-(density × d)²)Softer, less obvious fog boundary
HeightCombined with Y axisSuitable for valleys, water surfaces, etc.

Practical Application Suggestions

  1. Choose appropriate fog type:

    • Use exponential or exponential squared fog for most scenes
    • Use linear fog when precise fog boundary control is needed
  2. Adjust fog color:

    • Usually consistent with skybox or background color
    • Can change over time to simulate day/night effects
  3. Performance considerations:

    • Mobile devices recommend vertex-level fog
    • Complex scenes can use post-processing fog
  4. Combine with other effects:

    • Fog can be combined with volumetric lighting, atmospheric scattering
    • Pay attention to fog's effect on transparent objects
标签:WebGL