Fog effects

This section shows you how fog effects are used in the Ice Cave demo. A fog effect adds atmosphere to a scene. There are advanced fog implementations, but these are too expensive for mobile and a simple fog effect can be effective.

Fog is common in the real world, so adding this effect to your game adds to the sense of realism. In the real world, whether or not it is foggy, the colors fade the further you look in the distance. You can see this effect on sunny days, especially when looking at mountains.

This section describes two non-expensive versions of the fog effect:

  • Procedural linear fog
  • Particle-based fog

You can apply both effects simultaneously. The Ice Cave demo uses both techniques.

Procedural linear fog

Procedural linear fog ensures that the further away the object is, the more its colors fade to the defined fog color. To achieve this effect in your fragment shader, you can use simple linear interpolation between the fragment color and the fog color.

The following code shows you how to calculate the fog color in the vertex shader, based on the distance to the camera. This color is passed to fragment shader as a varying:

output.fogColor = _FogColor * clamp(vertexDistance * _FogDistanceScale, 0.0, 1.0);

The following list describes what is going on in the code:

  • vertexDistance is the vertex to camera distance.
  • _FogDistanceScale is a factor that is passed to the shader as a uniform.
  • _FogColor is the base fog color that you define and is passed as a uniform.

In the fragment shader, the interpolated input.fogColor is combined with the fragment color output.Color. This is shown in the following code:

outputColor = lerp(outputColor, input.fogColor.rgb, input.fogColor.a);

The following image shows the result after applying the preceding code to your scene:

Note: Calculating the fog in a shader means that you are not required to do any extra post processing to produce the fog effect.

Try to merge as many effects as possible into one shader. However, check performance because if the cache is exceeded this means that the performance might reduce. Therefore, the shader may need to be split into two or more passes.

You can perform the fog color calculation in the vertex or fragment shader. The calculation in the fragment shader is more accurate but requires more compute performance. This is because it is calculated for every fragment.

The vertex shader calculation is lower precision but higher performance because it is only computed once per vertex.

Linear fog with height

Fog is applied uniformly across your scene. You can make fog more realistic by changing the density according to height. For example, make the fog denser lower down and thinner higher up. You can expose the height level, to adjust it manually.

The following image shows linear fog based on distance and height:

Non-uniform fog

The fog does not have to be uniform. You can make fog more visually interesting, for example non-uniform, by introducing some noise, for example, applying a noise texture.

For a more complex effect, you can also apply more than one noise texture and make those textures slide with different speeds. For instance, the noise texture that is further away slides more slowly than the texture that is closer to the camera.

You can apply more than one texture in a single pass in one shader, by blending the noise textures according to the distance.

Pre-baked fog

You can pre-bake fog into textures if the camera does not get close to them. This reduces the compute power required.

Volumetric fog that uses particles

You can simulate volumetric fog with particles. This technique can provide high-quality results.

Keep the number of particles to a minimum. Particles increase overdraw because more shaders are executed per fragment. If overdraw occurs, try using larger particles instead of creating more particles.

In the Ice Cave demo, a maximum of 15 particles are used at a time.

Let’s look at different types of fog effects that use particles.

Geometry instead of billboard rendering

Set the Render Mode of your particle system to Mesh instead of Billboard. To achieve a volumetric effect, the individual particles must have random rotations.

The following image shows the particle geometry without textures:

The following image shows the particle geometry with textures:

The following image shows the texture that is applied to each particle:

Angle fade effect

Fade the individual particles in and out according to their orientation compared to the camera position. If individual particles do not fade in and out, the sharp edges of the particles are visible.

The following example code shows a vertex shader that performs the fading:

half4 vertexInWorld = mul(_Object2World, input.vertex);
half3 normalInWorld = (mul(half4(input.normal, 0.0), _World2Object).xyz);
const half3 viewDirInWorld = normalize(vertexInWorld - _WorldSpaceCameraPos);
output.visibility = abs(dot(-normalInWorld, viewDirInWorld));
output.visibility *= output.visibility; // instead of power of 2

The varying parameter output.visibility is interpolated across the particle polygon. Read this value in a fragment shader and apply an amount of transparency. This is shown in the following code:

half4 diffuseTex = _Color * tex2D(_MainTex, half2(input.texCoord));
diffuseTex *= input.visibility;
return diffuseTex;

Rendering particles

Use the following steps to render for particles:

  1. Render particles as the last primitives within the frame, by adding the following line:
  2. Tags { "Queue" = "Transparent+10" }
    Note: The +10 is present in this example because the Ice Cave demo renders nine other transparent objects before the particles.
  3. Set the appropriate blending mode, by adding the following line at the beginning of a shader pass:
    Blend SrcAlpha One
  4. Disable writing to z-buffer, by adding the following line:
  5.  ZWrite Off

Particle system settings

The following screenshot shows the settings that are used in the Ice Cave demo for the particle system:

The following screenshot shows the area where the particles are spawned, which is indicated by the box in the image. The box is defined by the built-in option Shape in the Unity Particle System:

Previous Next