Fog Shader help!

So I’m using the global fog shader & Script for image effects. Now what I’m trying to do, is instead of using 1 fog color for the shader, sample the skybox. I want to give the effect of everything simply fading into the sky, rather than having that look of a mountain in the distance being a solid color…

I need some help to put me in the right direction? One of my ideas was to have the shader place the skybox image across it in a way where… Well the unity 4 docs (not the 5) had a page where it shows an image being overlayed on the soldier, but depending on where the camera was… as in… imagine drawing a 2D image on a screen, and masking it just to the character. Perhaps I could do that, and use a seperate camera that only sees the skybox, and sample the render texture? I’m really not sure how to go about doing this.

Help is appreciated :).

Shader "Hidden/GlobalFog" {
Properties {
    _MainTex ("Base (RGB)", 2D) = "black" {}
}

CGINCLUDE

    #include "UnityCG.cginc"

    uniform sampler2D _MainTex;
    uniform sampler2D_float _CameraDepthTexture;
  
    // x = fog height
    // y = FdotC (CameraY-FogHeight)
    // z = k (FdotC > 0.0)
    // w = a/2
    uniform float4 _HeightParams;
  
    // x = start distance
    uniform float4 _DistanceParams;
  
    int4 _SceneFogMode; // x = fog mode, y = use radial flag
    float4 _SceneFogParams;
    #ifndef UNITY_APPLY_FOG
    half4 unity_FogColor;
    half4 unity_FogDensity;
    #endif  

    uniform float4 _MainTex_TexelSize;
  
    // for fast world space reconstruction
    uniform float4x4 _FrustumCornersWS;
    uniform float4 _CameraWS;

    struct v2f {
        float4 pos : SV_POSITION;
        float2 uv : TEXCOORD0;
        float2 uv_depth : TEXCOORD1;
        float4 interpolatedRay : TEXCOORD2;
    };
  
    v2f vert (appdata_img v)
    {
        v2f o;
        half index = v.vertex.z;
        v.vertex.z = 0.1;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.texcoord.xy;
        o.uv_depth = v.texcoord.xy;
      
        #if UNITY_UV_STARTS_AT_TOP
        if (_MainTex_TexelSize.y < 0)
            o.uv.y = 1-o.uv.y;
        #endif              
      
        o.interpolatedRay = _FrustumCornersWS[(int)index];
        o.interpolatedRay.w = index;
      
        return o;
    }
  
    // Applies one of standard fog formulas, given fog coordinate (i.e. distance)
    half ComputeFogFactor (float coord)
    {
        float fogFac = 0.0;
        if (_SceneFogMode.x == 1) // linear
        {
            // factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
            fogFac = coord * _SceneFogParams.z + _SceneFogParams.w;
        }
        if (_SceneFogMode.x == 2) // exp
        {
            // factor = exp(-density*z)
            fogFac = _SceneFogParams.y * coord; fogFac = exp2(-fogFac);
        }
        if (_SceneFogMode.x == 3) // exp2
        {
            // factor = exp(-(density*z)^2)
            fogFac = _SceneFogParams.x * coord; fogFac = exp2(-fogFac*fogFac);
        }
        return saturate(fogFac);
    }

    // Distance-based fog
    float ComputeDistance (float3 camDir, float zdepth)
    {
        float dist;
        if (_SceneFogMode.y == 1)
            dist = length(camDir);
        else
            dist = zdepth * _ProjectionParams.z;
        // Built-in fog starts at near plane, so match that by
        // subtracting the near value. Not a perfect approximation
        // if near plane is very large, but good enough.
        dist -= _ProjectionParams.y;
        return dist;
    }

    // Linear half-space fog, from https://www.terathon.com/lengyel/Lengyel-UnifiedFog.pdf
    float ComputeHalfSpace (float3 wsDir)
    {
        float3 wpos = _CameraWS + wsDir;
        float FH = _HeightParams.x;
        float3 C = _CameraWS;
        float3 V = wsDir;
        float3 P = wpos;
        float3 aV = _HeightParams.w * V;
        float FdotC = _HeightParams.y;
        float k = _HeightParams.z;
        float FdotP = P.y-FH;
        float FdotV = wsDir.y;
        float c1 = k * (FdotP + FdotC);
        float c2 = (1-2*k) * FdotP;
        float g = min(c2, 0.0);
        g = -length(aV) * (c1 - g * g / abs(FdotV+1.0e-5f));
        return g;
    }

    half4 ComputeFog (v2f i, bool distance, bool height) : SV_Target
    {
        half4 sceneColor = tex2D(_MainTex, i.uv);
      
        // Reconstruct world space position & direction
        // towards this screen pixel.
        float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
        float dpth = Linear01Depth(rawDepth);
        float4 wsDir = dpth * i.interpolatedRay;
        float4 wsPos = _CameraWS + wsDir;

        // Compute fog distance
        float g = _DistanceParams.x;
        if (distance)
            g += ComputeDistance (wsDir, dpth);
        if (height)
            g += ComputeHalfSpace (wsDir);

        // Compute fog amount
        half fogFac = ComputeFogFactor (max(0.0,g));
        // Do not fog skybox
        if (rawDepth >= 0.999999)
            fogFac = 1.0;
        //return fogFac; // for debugging
      
        // Lerp between fog color & original scene color
        // by fog amount
        return lerp (unity_FogColor, sceneColor, fogFac);
    }

ENDCG

SubShader
{
    ZTest Always Cull Off ZWrite Off Fog { Mode Off }

    // 0: distance + height
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        half4 frag (v2f i) : SV_Target { return ComputeFog (i, true, true); }
        ENDCG
    }
    // 1: distance
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        half4 frag (v2f i) : SV_Target { return ComputeFog (i, true, false); }
        ENDCG
    }
    // 2: height
    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        half4 frag (v2f i) : SV_Target { return ComputeFog (i, false, true); }
        ENDCG
    }
}

