Open In App

How to Implement Fog Effects in WebGL?

Last Updated : 03 Sep, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

Fog consequences are critical for growing immersive and sensible three-dimensional environments in WebGL. By simulating atmospheric scattering, fog can decorate intensity perception and temper within our scenes on our websites which looks cool and sophisticated. Whether we are developing a video game or a visualization tool, adding fog results can appreciably and unknowingly enhance the visible enchantment and realism of our project.

Below are the following approaches to implement Fog Effects in WebGL:

Linear Fog

This is made up of two things, camera and far-from-camera density dependency, it is the most basic and most straightforward approach to simulate fog by increasing density gradually from a starting point(near the camera) to a maximum distance(far from the camera). This type of fog creates a smooth transition between foggy and clear areas, making an ideal environment or scene where a gradual fade is required.

Example: This illustrates a 3D scene using Three.js, with a rotating cube and dynamic fog settings.

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" 
          content="width=device-width,
                   initial-scale=1.0">
    <title>Linear Fog with Slider in WebGL</title>
    <link rel="stylesheet" href="style.css">
    <style>
        #top-left-image {
            position: absolute;
            top: 10px;
            right: 10px;
            width: 100px;
        }
    </style>
</head>

<body>
    <img id="top-left-image" 
         src="371ba38b-8a03-4bff-916c-c3fa5396ceda.webp" 
         alt="Top Left Image">

    <div id="slider-container">
        <label for="fog-near">Fog Near Distance: 
          <span id="fog-near-value">5</span>
        </label>
        <input type="range" id="fog-near" 
               min="0" max="100" value="5" step="1">

        <label for="fog-far">Fog Far Distance: 
        <span id="fog-far-value">15</span>
        </label>
        <input type="range" id="fog-far"
               min="0" max="100" value="15" step="1">
    </div>

    <script src=
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js">
   </script>
    <script src="script.js"></script>
</body>

</html>
CSS
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    display: block;
}

#slider-container {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(255, 255, 255, 0.8);
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

#slider-container label {
    display: block;
    margin-bottom: 5px;
}

#slider-container input {
    width: 100%;
}
JavaScript
const scene = new THREE.Scene();
const fogColor = 0xaaaaaa;
let fogNear = 5;
let fogFar = 15;
scene.fog = new THREE.Fog(fogColor, fogNear, fogFar);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,
    window.innerHeight);
renderer.setClearColor(fogColor, 1);
document.body.appendChild(renderer
    .domElement);

const camera = new THREE.PerspectiveCamera(75,
    window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth,
        window.innerHeight);
    camera.aspect = window
        .innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
});

function updateFog() {
    scene.fog.near = fogNear;
    scene.fog.far = fogFar;
    document.getElementById('fog-near-value')
        .textContent = fogNear;
    document.getElementById('fog-far-value')
        .textContent = fogFar;
    renderer.setClearColor(fogColor, 1);
}

const nearSlider = document
    .getElementById('fog-near');
const farSlider = document
    .getElementById('fog-far');

nearSlider.addEventListener('input', (event) => {
    fogNear = parseFloat(event
        .target.value);
    updateFog();
});

farSlider.addEventListener('input', (event) => {
    fogFar = parseFloat(event
        .target.value);
    updateFog();
});

updateFog();

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}

animate();

Output:

Linear-Fog
Linear Fog

Exponential Fog

Exponential fog simulates a extra severe fog effect by means of making use of a density that increases exponentially with distance from the camera in our website which is also called a focus shifting. This consequences in a extra deep and edged fog effect as objects passes away, this makes more developed and a more dramatic ecosystem in comparison to linear fog.This is mostly used in production grade website to showcase something which is more valuable and the gradient increases from top to bottom where more fog is at bottom.

