ToriaAssets/Sources/Shaders/GlobalSnowDistance.shader

184 lines
5.6 KiB
Plaintext
Raw Permalink Normal View History

2026-05-19 12:20:15 +02:00
Shader "GlobalSnow/SnowDistance" {
Properties {
_MainTex ("Base (RGBA)", 2D) = "white" {}
_Color ("Tint", Vector) = (0.8,0.8,1,1)
}
CGINCLUDE
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile __ NORMALS_GBUFFER IGNORE_NORMALS
#pragma multi_compile __ IGNORE_COVERAGE
#pragma target 3.0
#include "UnityCG.cginc"
#define SNOW_NORMAL_THRESHOLD 0.6
uniform sampler2D _MainTex; // frame buffer
uniform float4 _MainTex_TexelSize;
uniform float4 _MainTex_ST;
uniform sampler2D_float _CameraDepthTexture; // depth from view
uniform sampler2D_float _GS_DepthTexture;
#if !IGNORE_NORMALS
#if NORMALS_GBUFFER
uniform sampler2D _CameraGBufferTexture2; // for deferred normals
#else
uniform sampler2D_float _CameraDepthNormalsTexture;
#endif
#endif
uniform float4x4 _ClipToWorld;
uniform float4x4 _CamToWorld;
uniform float4 _GS_SnowCamPos;
uniform float4 _GS_SunDir;
uniform sampler2D _GS_SnowTex;
uniform float4 _GS_SnowData1; // x = relief, y = occlusion, z = glitter, w = brightness
uniform float4 _GS_SnowData2; // x = minimum altitude, y = altitude scattering, z = coverage extension
uniform float4 _GS_SnowData3;
uniform float4 _GS_SnowData5; // x = slope threshold, y = slope sharpness, z = slope noise
uniform float3 _GS_SnowData6; // x = slope threshold, y = slope sharpness, z = slope noise, w = distance slope threshold
uniform float _Distance01;
uniform fixed4 _Color;
uniform float _DistanceSlopeThreshold;
struct appdata {
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv: TEXCOORD0;
float2 depthUV : TEXCOORD1;
float3 cameraToFarPlane : TEXCOORD2;
};
v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);
o.depthUV = o.uv;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0) {
// Depth texture is inverted WRT the main texture
o.depthUV.y = 1 - o.depthUV.y;
}
#endif
// Clip space X and Y coords
float2 clipXY = o.pos.xy / o.pos.w;
// Position of the far plane in clip space
float4 farPlaneClip = float4(clipXY, 1, 1);
// Homogeneous world position on the far plane
farPlaneClip.y *= _ProjectionParams.x;
_ClipToWorld = mul(_ClipToWorld, unity_CameraInvProjection);
float4 farPlaneWorld4 = mul(_ClipToWorld, farPlaneClip);
// World position on the far plane
float3 farPlaneWorld = farPlaneWorld4.xyz / farPlaneWorld4.w;
// Vector from the camera to the far plane
o.cameraToFarPlane = farPlaneWorld - _WorldSpaceCameraPos;
return o;
}
float3 getWorldPos(v2f i, float depth01) {
return i.cameraToFarPlane * depth01 + _WorldSpaceCameraPos;
}
inline fixed3 LambertLight (float3 wsNormal, float3 albedo) {
fixed diff = max (0, dot (wsNormal, -_GS_SunDir.xyz));
return albedo * diff;
}
// get snow coverage
fixed4 frag (v2f i) : SV_Target {
// discard sky
float depth01 = Linear01Depth(UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, i.depthUV)));
if (depth01<_Distance01 || depth01>0.999) return 0;
// pixel world position
float3 worldPos = getWorldPos(i, depth01);
float4 st = float4(worldPos.xz - _GS_SnowCamPos.xz, 0, 0);
st *= _GS_SnowData2.z;
st += 0.5;
float2 zmask = tex2Dlod(_GS_DepthTexture, st).rg;
_GS_SnowData6.x += zmask.g;
// get snow coverage
#if IGNORE_COVERAGE
float snowCover = 1.0;
#else
float y = max(_GS_SnowCamPos.y - worldPos.y, 0.001) / _GS_SnowCamPos.w;
float zd = min(zmask.r + _GS_SnowData3.z, y);
float snowCover = saturate( ((zd / y) - 0.9875) * 110);
#endif
// prevent snow on walls and below minimum altitude
#if !IGNORE_NORMALS
#if NORMALS_GBUFFER
float3 wsNormal = tex2D(_CameraGBufferTexture2, i.uv).xyz * 2.0 - 1.0; // for deferred
#else
float3 wsNormal = DecodeViewNormalStereo(tex2D(_CameraDepthNormalsTexture, i.depthUV));
wsNormal = mul((float3x3)_CamToWorld, wsNormal);
#endif
#endif
fixed3 diff = tex2D(_GS_SnowTex, worldPos.xz * 0.02).rgb;
// prevent snow on walls and below minimum altitude
float altG = tex2D(_GS_SnowTex, worldPos.xz * 0.5).g;
float altNoise = diff.r * altG - 0.9;
#if !IGNORE_NORMALS
#if !SHADER_API_D3D9
float ny = wsNormal.y - _DistanceSlopeThreshold;
float flatSurface = saturate( (ny + altNoise * _GS_SnowData5.z) * _GS_SnowData5.y);
#else
float ny = wsNormal.y - 0.7;
float flatSurface = saturate(ny * 15.0);
#endif
#else
const float flatSurface = 1.0;
#endif
float minAltitude = worldPos.y - _GS_SnowData2.w - altNoise * _GS_SnowData2.y;
minAltitude = saturate(minAltitude / _GS_SnowData6.z);
snowCover = snowCover * minAltitude * flatSurface - _GS_SnowData6.x;
if (snowCover <=0) {
return fixed4(0,0,0,0);
}
snowCover = saturate(snowCover);
snowCover*= saturate((depth01 - _Distance01) / (_Distance01 * 0.111));
// Final color
diff *= _Color.rgb;
#if IGNORE_NORMALS
fixed3 color = lerp(diff , diff * _GS_SnowData1.www, saturate(0.65 + _GS_SunDir.y));
#else
fixed3 color = lerp(diff , LambertLight(wsNormal * _GS_SnowData1.www, diff), saturate(0.65 + _GS_SunDir.y));
#endif
return fixed4(color, snowCover);
}
ENDCG
SubShader {
ZTest Always Cull Off ZWrite Off
Fog { Mode Off }
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}