Search code examples
three.jswebglshader

Is it possible to let Fog interact with the material's opacity?


I am working on a project that displays buildings. The requirement is to let the building gradually fade out (transparent) based on the distance between the camera and the buildings. Also, this effect has to follow the camera's movement.

I consider using THREE.Fog(), but the Fog seems can only change the material's color. Buildings with White Fog enabled Above is a picture of the building with white fog.

  • The buildings are in tiles, each tile is one single geometry (I merged all the buildings into one) using var bigGeometry = new THREE.Geometry(); bigGeometry.merge(smallGeometry);
  • The purple/blue color thing is the ground, and ground.material.fog = false;. So the ground won't interact with the fog.

My question is:

  • Is it possible to let the fog interact with the building's material's opacity instead of color? (more white translate to more transparent)
  • Or should I use Shader to control the material's opacity based on distance to the camera? But I have no idea of how to do this.
  • I also considered adding alphaMap. If so, each building tile have to map an alphaMap and all these alphaMap have to interact with the camera's movement. It's going to be a tons of work.

So any suggestions?

Best Regards, Arthur


Solution

  • I am the OP. After spending some time reading how to use Three.js's Shader material. I got some code that is working as desired.

    Here's the code: https://jsfiddle.net/yingcai/4dxnysvq/

    The basic idea is:

    1. Create an Uniform that contains controls.target (Vector3 position).
    2. Pass vertex position attributes to varying in the Vertex Shader. So that the Fragment Shader can access it.
    3. Get the distance between each vertex position and controls.target. Calculate alpha value based on the distance.
    4. Assign alpha value to the vertex color.

    Another important thing is: Because the fade out mask should follow the camera move, so don't forget to update the control in the uniforms every frame.

    // Create uniforms that contains control position value.
    uniforms = {
        texture: {
          value: new THREE.TextureLoader().load("https://threejs.org/examples/textures/water.jpg")
        },
        control: {
          value: controls.target
        }
      };
    
    // In the render() method.
    // Update the uniforms value every frame.
    uniforms.control.value = controls.target;
    

    enter image description here