Example: This illustrates a 3D scene using Three.js with a rotating cube and adjustable exponential fog density.

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Exponential Fog in WebGL</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <img id="top-right-image"
         src="371ba38b-8a03-4bff-916c-c3fa5396ceda.webp" alt="Top Right Image">

    <div id="slider-container">
        <label for="fog-density">Fog Density: 
          <span id="fog-density-value">0.1</span></label>
        <input type="range" id="fog-density" min="0.01" max="1" value="0.1" step="0.01">
    </div>

    <script src=
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="script.js"></script>
</body>

</html>
CSS
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    display: block;
}
#top-right-image {
            position: absolute;
            top: 10px;
            right: 10px;
        }

#slider-container {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(255, 255, 255, 0.8);
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

#slider-container label {
    display: block;
    margin-bottom: 5px;
}

#slider-container input {
    width: 100%;
}
JavaScript
const scene = new THREE.Scene();
const fogColor = 0xaaaaaa;
let fogDensity = 0.1;
scene.fog = new THREE.FogExp2(fogColor, fogDensity);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(fogColor, 1);
document.body.appendChild(renderer.domElement);

const camera = new THREE
.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;

const geometry = new THREE.BoxGeometry();
const material = new THREE
.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
});

function updateFog() {
    scene.fog.density = fogDensity;
    document.getElementById('fog-density-value')
    .textContent = fogDensity.toFixed(2);
    renderer.setClearColor(fogColor, 1);
}

const densitySlider = document.getElementById('fog-density');

densitySlider.addEventListener('input', (event) => {
    fogDensity = parseFloat(event.target.value);
    updateFog();
});

updateFog();

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}

animate();

Output:

Exponential-Fog
Exponential Fog

Exponential Squared Fog

Exponential fog squared is a squared version of exponential fog that uses a squared density parameter. This method results in an excellent extra reported fog effect, in particular at greater distances, offering a denser and greater immersive fog look which makes much more cloudy type effects with fogs as the camera distance increases.

Example: This illustrates a Three.js 3D scene with a rotating cube and dynamic exponential fog density using exponential squared Fog.

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, 
                   initial-scale=1.0">
    <title>Exponential Squared Fog in WebGL</title>
    <link rel="stylesheet" href="style.css">
    <style>

        #top-right-image {
            position: absolute;
            top: 10px;
            right: 10px;
            width: 100px;
        }
    </style>
</head>

<body>
    <img id="top-right-image" 
         src="371ba38b-8a03-4bff-916c-c3fa5396ceda.webp" 
         alt="Top Right Image">

    <div id="slider-container">
        <label for="fog-density">Fog Density:
        <span id="fog-density-value">0.1</span></label>
        <input type="range" id="fog-density"
               min="0.01" max="1" value="0.1" step="0.01">
    </div>

    <script src=
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="script.js"></script>
</body>

</html>
CSS
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    display: block;
}

#slider-container {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(255, 255, 255, 0.8);
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

#slider-container label {
    display: block;
    margin-bottom: 5px;
}

#slider-container input {
    width: 100%;
}
JavaScript
const scene = new THREE.Scene();
const fogColor = 0xaaaaaa;
let fogDensity = 0.1;
scene.fog = new THREE.FogExp2(fogColor, fogDensity);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(fogColor, 1);
document.body.appendChild(renderer.domElement);

const camera = new THREE.PerspectiveCamera(75, 
window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;

const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
});

function updateFog() {
    scene.fog.density = fogDensity;
    document.getElementById('fog-density-value').textContent = fogDensity.toFixed(2);
    renderer.setClearColor(fogColor, 1);
}

const densitySlider = document.getElementById('fog-density');

densitySlider.addEventListener('input', (event) => {
    fogDensity = parseFloat(event.target.value);
    updateFog();
});

updateFog();

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}

animate();

Output:

Exponential-Squared-fog
Exponential Squared fog

Custom Fog Effect

Custom fog effects provide superior control by allowing us to create personalized fog shaders by our own. This method provides full flexibility to define how fog is rendered, together with the density feature and colour mixing, resulting in distinctly customizable and precise fog results.Everything in this is decided by us taking from color shading to fog manipulations.

