├── .gitattributes ├── HeightFog.cs ├── HeightFog.cs.meta ├── HeightFog.hlsl ├── HeightFog.hlsl.meta ├── LICENSE.md ├── README.md ├── Resources.meta ├── Resources ├── ScreenSpaceHeightFog.shader └── ScreenSpaceHeightFog.shader.meta └── ~git ├── Screenshot_1.jpg └── Screenshot_2.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /HeightFog.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using Unity.Collections; 3 | using UnityEngine; 4 | using UnityEngine.Rendering; 5 | using UnityEngine.Rendering.PostProcessing; 6 | 7 | namespace Hollow.HeightFog 8 | { 9 | public enum SunColorMode 10 | { 11 | Multiply, 12 | Override 13 | } 14 | 15 | [System.Serializable] 16 | public class SunColorTypeEnumParameter : ParameterOverride { } 17 | 18 | // Keep in sync with cbuffer in HeightFog.hlsl 19 | [StructLayout(LayoutKind.Sequential)] 20 | unsafe struct FogVolume_ConstantBuffer 21 | { 22 | public Matrix4x4 FogVolume_InverseVPMatrix; 23 | 24 | public Vector4 FogVolume_SunDirection; 25 | public Vector4 FogVolume_SunColor; 26 | public Vector4 FogVolume_Emissive; 27 | public Vector4 FogVolume_SkyParams; 28 | public Vector4 FogVolume_SmoothDepthParams; 29 | 30 | public Vector2 FogVolume_Distances; 31 | public Vector2 FogVolume_GlobalFogPhaseW; 32 | 33 | public Vector2 FogVolume_HeightExponents; 34 | public float FogVolume_Extinction; 35 | public float FogVolume_BaseHeight; 36 | 37 | public float FogVolume_GlobalFogPhase; 38 | public float FogVolume_AmbientStrength; 39 | } 40 | 41 | [System.Serializable] 42 | [PostProcess(typeof(HeightFogRenderer), PostProcessEvent.BeforeTransparent, "Custom/Height Fog")] 43 | public class HeightFog : PostProcessEffectSettings 44 | { 45 | [Tooltip("[Default = 0]\nDensity of the fog particles. To work correctly you need to keep 'Smooth Length' equal to 1")] 46 | [Range(0, 1f)] public FloatParameter density = new() { value = 0.0f }; 47 | [Tooltip("[Default = -1]\nOffsets fog density from camera"), UnityEngine.Rendering.PostProcessing.Min(0f)] 48 | public FloatParameter minDistnace = new() { value = -1.0f }; 49 | [Tooltip("[Default = 1]"), UnityEngine.Rendering.PostProcessing.Min(0f)] 50 | public FloatParameter smoothLength = new() { value = 1.0f }; 51 | 52 | [Space] 53 | [ColorUsage(false, true)] public ColorParameter emissive = new() { value = Color.white }; 54 | 55 | [Space] 56 | [UnityEngine.Rendering.PostProcessing.Min(0)] public FloatParameter sunLightStength = new(); 57 | [Tooltip("Multiply - 'Sun Color' will multiply color of the current directional light\n" + 58 | "Override - 'Sun Color' will replace color of the current directional light")] 59 | public SunColorTypeEnumParameter sunMode = new(); 60 | [ColorUsage(false, true)] public ColorParameter sunColor = new() { value = Color.white }; 61 | 62 | [Header("Phase")] 63 | [Tooltip("[Default = -0.5]\nNegative values will collect light from the sun into a point, around zero will evenly spread light color and positive values will make sun appear on the opposite side")] 64 | [Range(-1f, 1f)] public FloatParameter phase = new() { value = -.5f }; 65 | [Range(0f, 1f)] public FloatParameter phaseW0 = new() { value = 1f }; 66 | [Range(0f, 1f)] public FloatParameter phaseW1 = new() { value = 0f }; 67 | 68 | [Header("Height")] 69 | [UnityEngine.Rendering.PostProcessing.Min(0)] public FloatParameter minHeight = new(); 70 | [UnityEngine.Rendering.PostProcessing.Min(0)] public FloatParameter transitionLength = new() { value = 100f }; 71 | 72 | [Header("Sky")] 73 | public BoolParameter skyHandling = new() { value = true }; 74 | public FloatParameter skyPower = new() { value = 1f }; 75 | [Tooltip("Represents percentage of sky that will be populated with fog. Everything below left handle is fully covered in fog, everything above is fully visible")] 76 | [MinMax(0, 1)] 77 | public Vector2Parameter skyFillRange = new() { value = new(0, 1f) }; 78 | } 79 | 80 | public unsafe class HeightFogRenderer : PostProcessEffectRenderer 81 | { 82 | private GlobalKeyword fogEnabledKeyword; 83 | private Shader heightFogShader; 84 | private GraphicsBuffer fogVolumeDataBuffer; 85 | private NativeArray fogVolumeData; 86 | static readonly int FogVolumeConstantBuffer = Shader.PropertyToID("FogVolume_ConstantBuffer"); 87 | 88 | public override void Init() 89 | { 90 | heightFogShader = Resources.Load("ScreenSpaceHeightFog"); 91 | 92 | fogVolumeDataBuffer = new(GraphicsBuffer.Target.Constant, 1, sizeof(FogVolume_ConstantBuffer)); 93 | fogVolumeData = new(1, Allocator.Persistent); 94 | 95 | fogEnabledKeyword = GlobalKeyword.Create("_HEIGHT_FOG_ENABLED"); 96 | Shader.SetKeyword(fogEnabledKeyword, true); 97 | } 98 | 99 | public override void Release() 100 | { 101 | Shader.SetKeyword(fogEnabledKeyword, false); 102 | fogVolumeData.Dispose(); 103 | fogVolumeDataBuffer.Dispose(); 104 | } 105 | 106 | internal Vector2 HeightExponents() 107 | { 108 | var minHeight = settings.minHeight.value; 109 | float maxHeight = minHeight + settings.transitionLength.value; 110 | float layerDepth = Mathf.Max(0.01f, maxHeight - minHeight); 111 | 112 | // Exp[-d / H] = 0.001 113 | // -d / H = Log[0.001] 114 | // H = d / -Log[0.001] 115 | // return d * 0.144765f; 116 | float H = layerDepth * 0.144765f; 117 | return new Vector2(1.0f / H, H); 118 | } 119 | 120 | public override void Render(PostProcessRenderContext context) 121 | { 122 | var sheet = context.propertySheets.Get(heightFogShader); 123 | 124 | FogVolume_ConstantBuffer fogData = default; 125 | 126 | var view = context.camera.worldToCameraMatrix; 127 | var proj = GL.GetGPUProjectionMatrix(context.camera.projectionMatrix, true); 128 | 129 | Matrix4x4 invView = default; 130 | Matrix4x4.Inverse3DAffine(view, ref invView); 131 | 132 | var invVP = invView * proj.inverse; 133 | 134 | fogData.FogVolume_InverseVPMatrix = invVP; 135 | fogData.FogVolume_Distances.x = settings.minDistnace.value; 136 | fogData.FogVolume_Distances.y = Mathf.Max(0.0001f, settings.smoothLength.value); 137 | fogData.FogVolume_Extinction = Mathf.Pow(settings.density.value, 2 * (float)System.Math.E); 138 | fogData.FogVolume_GlobalFogPhase = settings.phase.value; 139 | fogData.FogVolume_GlobalFogPhaseW = new(settings.phaseW0.value, settings.phaseW1.value); 140 | fogData.FogVolume_BaseHeight = settings.minHeight.value; 141 | fogData.FogVolume_HeightExponents = HeightExponents(); 142 | fogData.FogVolume_AmbientStrength = 0.0f; 143 | fogData.FogVolume_Emissive = settings.emissive.value; 144 | 145 | fogData.FogVolume_SkyParams.x = settings.skyPower.value; 146 | fogData.FogVolume_SkyParams.y = settings.skyHandling.value? 1 : 0; 147 | fogData.FogVolume_SkyParams.z = settings.skyFillRange.value.x; 148 | fogData.FogVolume_SkyParams.w = settings.skyFillRange.value.y; 149 | 150 | // x - 1 / (smoothInterval), y - pow(1./s, 2), z-0.5*pow(1./s,3), w - h0 151 | float s = Mathf.Max(0.0001f, settings.smoothLength.value); 152 | fogData.FogVolume_SmoothDepthParams.x = s == 0? 0.0f : 1.0f / s; 153 | fogData.FogVolume_SmoothDepthParams.y = Mathf.Pow(1/s, 2); 154 | fogData.FogVolume_SmoothDepthParams.z = Mathf.Pow(1/s, 3) * 0.5f; 155 | // pow(1/s, 2) * pow(s, 3) - 0.5*pow(1/s,3)*pow(s, 4) 156 | fogData.FogVolume_SmoothDepthParams.w = Mathf.Pow(1/s, 2)*Mathf.Pow(s, 3) - 0.5f*Mathf.Pow(1/s, 3)*Mathf.Pow(s, 4); 157 | 158 | if (RenderSettings.sun) 159 | { 160 | var sun = RenderSettings.sun; 161 | if (settings.sunMode.value == SunColorMode.Override) 162 | { 163 | fogData.FogVolume_SunColor = settings.sunColor.value; 164 | } 165 | else 166 | { 167 | fogData.FogVolume_SunColor = settings.sunColor.value * RenderSettings.sun.color; 168 | } 169 | 170 | fogData.FogVolume_SunColor *= sun.intensity; 171 | 172 | fogData.FogVolume_SunDirection = sun.transform.forward; 173 | fogData.FogVolume_SunDirection.w = settings.sunLightStength.value; 174 | } 175 | else 176 | { 177 | fogData.FogVolume_SunColor = Color.black; 178 | fogData.FogVolume_SunDirection.w = 0; 179 | } 180 | 181 | fogVolumeData[0] = fogData; 182 | Shader.SetGlobalConstantBuffer(FogVolumeConstantBuffer, fogVolumeDataBuffer, 0, fogVolumeDataBuffer.stride); 183 | context.command.SetBufferData(fogVolumeDataBuffer, fogVolumeData); 184 | context.command.SetGlobalConstantBuffer(fogVolumeDataBuffer, FogVolumeConstantBuffer, 0, fogVolumeDataBuffer.stride); 185 | 186 | context.command.BlitFullscreenTriangle(context.source, context.destination, sheet, 0); 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /HeightFog.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 57a2af8d1e4e67e4592ab0eed24edc48 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /HeightFog.hlsl: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef INV_FOUR_PI 4 | #define INV_FOUR_PI 0.07957747154594766788 5 | #endif 6 | 7 | CBUFFER_START(FogVolume_ConstantBuffer) 8 | 9 | float4x4 FogVolume_InverseVPMatrix; 10 | 11 | float4 FogVolume_SunDirection; 12 | float4 FogVolume_SunColor; 13 | float4 FogVolume_Emissive; 14 | float4 FogVolume_SkyParams; 15 | float4 FogVolume_SmoothDepthParams; // x - 1 / (smoothInterval), y - pow(1./s, 2), z-0.5*pow(1./s,3), w - h0 16 | 17 | float2 FogVolume_Distances; 18 | 19 | float2 FogVolume_GlobalFogPhaseW; 20 | float2 FogVolume_HeightExponents; 21 | float FogVolume_Extinction; 22 | float FogVolume_BaseHeight; 23 | 24 | float FogVolume_GlobalFogPhase; 25 | float FogVolume_AmbientStrength; 26 | 27 | CBUFFER_END 28 | #define FogVolume_H0 (FogVolume_SmoothDepthParams.w) 29 | #define FogVolume_SmoothIntervalRcp (FogVolume_SmoothDepthParams.x) 30 | 31 | float HG_Phase(float g, float theta) 32 | { 33 | float hg = (1-g*g) / pow(abs(1+g*g - 2*g*theta), 2./3.); 34 | return INV_FOUR_PI * hg; 35 | } 36 | 37 | float RDR_Phase_M2(float g, float theta, float sigma_ext, float w0, float w1) 38 | { 39 | float hg = HG_Phase(g, theta) * w0; 40 | 41 | const float M = 2; 42 | float c = (w1 * sigma_ext) / (M - 1); 43 | 44 | float c0 = HG_Phase(pow(2/3, 1)*g, theta); 45 | float c1 = HG_Phase(pow(2/3, 2)*g, theta); 46 | 47 | float hg2 = c * (c0+c1); 48 | 49 | return hg + hg2; 50 | } 51 | 52 | float3 FogVolume_SampleGlobalLightingScattering(float3 viewDirection, float extinction) 53 | { 54 | float theta = dot(FogVolume_SunDirection.xyz, viewDirection.xyz); 55 | float g = FogVolume_GlobalFogPhase; 56 | float phase = RDR_Phase_M2(g, theta, extinction, FogVolume_GlobalFogPhaseW.x, FogVolume_GlobalFogPhaseW.y); 57 | float3 light = FogVolume_SunColor.rgb; 58 | 59 | float3 scatteredLight = lerp(FogVolume_Emissive.rgb, light, saturate(phase * FogVolume_SunDirection.w)); 60 | 61 | return scatteredLight; 62 | } 63 | 64 | // From com.unity.srp-core Volumetrics 65 | float OpticalDepthHeightFog(float baseExtinction, float baseHeight, float2 heightExponents, 66 | float cosZenith, float startHeight, float intervalLength) 67 | { 68 | // Height fog is composed of two slices of optical depth: 69 | // - homogeneous fog below 'baseHeight': d = k * t 70 | // - exponential fog above 'baseHeight': d = Integrate[k * e^(-(h + z * x) / H) dx, {x, 0, t}] 71 | 72 | float H = heightExponents.y; 73 | float rcpH = heightExponents.x; 74 | float Z = cosZenith; 75 | float absZ = max(abs(cosZenith), 0.001f); 76 | float rcpAbsZ = rcp(absZ); 77 | 78 | float endHeight = startHeight + intervalLength * Z; 79 | float minHeight = min(startHeight, endHeight); 80 | float h = max(minHeight - baseHeight, 0); 81 | 82 | float homFogDist = clamp((baseHeight - minHeight) * rcpAbsZ, 0, intervalLength); 83 | float expFogDist = intervalLength - homFogDist; 84 | float expFogMult = exp(-h * rcpH) * (1 - exp(-expFogDist * absZ * rcpH)) * (rcpAbsZ * H); 85 | 86 | return baseExtinction * (homFogDist + expFogMult); 87 | } 88 | 89 | void FogVolume_ComputeAnalyticalUniformFog(float3 origin, float3 viewDirection, float segmentLength, 90 | out float3 outScattering, out float outTransmittance) 91 | { 92 | float3 albedo = FogVolume_Extinction.xxx; // FogVolume_FogAlbedoExtinction.rgb; 93 | float extinction = FogVolume_Extinction.x; // FogVolume_FogAlbedoExtinction.a; 94 | 95 | // FogVolume_Distances.x - min distance, FogVolume_Distances.y - transition interval length 96 | float s = FogVolume_Distances.y; 97 | float smoothOpticalDepth; 98 | if(segmentLength < FogVolume_Distances.x) 99 | { 100 | smoothOpticalDepth = 0; 101 | } 102 | else if(segmentLength < (FogVolume_Distances.x+s)) 103 | { 104 | float x = max(0.0, segmentLength - FogVolume_Distances.x); 105 | // Integral over scaled smoothstep function 106 | smoothOpticalDepth = FogVolume_SmoothDepthParams.y * pow(x, 3) - FogVolume_SmoothDepthParams.z*pow(x, 4); 107 | } 108 | else 109 | { 110 | float x = max(0.0, segmentLength - FogVolume_Distances.x); 111 | float h0 = FogVolume_H0; // pow(1/s, 2) * pow(s, 3) - 0.5*pow(1/s,3)*pow(s, 4); 112 | 113 | smoothOpticalDepth = (x-s)+h0; 114 | } 115 | smoothOpticalDepth *= extinction; 116 | 117 | float3 fogColor = FogVolume_SampleGlobalLightingScattering(viewDirection, extinction); 118 | float3 scattering = fogColor * albedo; 119 | 120 | // Takes care of base extinction too 121 | float heightOpticalDepth = OpticalDepthHeightFog(extinction, FogVolume_BaseHeight, FogVolume_HeightExponents, viewDirection.y, origin.y, segmentLength); 122 | 123 | // This is more correct than multiply but still iffy 124 | float transmittance = exp(-min(smoothOpticalDepth, heightOpticalDepth)); 125 | 126 | // From frostbite volumetric fog talk, energy preserving scattering 127 | // This still works because "transmittance" part is treated as constant in an integral, so simple formula produces correct results 128 | float3 scatteringIntegrated = (scattering - scattering * transmittance) / max(0.0001, extinction); 129 | 130 | outScattering = scatteringIntegrated; // scattering * (1-transmittance); 131 | outTransmittance = transmittance; 132 | } 133 | 134 | float4 FogVolume_Apply(float3 worldPosition, float4 color) 135 | { 136 | if(FogVolume_Extinction.x <= 0) // Unity will 0-init by default, so if cbuffer isn't bound, fog is not enabled 137 | return color; 138 | 139 | // view -> vector from camera to pixel world-space position 140 | float3 view = worldPosition - _WorldSpaceCameraPos; 141 | float viewLength = length(view); // distance from camera to pixel 142 | view /= viewLength; // normalize view vector 143 | 144 | float3 scattering; float transmittance; // scattering -> fog color, transmittance -> fog opacity 145 | FogVolume_ComputeAnalyticalUniformFog(_WorldSpaceCameraPos, view, viewLength, scattering, transmittance); 146 | 147 | color.rgb = lerp(scattering.rgb * color.a, color.rgb, transmittance); 148 | 149 | return color; 150 | } -------------------------------------------------------------------------------- /HeightFog.hlsl.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c467eed981df49dba07cd9b741545b92 3 | timeCreated: 1731532651 -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Alex Khodyka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Height-Fog V2 3 | This is NOT volumetric fog, it's an image effect. It is based on same math, but analytical and as such, doesn't have support for shadows and other effects you'd expect from volumetrics 4 |
5 | Screenshots 6 | 7 | ![](./~git/Screenshot_1.jpg) 8 | 9 | ![](./~git/Screenshot_2.jpg) 10 | 11 |
12 | 13 | # How to use 14 | Like any Post Processing V2 shader. Add it as effect to Post Process volume. 15 | 16 | Fog is applied only to opaque geometry by default, to support semi transparent objects you need to use "FogVolume_ApplyFog" method in "HeightFog.hlsl" file 17 | 18 | ### **NOTE:** You need to enable unsafe code in project settings/assembly definition in order to compile this project 19 | 20 | ### Fragment shader version 21 | ```c 22 | #include "HeightFog.hlsl" 23 | // 24 | #pragma multi_compile _ _HEIGHT_FOG_ENABLED 25 | 26 | struct v2f 27 | { 28 | float3 wpos : VAR_WORLDPOS; 29 | } 30 | 31 | v2f vert (appdata v) 32 | { 33 | v2f o; 34 | //...// 35 | o.wpos = mul(unity_ObjectToWorld, v.vertex); 36 | //...// 37 | return o; 38 | } 39 | 40 | fixed4 frag (v2f i) : SV_Target 41 | { 42 | fixed4 col = tex2D(_MainTex, i.uv); 43 | 44 | #if _HEIGHT_FOG_ENABLED // 45 | col = FogVolume_ApplyFog(i.wpos, col); 46 | #endif 47 | return col; 48 | } 49 | 50 | ``` 51 | 52 | ### Surface shader version 53 | ```c 54 | #include "HeightFog.hlsl" 55 | #pragma surface surf Standard alpha:auto finalcolor:compute_fog 56 | // ↑ add this 57 | 58 | void compute_fog (Input IN, SurfaceOutputStandard o, inout float4 color) 59 | { 60 | color = FogVolume_Apply(IN.worldPos, color); 61 | } 62 | ``` 63 | 64 | ## References 65 | * [Frostbite - Physically-based & Unified Volumetric Rendering](https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite) 66 | * [Volumetric Fog. B.Wronski](https://bartwronski.com/wp-content/uploads/2014/08/bwronski_volumetric_fog_siggraph2014.pdf) 67 | * [Creating the Atmospheric World of Red Dead Redemption 2](https://advances.realtimerendering.com/s2019/index.htm) -------------------------------------------------------------------------------- /Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4ac212b588790bb4c8bfde233aa5703f 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/ScreenSpaceHeightFog.shader: -------------------------------------------------------------------------------- 1 | Shader "Hidden/Custom/Height Fog" 2 | { 3 | HLSLINCLUDE 4 | #pragma target 5.0 5 | 6 | #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl" 7 | #include "../HeightFog.hlsl" 8 | 9 | TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture); 10 | TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex); 11 | 12 | struct Varyings 13 | { 14 | float4 position : SV_Position; 15 | float2 texcoord : TEXCOORD0; 16 | }; 17 | 18 | Varyings Vertex(uint vertexID : SV_VertexID) 19 | { 20 | // Vertex ID -> clip space vertex position 21 | float x = (vertexID != 1) ? -1 : 3; 22 | float y = (vertexID == 2) ? -3 : 1; 23 | float3 vpos = float3(x, y, 1.0); 24 | 25 | Varyings o; 26 | o.position = float4(vpos.x, -vpos.y, 1, 1); 27 | o.texcoord = (vpos.xy + 1) / 2; 28 | return o; 29 | } 30 | 31 | float unlerp(float a, float b, float x) { return (x - a) / (b - a); } 32 | 33 | float4 Frag(Varyings i) : SV_Target 34 | { 35 | float2 clip = float2(i.texcoord.x, 1 - i.texcoord.y) * 2 - 1; 36 | float deviceDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoord); 37 | 38 | float4 wsPos = mul(FogVolume_InverseVPMatrix, float4(clip, deviceDepth, 1)); 39 | wsPos.xyz /= wsPos.w; 40 | 41 | float3 view = wsPos.xyz - _WorldSpaceCameraPos; 42 | float viewLength = length(view); 43 | view /= viewLength; 44 | 45 | float3 scattering; 46 | float transmittance; 47 | FogVolume_ComputeAnalyticalUniformFog(_WorldSpaceCameraPos, view, viewLength, scattering, transmittance); 48 | 49 | bool isSkybox = deviceDepth < FLT_EPSILON * 10; 50 | if (isSkybox && FogVolume_SkyParams.y > 0) 51 | { 52 | float s = max(0, dot(view, float3(0, 1, 0))); 53 | s = saturate(unlerp(FogVolume_SkyParams.z, FogVolume_SkyParams.w, s)); 54 | s = smoothstep(1, 0, pow(s, FogVolume_SkyParams.x)); // 1 - pow(s, FogVolume_SkyParams.x); 55 | 56 | float3 fogColor = FogVolume_SampleGlobalLightingScattering(view, FogVolume_Extinction); 57 | scattering = fogColor * FogVolume_Extinction.xxx; 58 | 59 | float skyTransmittance = 1 - s; 60 | float3 skyScattering = (scattering - scattering * skyTransmittance) / max(0.0001, FogVolume_Extinction.x); 61 | 62 | float4 fog = float4(skyScattering, skyTransmittance); 63 | return fog; 64 | } 65 | else 66 | { 67 | float4 fog = float4(scattering, transmittance); 68 | return fog; 69 | } 70 | } 71 | ENDHLSL 72 | 73 | SubShader 74 | { 75 | Cull Off ZWrite Off ZTest Always 76 | 77 | Pass 78 | { 79 | BlendOp Add 80 | Blend One SrcAlpha, Zero One 81 | HLSLPROGRAM 82 | #pragma shader_feature HF_LIGHT_ATTEN 83 | #pragma vertex Vertex 84 | #pragma fragment Frag 85 | ENDHLSL 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Resources/ScreenSpaceHeightFog.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 67311e19fdbd00643a14c770ed7549dd 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /~git/Screenshot_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SanielX/Height-Fog/ab6f8278b58d77a6bc9cea4d3c9dcd3f34291fe7/~git/Screenshot_1.jpg -------------------------------------------------------------------------------- /~git/Screenshot_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SanielX/Height-Fog/ab6f8278b58d77a6bc9cea4d3c9dcd3f34291fe7/~git/Screenshot_2.jpg --------------------------------------------------------------------------------