Shader "BriarQueen/UI/Fog Reveal Haar" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1, 1, 1, 1) _FogAmount ("Fog Amount", Range(0, 1)) = 0 _FogColor ("Fog Color", Color) = (0.18, 0.20, 0.26, 1) _FogIntensity ("Fog Intensity", Range(0, 1)) = 0.95 _EdgeSoftness ("Edge Softness", Range(0.05, 1.0)) = 0.55 _NoiseScale ("Noise Scale", Range(1, 20)) = 5 _DriftSpeed ("Drift Speed", Range(0, 0.5)) = 0.04 _DensityVariation ("Density Variation", Range(0, 1)) = 0.28 _AspectRatio ("Aspect Ratio", Float) = 1.777 [Toggle] _FogMotion ("Fog Motion", Float) = 1 [Toggle] _UseSprite ("Use Sprite", Float) = 0 _StencilComp ("Stencil Comparison", Float) = 8 _Stencil ("Stencil ID", Float) = 0 _StencilOp ("Stencil Operation", Float) = 0 _StencilWriteMask ("Stencil Write Mask", Float) = 255 _StencilReadMask ("Stencil Read Mask", Float) = 255 _ColorMask ("Color Mask", Float) = 15 [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0 } SubShader { Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "PreviewType" = "Plane" "CanUseSpriteAtlas" = "True" } Stencil { Ref [_Stencil] Comp [_StencilComp] Pass [_StencilOp] ReadMask [_StencilReadMask] WriteMask [_StencilWriteMask] } Cull Off Lighting Off ZWrite Off ZTest [unity_GUIZTestMode] Blend SrcAlpha OneMinusSrcAlpha ColorMask [_ColorMask] Pass { Name "Default" CGPROGRAM #pragma vertex Vert #pragma fragment Frag #pragma target 3.0 #include "UnityCG.cginc" #include "UnityUI.cginc" #pragma multi_compile_local _ UNITY_UI_CLIP_RECT #pragma multi_compile_local _ UNITY_UI_ALPHACLIP struct AppData { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 uv : TEXCOORD0; float4 worldPosition : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO }; sampler2D _MainTex; fixed4 _Color; fixed4 _TextureSampleAdd; float4 _ClipRect; fixed4 _FogColor; float _FogAmount; float _FogIntensity; float _EdgeSoftness; float _NoiseScale; float _DriftSpeed; float _DensityVariation; float _AspectRatio; float _FogMotion; float _UseSprite; // ── Noise ───────────────────────────────────────────────── float2 Hash22(float2 p) { p = float2(dot(p, float2(127.1, 311.7)), dot(p, float2(269.5, 183.3))); return -1.0 + 2.0 * frac(sin(p) * 43758.5453); } float GradientNoise(float2 uv) { float2 i = floor(uv); float2 f = frac(uv); float2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0); return lerp( lerp(dot(Hash22(i + float2(0, 0)), f - float2(0, 0)), dot(Hash22(i + float2(1, 0)), f - float2(1, 0)), u.x), lerp(dot(Hash22(i + float2(0, 1)), f - float2(0, 1)), dot(Hash22(i + float2(1, 1)), f - float2(1, 1)), u.x), u.y); } float HaarFBM(float2 uv, float2 drift) { float2 p = uv * _NoiseScale; float2 uvA = p + drift; float n = GradientNoise(uvA) * 0.500; n += GradientNoise(uvA * 2.01 + 3.7) * 0.250; n += GradientNoise(uvA * 4.03 + 7.3) * 0.125; n += GradientNoise(uvA * 8.07 + 1.9) * 0.063; float2 uvB = p + drift * float2(0.7, -0.4) + float2(17.3, 5.8); float m = GradientNoise(uvB) * 0.500; m += GradientNoise(uvB * 2.05 + 9.1) * 0.250; m += GradientNoise(uvB * 4.11 + 2.6) * 0.125; float combined = lerp(n, m, 0.4); return saturate(combined * 0.5 + 0.5); } // ── Vertex ──────────────────────────────────────────────── Varyings Vert(AppData input) { Varyings output; UNITY_SETUP_INSTANCE_ID(input); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); output.worldPosition = input.vertex; output.vertex = UnityObjectToClipPos(input.vertex); output.uv = input.texcoord; output.color = input.color * _Color; return output; } // ── Fragment ────────────────────────────────────────────── fixed4 Frag(Varyings input) : SV_Target { #ifdef UNITY_UI_CLIP_RECT float clipAlpha = UnityGet2DClipping(input.worldPosition.xy, _ClipRect); #else float clipAlpha = 1.0; #endif fixed4 texColor = (tex2D(_MainTex, input.uv) + _TextureSampleAdd) * input.color; float2 centredUv = input.uv - 0.5; centredUv.x *= _AspectRatio; float dist = length(centredUv) * 2.0; float2 drift = _FogMotion > 0.5 ? float2(_Time.y * _DriftSpeed, _Time.y * _DriftSpeed * 0.3) : float2(0.0, 0.0); float fogNoise = HaarFBM(input.uv, drift); float noiseBias = (fogNoise - 0.5) * _EdgeSoftness * 2.5; float softEdge = _EdgeSoftness * 0.8; float threshold = lerp(-_EdgeSoftness * 2.0, 2.5, _FogAmount); float fogMask = 1.0 - smoothstep( threshold - softEdge + noiseBias, threshold + softEdge + noiseBias, dist ); fogMask = saturate(fogMask); fixed4 result; if (_UseSprite > 0.5) { // Sprite mode — fog mask directly gates the sprite's alpha // Pixels outside the fog are fully transparent // Pixels inside the fog draw the sprite normally // The fog edge produces a soft natural reveal with noise result.rgb = texColor.rgb; result.a = texColor.a * fogMask * clipAlpha; } else { // Transparent mode — purely procedural fog colour float density = 1.0 - fogNoise * _DensityVariation; result.rgb = _FogColor.rgb * density; result.a = fogMask * _FogIntensity * clipAlpha; } #ifdef UNITY_UI_ALPHACLIP clip(result.a - 0.001); #endif return result; } ENDCG } } }