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 appdata_fog { float4 vertex : POSITION; half2 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv_depth : TEXCOORD1; float4 interpolatedRay : TEXCOORD2; }; v2f vert (appdata_fog v) { v2f o; v.vertex.z = 0.1; o.pos = UnityObjectToClipPos(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 int frustumIndex = v.texcoord.x + (2 * o.uv.y); o.interpolatedRay = _FrustumCornersWS[frustumIndex]; o.interpolatedRay.w = frustumIndex; 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, UnityStereoTransformScreenSpaceTex(i.uv)); // Reconstruct world space position & direction // towards this screen pixel. float rawDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, UnityStereoTransformScreenSpaceTex(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 (dpth == _DistanceParams.y) 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 }