Fallback off

}
using System;
using UnityEngine;

namespace UnitySampleAssets.ImageEffects
{
    [ExecuteInEditMode]
    [RequireComponent (typeof(Camera))]
    [AddComponentMenu ("Image Effects/Rendering/Global Fog")]
    class GlobalFog : PostEffectsBase
    {
        [Tooltip("Apply distance-based fog?")]
        public bool  distanceFog = true;
        [Tooltip("Distance fog is based on radial distance from camera when checked")]
        public bool  useRadialDistance = false;
        [Tooltip("Apply height-based fog?")]
        public bool  heightFog = true;
        [Tooltip("Fog top Y coordinate")]
        public float height = 1.0f;
        [Range(0.001f,10.0f)]
        public float heightDensity = 2.0f;
        [Tooltip("Push fog away from the camera by this amount")]
        public float startDistance = 0.0f;

        public Shader fogShader = null;
        private Material fogMaterial = null;


        public override bool CheckResources ()
        {
            CheckSupport (true);

            fogMaterial = CheckShaderAndCreateMaterial (fogShader, fogMaterial);

            if (!isSupported)
                ReportAutoDisable ();
            return isSupported;
        }

        [ImageEffectOpaque]
        void OnRenderImage (RenderTexture source, RenderTexture destination)
        {
            if (CheckResources()==false || (!distanceFog && !heightFog))
            {
                Graphics.Blit (source, destination);
                return;
            }

            Camera cam = GetComponent<Camera>();
            Transform camtr = cam.transform;
            float camNear = cam.nearClipPlane;
            float camFar = cam.farClipPlane;
            float camFov = cam.fieldOfView;
            float camAspect = cam.aspect;

            Matrix4x4 frustumCorners = Matrix4x4.identity;

            float fovWHalf = camFov * 0.5f;

            Vector3 toRight = camtr.right * camNear * Mathf.Tan (fovWHalf * Mathf.Deg2Rad) * camAspect;
            Vector3 toTop = camtr.up * camNear * Mathf.Tan (fovWHalf * Mathf.Deg2Rad);

            Vector3 topLeft = (camtr.forward * camNear - toRight + toTop);
            float camScale = topLeft.magnitude * camFar/camNear;

            topLeft.Normalize();
            topLeft *= camScale;

            Vector3 topRight = (camtr.forward * camNear + toRight + toTop);
            topRight.Normalize();
            topRight *= camScale;

            Vector3 bottomRight = (camtr.forward * camNear + toRight - toTop);
            bottomRight.Normalize();
            bottomRight *= camScale;

            Vector3 bottomLeft = (camtr.forward * camNear - toRight - toTop);
            bottomLeft.Normalize();
            bottomLeft *= camScale;

            frustumCorners.SetRow (0, topLeft);
            frustumCorners.SetRow (1, topRight);
            frustumCorners.SetRow (2, bottomRight);
            frustumCorners.SetRow (3, bottomLeft);

            var camPos= camtr.position;
            float FdotC = camPos.y-height;
            float paramK = (FdotC <= 0.0f ? 1.0f : 0.0f);
            fogMaterial.SetMatrix ("_FrustumCornersWS", frustumCorners);
            fogMaterial.SetVector ("_CameraWS", camPos);
            fogMaterial.SetVector ("_HeightParams", new Vector4 (height, FdotC, paramK, heightDensity*0.5f));
            fogMaterial.SetVector ("_DistanceParams", new Vector4 (-Mathf.Max(startDistance,0.0f), 0, 0, 0));

            var sceneMode= RenderSettings.fogMode;
            var sceneDensity= RenderSettings.fogDensity;
            var sceneStart= RenderSettings.fogStartDistance;
            var sceneEnd= RenderSettings.fogEndDistance;
            Vector4 sceneParams;
            bool  linear = (sceneMode == FogMode.Linear);
            float diff = linear ? sceneEnd - sceneStart : 0.0f;
            float invDiff = Mathf.Abs(diff) > 0.0001f ? 1.0f / diff : 0.0f;
            sceneParams.x = sceneDensity * 1.2011224087f; // density / sqrt(ln(2)), used by Exp2 fog mode
            sceneParams.y = sceneDensity * 1.4426950408f; // density / ln(2), used by Exp fog mode
            sceneParams.z = linear ? -invDiff : 0.0f;
            sceneParams.w = linear ? sceneEnd * invDiff : 0.0f;
            fogMaterial.SetVector ("_SceneFogParams", sceneParams);
            fogMaterial.SetVector ("_SceneFogMode", new Vector4((int)sceneMode, useRadialDistance ? 1 : 0, 0, 0));

            int pass = 0;
            if (distanceFog && heightFog)
                pass = 0; // distance + height
            else if (distanceFog)
                pass = 1; // distance only
            else
                pass = 2; // height only
            CustomGraphicsBlit (source, destination, fogMaterial, pass);
        }