Example: This illustrates a 3D scene using Three.js with a rotating cube and a custom fog effect, implemented via shaders, with dynamic fog density using Custom Fog Effect.

HTML
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          
          content="width=device-width, initial-scale=1.0">
    <title>Custom Fog in WebGL</title>
    <link rel="stylesheet" href="style.css">
    <style>
        #top-right-image {
            position: absolute;
            top: 10px;
            right: 10px;
            width: 100px;
        }
    </style>
</head>

<body>
    <img id="top-right-image" 
         src="
371ba38b-8a03-4bff-916c-c3fa5396ceda.webp" 
         alt="Top Right Image">

    <div id="slider-container">
        <label for="custom-fog-param">Custom Fog Parameter:
         <span id="custom-fog-param-value">0.5</span></label>
        <input type="range" id="custom-fog-param" 
               min="0" max="1" value="0.5" step="0.01">
    </div>

    <script src=
"https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script src="script.js"></script>
</body>

</html>
CSS
body {
    margin: 0;
    overflow: hidden;
}

canvas {
    display: block;
}

#slider-container {
    position: absolute;
    top: 10px;
    left: 10px;
    background: rgba(255, 255, 255, 0.8);
    padding: 10px;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
}

#slider-container label {
    display: block;
    margin-bottom: 5px;
}

#slider-container input {
    width: 100%;
}
JavaScript
const scene = new THREE.Scene();
const fogColor = 0xaaaaaa;
let customFogParam = 0.5;

// Create a custom fog effect using a shader material
const fogShader = {
    vertexShader: `
        varying vec3 vPosition;
        void main() {
            vPosition = position;
            gl_Position = projectionMatrix 
            * modelViewMatrix * vec4(position, 1.0);
        }
    `,
    fragmentShader: `
        uniform vec3 fogColor;
        uniform float fogDensity;
        varying vec3 vPosition;

        void main() {
            float distance = length(vPosition);
            float fogFactor = exp(-fogDensity 
            * distance * distance);
            fogFactor = clamp(fogFactor, 0.0, 1.0);
            gl_FragColor = vec4(mix(fogColor,
            vec3(1.0), fogFactor), 1.0);
        }
    `
};

const fogMaterial = new THREE.ShaderMaterial({
    uniforms: {
        fogColor: { value: new THREE.Color(fogColor) },
        fogDensity: { value: customFogParam }
    },
    vertexShader: fogShader.vertexShader,
    fragmentShader: fogShader.fragmentShader
});

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(fogColor, 1);
document.body.appendChild(renderer.domElement);

const camera = new THREE.PerspectiveCamera(75,
window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 10;

const geometry = new THREE.BoxGeometry();
const cube = new THREE.Mesh(geometry, fogMaterial);
scene.add(cube);

window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
});

function updateFog() {
    fogMaterial.uniforms.fogDensity.value = customFogParam;
    document.getElementById('custom-fog-param-value')
    .textContent = customFogParam.toFixed(2);
    renderer.setClearColor(fogColor, 1);
}

const fogParamSlider = document.getElementById('custom-fog-param');

fogParamSlider.addEventListener('input', (event) => {
    customFogParam = parseFloat(event.target.value);
    updateFog();
});

updateFog();

function animate() {
    requestAnimationFrame(animate);
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    renderer.render(scene, camera);
}

animate();

Output:

Custom-Fog
Custom Fog

Conclusion

Implementing fog effects in WebGL can significantly beautify the depth and realism of our three-dimensional scenes. Understanding and applying exclusive kinds of fog including the Linear Fog, Exponential Fog, Exponential Fog Squared, and Custom Fog which allows us to gain numerous atmospheric outcomes. Experiment with those strategies to locate the satisfactory suits on our project and create more immersive and visually attractive environments which makes the things more production grade.


Explore