Radial fog in shader graph BIRP

Hello, I asked AI to do radial fog in shader graph and I was surprised that it worked. There were two issues though, shadows got applied over the fog as well as SSAO.

I’m guessing the only other option for me would be to create a post processing shader for fog?


Indeed, since the fog you have is modifying the materials surface properties (just making the surface color white), the rest of the lighting works as usual, like if there is no fog.
So to really have some fog “overlayed” on the shaded object, you’d want to use it as a post process.

Thanks for reply Remy, would it still be possible to do it in regular shader if I create a pass with the transparent tag or later?

What if I edit the unitycg code to change the fog calculations? I’m on BIRP so i’m just trying to think of the best way to go about it.

I was able to get a post process shader working but I’m not sure how to do the fog calculation.

            fixed4 frag (v2f i) : SV_Target
            {
                // Sample the main screen texture
                fixed4 originalColor = tex2D(_MainTex, i.uv);

                // Sample the depth texture and get the linear depth
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
                float linearDepth = Linear01Depth(depth);

                // Reconstruct world space position of the current fragment
                float3 viewPos = i.ray.xyz * linearDepth;
                float3 worldPos = mul(unity_MatrixInvV, float4(viewPos, 1.0)).xyz;

                // Calculate the world space distance from the camera's current position
                float viewDistance = distance(worldPos, _WorldSpaceCameraPos);

                // Calculate a linear fog factor based on view distance
                float fogFactor = saturate((viewDistance - _FogStartDistance) / (_FogEndDistance - _FogStartDistance));

                // Apply the fog
                fixed4 finalColor = lerp(originalColor, _FogColor, fogFactor);

                return finalColor;
            }

What I currently have will create fog based off how far the camera is from (0,0,0) world position. I want the fog to be seen in the distance moving with camera like how the default unity linear fog works.

I feel like this should be working because it looks like the same formula in my shader graph. :thinking:

A valid option since you’re using the built-in pipeline, is to use the final color function of surface shader to make the fog :

But it means that you have to change all your objects materials.

Else you can continue with the post process effect like you’re doing, and from what I see from the code it should look like a radial fog from the camera position … if it’s not working you can try to debug it by outputting the different intermediate values of the function to check if it looks like what is expected.

Thx for reply Remy. In hindsight that might be a better approach. :thinking:

I just got the post process shader working though, after 5+ hours of debugging with google gemini AI. Here are the instructions for anyone wanting to recreate this in BIRP.

  1. Add the following script to your camera gameobject.
using UnityEngine;

public class ExponentialFogEffect : MonoBehaviour
{
    public Material fogMaterial;

    [Range(0, 1)]
    public float fogDensity = 0.05f;
    [Range(0, 500)]
    public float startDistance = 20f;
    public Color fogColor = Color.white;

    private Camera cam;

    void Start()
    {
        cam = GetComponent<Camera>();
    }

    void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        fogMaterial.SetFloat("_FogDensity", fogDensity);
        fogMaterial.SetFloat("_StartDistance", startDistance);
        fogMaterial.SetColor("_FogColor", fogColor);
        fogMaterial.SetVector("_CameraPosition", cam.transform.position);

        Graphics.Blit(source, destination, fogMaterial);
    }
}
  1. Create the following shader.
Shader "Hidden/ExponentialFog"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _FogColor ("Fog Color", Color) = (0.5, 0.5, 0.5, 1)
        _FogDensity ("Fog Density", Range(0, 1)) = 0.05
        _StartDistance ("Fog Start Distance", float) = 20.0
    }
    SubShader
    {
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                float2 uv_depth : TEXCOORD1;
            };

            sampler2D _MainTex;
            sampler2D _CameraDepthTexture;
            
            float3 _CameraPosition;
            float _FogDensity;
            float _StartDistance;
            fixed4 _FogColor;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.uv_depth = v.uv;
                return o;
            }

            // A helper function to get the world position
            float3 GetWorldPos(float2 uv)
            {
                float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv);
                float linearDepth = LinearEyeDepth(depth);
                
                float3 viewRay = mul(unity_CameraInvProjection, float4(uv * 2 - 1, 1, 1));
                
                float3 viewPos = viewRay * linearDepth;
                
                float4 worldPos = mul(unity_CameraToWorld, float4(viewPos, 1));
                return worldPos.xyz;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 worldPos = GetWorldPos(i.uv_depth);
                
                float dist = distance(worldPos.xyz, _CameraPosition);

                // Calculate exponential fog. It is always active but fades in.
                float exponentialFog = 1.0 - exp(-dist * _FogDensity);

                // Blend in the fog after the start distance.
                // We use a linear ramp, but a small blend zone could be added for a smoother transition.
                float startFade = saturate((dist - _StartDistance) / 10.0);
                
                // Combine the effects
                float fogAmount = exponentialFog * startFade;
                
                fixed4 originalColor = tex2D(_MainTex, i.uv);
                
                return lerp(originalColor, _FogColor, fogAmount);
            }
            ENDHLSL
        }
    }
}
  1. Create a material and set it to the shader.

  2. Set the material to the “fogMaterial” in the script on your camera.

  3. Set the fog start, density, color, etc, in the camera script.

I’ve noticed that it doesn’t play nice with the post process stack anti aliasing though, there is some jitter going on and there might be some conflicts with the other post processing effects so it might be best to try Remys method.

Also make sure your camera near plane is at least 0.1 or the camera depth texture doesn’t get generated correctly.

I’ve got to say… I’m impressed with the google AI. It gave me code that didn’t work a couple of times but I kept asking it to give me variations and eventually it gave me snippets that worked here and there. It told me how to debug the shaders and it explained what every line does so I feel like I learned a lot and saved myself 20 hours of headache. I couldn’t have done it without :laughing:

EDIT: I tried your method Remy but SSAO displays on top of it. I think I’ll just use the post process shader and switch to FXAA aliasing instead of TAA. I tried creating a custom post processing V2 stack shader and it worked but I couldn’t figure out where/how to tie it into the TAA code to stop the jittering.