        static void CustomGraphicsBlit (RenderTexture source, RenderTexture dest, Material fxMaterial, int passNr)
        {
            RenderTexture.active = dest;

            fxMaterial.SetTexture ("_MainTex", source);

            GL.PushMatrix ();
            GL.LoadOrtho ();

            fxMaterial.SetPass (passNr);

            GL.Begin (GL.QUADS);

            GL.MultiTexCoord2 (0, 0.0f, 0.0f);
            GL.Vertex3 (0.0f, 0.0f, 3.0f); // BL

            GL.MultiTexCoord2 (0, 1.0f, 0.0f);
            GL.Vertex3 (1.0f, 0.0f, 2.0f); // BR

            GL.MultiTexCoord2 (0, 1.0f, 1.0f);
            GL.Vertex3 (1.0f, 1.0f, 1.0f); // TR

            GL.MultiTexCoord2 (0, 0.0f, 1.0f);
            GL.Vertex3 (0.0f, 1.0f, 0.0f); // TL

            GL.End ();
            GL.PopMatrix ();
        }
    }
}

Bump!

I do something similar to this, but rather than sampling the skybox I’m rendering a special ‘fogging scene’, which blends over objects based on distance. In my case, I render that scene to a small texture since it has no high frequency information, then use that texture in the fog shader for the fog color. You’d have to do something similar, or use GrabPass, if your skybox is made up of multiple objects.

However, if your skybox is just a cubmap, you can just sample that directly in the fog shader, which will be much easier/faster…