Skip to content

Instantly share code, notes, and snippets.

@mandarinx
Last active July 19, 2023 14:08
Show Gist options
  • Save mandarinx/108e5fa2b6c896f89408f3d85b9728fe to your computer and use it in GitHub Desktop.
Save mandarinx/108e5fa2b6c896f89408f3d85b9728fe to your computer and use it in GitHub Desktop.
altitude fog shader

Copied from https://jakobknudsen.wordpress.com/2013/08/06/altitude-fog/ for archival purpose.

When you submerge an object in water you expect it to become more and more obscured as it sinks further into the depths. As far as I can see, this is usually accomplished in Unity3D with a fullscreen image-effect like the global fog standard asset. But that only works in the pro-version of Unity, and I want something that works in the free version. This post shows my solution.

In Unity3D, you can implement your own custom fog on a material by writing your own final color function in a custom shader. The final color function is applied after any lightning has been applied, so we wont have light­sources brightening up objects that are supposed to be hidden. The incredibly useful Surface Shader Examples page in the Unity documentation includes an example titled “Custom Fog with Final Color Modifier”, where a custom finalcolor function is used to apply fog based on the vertical position of the pixel in screenspace. This is nearly what I want, but I want the fog to be only dependent on the y-axis-position in world space. Another example in there, titled “Slices via World Space Position”, uses the world coordinates of the rendered pixel to slice the object vertically. From this example we can see how to use the world space position of the pixel we are rendering.

The shader below utilises principles from both the examples. The final color is faded into the fog color using the world coordinates of the rendered pixel, providing a fog-like effect based on the vertical position of the object rather than the distance between the object and the camera.

Shader "Custom/AltitudeFog"
{
    Properties 
    {
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _FogColor ("Fog Color", Color) = (0.5, 0.5, 0.5, 1)
        _FogMaxHeight ("Fog Max Height", Float) = 0.0
        _FogMinHeight ("Fog Min Height", Float) = -1.0
    }
  
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200
        Cull Back
        ZWrite On
  
        CGPROGRAM
  
        #pragma surface surf Lambert finalcolor:finalcolor vertex:vert
  
        sampler2D _MainTex;
        float4 _FogColor;
        float _FogMaxHeight;
        float _FogMinHeight;
  
        struct Input 
        {
            float2 uv_MainTex;
            float4 pos;
        };
  
        void vert (inout appdata_full v, out Input o) 
        {
            float4 hpos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.pos = mul(_Object2World, v.vertex);
            o.uv_MainTex = v.texcoord.xy;
        }
  
        void surf (Input IN, inout SurfaceOutput o) 
        {
            float4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
        }
  
        void finalcolor (Input IN, SurfaceOutput o, inout fixed4 color)
        {
            #ifndef UNITY_PASS_FORWARDADD
            float lerpValue = clamp((IN.pos.y - _FogMinHeight) / (_FogMaxHeight - _FogMinHeight), 0, 1);
            color.rgb = lerp (_FogColor.rgb, color.rgb, lerpValue);
            #endif
        }
  
        ENDCG
    }
  
    FallBack "Diffuse"
}

This shader, used on the material of any partially submerged objects, applies a linear depth fog using the lerp function, but you could replace it with any function you want. A straightforward change is to use an exponential fog by changing the calculations in the finalcolor function, but for now, I’m quite happy with linear depth fog.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment