#if _SNOW TEXTURE2D(_SnowDiff); TEXTURE2D(_SnowNormal); #endif #if _SNOWNORMALNOISE TEXTURE2D(_SnowNormalNoise); #endif #if _SNOWFOOTSTEPS TEXTURE2D(_SnowTrackDiff); TEXTURE2D(_SnowTrackNSAO); #endif #if _SNOWMASK TEXTURE2D(_SnowMask); #endif #if _SNOWSPARKLE TEXTURE2D(_SnowSparkleNoise); #endif float SnowFade(float worldHeight, float snowMin, float snowMax, half snowDot, half snowDotVertex, half snowLevel, half puddleHeight) { float snowHeightFade = saturate((worldHeight - snowMin) / max(snowMax, 0.001)); half snowAngleFade = max(0, (snowDotVertex - _SnowHeightAngleRange.z) * 6); snowAngleFade = snowAngleFade * (1 - max(0, (snowDotVertex - _SnowHeightAngleRange.w) * 6)); return saturate((snowLevel * snowHeightFade * saturate(snowAngleFade)) - puddleHeight); } float DoSnowDisplace(float splat_height, float2 uv, float3 worldNormalVertex, float3 worldPos, float puddleHeight, Config config, half4 weights) { // could force a branch and avoid texsamples #if _SNOW #if _USEGLOBALSNOWLEVEL float snowLevel = _Global_SnowLevel; #else float snowLevel = _SnowAmount; #endif #if _USEGLOBALSNOWHEIGHT float snowMin = _Global_SnowMinMaxHeight.x; float snowMax = _Global_SnowMinMaxHeight.y; #else float snowMin = _SnowHeightAngleRange.x; float snowMax = _SnowHeightAngleRange.y; #endif float snowAge = _SnowParams.z; #if _PERTEXSNOWSTRENGTH && !_SNOWSIMPLE SAMPLE_PER_TEX(ptSnowStr, 8.5, config, half4(1.0, 0.0, 0.0, 0.0)); snowLevel *= ptSnowStr0.x * weights.x + ptSnowStr1.x * weights.y + ptSnowStr2.x * weights.z + ptSnowStr3.x * weights.w; #endif half2 levelMaxMin = half2(1, 0); #if _SNOWMASK levelMaxMin = SAMPLE_TEXTURE2D_LOD(_SnowMask, shared_linear_clamp_sampler, uv, 0).xy; #endif float3 snowUpVector = _SnowUpVector; float worldHeight = worldPos.y; half snowDot = saturate(dot(worldNormalVertex, snowUpVector)); half snowDotVertex = max(snowLevel/2, snowDot); float snowFade = SnowFade(worldHeight, snowMin, snowMax, snowDotVertex, snowDotVertex, snowLevel, puddleHeight); #if _SNOWMASK snowFade = min(levelMaxMin.x, snowFade); snowFade = max(levelMaxMin.y, snowFade); #endif float height = splat_height * _SnowParams.x; float erosion = height * _SnowParams.y; float snowMask = saturate((snowFade - erosion)); float snowMask2 = saturate(snowMask * 8); snowMask *= snowMask * snowMask * snowMask * snowMask * snowMask2; float snowAmount = snowMask * snowDot; return snowAmount; #endif return 0; } #if _SNOWSPARKLE void DoSnowSparkle(Input i, inout MicroSplatLayer o, float3 viewDir, float3 worldPos, float3 worldNormalVertex, float snowLevel) { #if _DEBUG_SNOWSPARKLE o.Albedo = 0; o.Smoothness = 0; o.Occlusion = 1; o.Emission = 0; #endif // screen space method. Looks nice because it's in SS, but fails because clearly not // combing from a single spot on the terrain. float size = 1 - (_SnowSparkleSize * 0.001); float density = _SnowSparkleDensity; float noiseDensity = _SnowSparkleNoiseDensity; float viewDep = _SnowSparkleViewDependency; float3 wsView = worldPos - _WorldSpaceCameraPos; float3 wsViewDir = normalize(wsView); float z = length(wsView); float e = floor(log2(0.3*z+3.0)/0.3785116); float level_z = 0.1 * pow(1.3, e) - 0.2; float level = 0.12 / level_z; density *= level; noiseDensity *= level; float3 v = wsView / z; float3 view_new = v * level_z; view_new = sign(view_new) * frac(abs(view_new)); float3 pos = density*worldPos + viewDep * normalize(view_new); float3 g_index = floor(pos); float3 pc = g_index / density; float3 noise = _SnowSparkleNoiseAmplitude * SAMPLE_TEXTURE2D_LOD( _SnowSparkleNoise, sampler_Diffuse, noiseDensity * pc.xz + pc.y, 0).rgb; float3 offset = 0.75; float3 px = pos - g_index + 0.5 * frac(noise)-offset; float dotvn = dot(wsViewDir, worldNormalVertex); float3 ma = v - dotvn*worldNormalVertex; float3 px_proj = dot(px, ma) * ma; px += (abs(dotvn)-1.0)*px_proj/dot(ma,ma); float dist2 = dot(px, px); float thresh = 1 - size; float r = dist2 > thresh? 0 : 1-dist2/thresh; r *= snowLevel * _SnowSparkleStrength; float3 c = _SnowSparkleTint * r; o.Albedo += c; o.Emission += c * _SnowSparkleEmission; o.Smoothness += r; #if _DEBUG_SNOWSPARKLE o.Albedo = c; o.Emission = c * _SnowSparkleEmission; o.Smoothness = r; o.Normal = float3(0,0,1); #endif } #endif #if _SNOWRIM void DoSnowRim(inout MicroSplatLayer o, Input i, float snowAmount) { float rim = 1.0 - saturate(dot(normalize(_WorldSpaceCameraPos - i.worldPos), WorldNormalVector(i, o.Normal))); o.Emission += pow(rim, _SnowRimPower) * _SnowRimColor * snowAmount; } #endif #if _SNOWSTOCHASTIC void SampleSnowStochastic(float2 uv, float2 dx, float2 dy, out float4 albedo, out float4 nsao) { float2 uv1, uv2, uv3; half3 w; PrepareStochasticUVs(_SnowStochasticScale, uv, uv1, uv2, uv3, w); half4 S1 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv1, dx, dy); half4 S2 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv2, dx, dy); half4 S3 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv3, dx, dy); COUNTSAMPLE COUNTSAMPLE COUNTSAMPLE half3 cw = BaryWeightBlend(w, S1.a, S2.a, S3.a, _SnowStochasticContrast); half4 N1, N2, N3 = half4(0,0,1,0); MSBRANCHCLUSTER(cw.x); { N1 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv1, dx, dy); COUNTSAMPLE } MSBRANCHCLUSTER(cw.y); { N2 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv2, dx, dy); COUNTSAMPLE } MSBRANCHCLUSTER(cw.z); { N3 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv3, dx, dy); COUNTSAMPLE } albedo = S1 * cw.x + S2 * cw.y + S3 * cw.z; nsao = N1 * cw.x + N2 * cw.y + N3 * cw.z; nsao = nsao.agrb; } #endif float DoSnow(Input i, inout MicroSplatLayer o, float2 uv, float3 worldNormal, float3 worldNormalVertex, float3 worldPos, float puddleHeight, half surfPorosity, float camDist, Config config, half4 weights, inout half3 SSSTint, inout half SSSThickness, float traxBuffer, float3 traxNormal) { #if _SNOW float2 maskUV = uv; #if _SNOWWORLDSPACEUV uv = worldPos.xz; #endif float2 dx = ddx(uv) * _SnowUVScales.xy; float2 dy = ddy(uv) * _SnowUVScales.xy; float3 wdx = ddx(worldPos) * _SnowUVScales.xxy; float3 wdy = ddy(worldPos) * _SnowUVScales.xxy; uv *= _SnowUVScales.xy; float3 wuv = worldPos * _SnowUVScales.xxy; #if _USEGLOBALSNOWLEVEL float snowLevel = _Global_SnowLevel; #else float snowLevel = _SnowAmount; #endif #if _USEGLOBALSNOWHEIGHT float snowMin = _Global_SnowMinMaxHeight.x; float snowMax = _Global_SnowMinMaxHeight.y; #else float snowMin = _SnowHeightAngleRange.x; float snowMax = _SnowHeightAngleRange.y; #endif float snowAge = _SnowParams.z; float snowErosion = _SnowParams.y; float snowHeight = _SnowParams.x; #if _PERTEXSNOWSTRENGTH && !_SIMPLESNOW SAMPLE_PER_TEX(ptSnowStr, 8.5, config, half4(1.0, 0.0, 0.0, 0.0)); snowLevel *= ptSnowStr0.x * weights.x + ptSnowStr1.x * weights.y + ptSnowStr2.x * weights.z + ptSnowStr3.x * weights.w; #endif half2 levelMaxMin = half2(1,0); #if _SNOWMASK #if _MEGASPLAT levelMaxMin = i.fx2.yx; #else levelMaxMin = SAMPLE_TEXTURE2D_GRAD(_SnowMask, shared_linear_clamp_sampler, maskUV, ddx(maskUV), ddy(maskUV)); #endif #endif float3 snowUpVector = _SnowUpVector; float worldHeight = i.worldHeight; half snowDot = max(snowLevel/2, dot(worldNormal, snowUpVector)); half snowDotVertex = snowDot; #if _SNOWSIMPLE half ao = 1; half oheight = 0; half smoothness = 0; #else half ao = o.Occlusion; half oheight = o.Height; half smoothness = o.Smoothness; #endif float snowFade = SnowFade(worldHeight, snowMin, snowMax, snowDot, snowDotVertex, snowLevel, puddleHeight); #if _SNOWMASK snowFade = min(levelMaxMin.x, snowFade); snowFade = max(levelMaxMin.y, snowFade); #endif //MSBRANCHOTHER(snowFade) { #if _SNOWSTOCHASTIC && _SNOWTRIPLANAR float3 pn = pow(abs(worldNormal), 3); pn = pn / (pn.x + pn.y + pn.z); half3 axisSign = sign(worldNormal); float2 uv0 = wuv.zy * axisSign.x; float2 uv1 = wuv.xz * axisSign.y; float2 uv2 = wuv.xy * axisSign.z; half4 snowAlb0; half4 snowAlb1; half4 snowAlb2; half4 snowNsao0; half4 snowNsao1; half4 snowNsao2; SampleSnowStochastic(uv0, wdx.zy, wdy.zy, snowAlb0, snowNsao0); SampleSnowStochastic(uv1, wdx.xz, wdy.xz, snowAlb1, snowNsao1); SampleSnowStochastic(uv2, wdx.xy, wdy.xy, snowAlb2, snowNsao2); half4 snowAlb = snowAlb0 * pn.x + snowAlb1 * pn.y + snowAlb2 * pn.z; half4 snowNsao = snowNsao0 * pn.x + snowNsao1 * pn.y + snowNsao2 * pn.z; #elif _SNOWTRIPLANAR float3 pn = pow(abs(worldNormal), 3); pn = pn / (pn.x + pn.y + pn.z); half3 axisSign = sign(worldNormal); float2 uv0 = wuv.zy * axisSign.x; float2 uv1 = wuv.xz * axisSign.y; float2 uv2 = wuv.xy * axisSign.z; half4 snowAlb0 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv0, wdx.zy, wdy.zy); half4 snowAlb1 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv1, wdx.xz, wdy.xz); half4 snowAlb2 = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv2, wdx.xy, wdy.xy); half4 snowNsao0 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv0, wdx.zy, wdy.zy).agrb; half4 snowNsao1 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv1, wdx.xz, wdy.xz).agrb; half4 snowNsao2 = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv2, wdx.xy, wdy.xy).agrb; half4 snowAlb = snowAlb0 * pn.x + snowAlb1 * pn.y + snowAlb2 * pn.z; half4 snowNsao = snowNsao0 * pn.x + snowNsao1 * pn.y + snowNsao2 * pn.z; COUNTSAMPLE COUNTSAMPLE COUNTSAMPLE COUNTSAMPLE COUNTSAMPLE COUNTSAMPLE #elif _SNOWSTOCHASTIC half4 snowAlb; half4 snowNsao; SampleSnowStochastic(uv, dx, dy, snowAlb, snowNsao); #else half4 snowAlb = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv, dx, dy); half4 snowNsao = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv, dx, dy).agrb; COUNTSAMPLE COUNTSAMPLE #endif #if _SNOWDISTANCERESAMPLE { float fade = saturate ((camDist - _SnowDistanceResampleScaleStrengthFade.z) / _SnowDistanceResampleScaleStrengthFade.w); fade *= _SnowDistanceResampleScaleStrengthFade.y; MSBRANCHOTHER(fade) { float2 snowResampleUV = uv * _SnowDistanceResampleScaleStrengthFade.x; float2 rsdx = dx * _SnowDistanceResampleScaleStrengthFade.x; float2 rsdy = dy * _SnowDistanceResampleScaleStrengthFade.x; half4 resSnowAlb = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, snowResampleUV, rsdx, rsdy); half4 resSnowNsao = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, snowResampleUV, rsdx, rsdy).grab; COUNTSAMPLE COUNTSAMPLE snowAlb.rgb = lerp(snowAlb, resSnowAlb, fade); snowNsao = lerp(snowNsao, resSnowNsao, fade); } } #endif #if _SNOWNORMALNOISE { float2 normalUV = uv * _SnowNormalNoiseScaleStrength.x; half3 noise = UnpackNormal(SAMPLE_TEXTURE2D_GRAD(_SnowNormalNoise, sampler_Diffuse, normalUV, dx * _SnowNormalNoiseScaleStrength.x, dy * _SnowNormalNoiseScaleStrength.x)); COUNTSAMPLE snowNsao.xy = lerp(snowNsao.xy, BlendNormal2(snowNsao.xy, noise.xy), _SnowNormalNoiseScaleStrength.y); } #endif #if _SNOWFOOTSTEPS { traxNormal.xy *= _SnowTraxNormalStrength; float2 fsdx = dx * _SnowTraxUVScales; float2 fsdy = dy * _SnowTraxUVScales; traxBuffer = 1 - ((1 - traxBuffer) * _SnowTraxTextureBlend); half4 traxDiffuse = SAMPLE_TEXTURE2D_GRAD(_SnowTrackDiff, sampler_Diffuse, uv * _SnowTraxUVScales, fsdx, fsdy); half4 traxN = SAMPLE_TEXTURE2D_GRAD(_SnowTrackNSAO, sampler_NormalSAO, uv * _SnowTraxUVScales, fsdx, fsdy).agrb; COUNTSAMPLE COUNTSAMPLE traxDiffuse.rgb *= _TraxSnowTint; snowAlb.rgba = lerp(traxDiffuse, snowAlb.rgba, traxBuffer); snowNsao.rgba = lerp(traxN + half4(traxNormal.xy, 0, 0), snowNsao.rgba, traxBuffer); snowAge = lerp(_TraxSnowAge, snowAge, traxBuffer); snowErosion = lerp(_TraxSnowErosion, snowErosion, traxBuffer); snowHeight = lerp(_TraxSnowHeight, snowHeight, traxBuffer); snowFade = saturate(snowFade - _TraxSnowRemoval * (1-saturate(traxBuffer))); } #endif half3 snowNormal = float3(snowNsao.xy * 2 - 1, 1); half height = saturate(oheight - (1.0 - snowHeight)); half erosion = saturate(ao * snowErosion); erosion *= erosion; half snowMask = saturate(snowFade - erosion - height); snowMask = snowMask * snowMask * snowMask; half snowAmount = snowMask * saturate(snowDot - (height + erosion) * 0.5); // up snowAmount = saturate(snowAmount * 8); float wetnessMask = saturate((_SnowParams.w * (4.0 * snowFade) - (snowNsao.b) * 0.5)); float snowNormalAmount = snowAmount * snowAmount; float porosity = saturate((((1.0 - smoothness) - 0.5)) / max(surfPorosity, 0.001)); float factor = lerp(1, 0.4, porosity); o.Albedo *= lerp(1.0, factor, wetnessMask); o.Normal = lerp(o.Normal, float3(0,0,1), wetnessMask); o.Smoothness = lerp(o.Smoothness, 0.8, wetnessMask); #if _SNOWSSS SSSTint = lerp(SSSTint, _SnowSSSTint.rgb, snowNormalAmount); SSSThickness = lerp(SSSThickness, _SnowSSSTint.a * 2 * snowAlb.a, snowNormalAmount); #endif snowAlb.rgb *= _SnowTint.rgb; o.Albedo = lerp(o.Albedo, snowAlb.rgb, snowAmount); o.Normal = lerp(o.Normal, snowNormal, snowNormalAmount); o.Smoothness = lerp(o.Smoothness, (snowNsao.b) * snowAge, snowAmount); o.Occlusion = lerp(o.Occlusion, snowNsao.w, snowAmount); o.Height = lerp(o.Height, snowAlb.a, snowAmount); o.Metallic = lerp(o.Metallic, 0.01, snowAmount); float crystals = saturate(0.65 - snowNsao.b); o.Smoothness = lerp(o.Smoothness, crystals * snowAge, snowAmount); #if _SNOWSPARKLE DoSnowSparkle(i, o, i.viewDir, worldPos, worldNormalVertex, snowAmount); #endif #if _SNOWRIM DoSnowRim(o, i, snowAmount); #endif return snowAmount; } #endif return 0; } // for object blend shader, must, unfortunately, keep in sync.. float DoSnowSimple(Input i, inout MicroSplatLayer o, float2 uv, float3 worldNormal, float3 worldNormalVertex, float3 worldPos, float puddleHeight, half surfPorosity) { #if _SNOW float2 maskUV = uv; #if _SNOWWORLDSPACEUV uv = worldPos.xz; #endif uv *= _SnowUVScales.xy; float2 dx = ddx(uv); float2 dy = ddy(uv); #if _USEGLOBALSNOWLEVEL float snowLevel = _Global_SnowLevel; #else float snowLevel = _SnowAmount; #endif #if _USEGLOBALSNOWHEIGHT float snowMin = _Global_SnowMinMaxHeight.x; float snowMax = _Global_SnowMinMaxHeight.y; #else float snowMin = _SnowHeightAngleRange.x; float snowMax = _SnowHeightAngleRange.y; #endif half2 levelMaxMin = half2(1,0); #if _SNOWMASK #if _MEGASPLAT levelMaxMin = i.fx2.yx; #else levelMaxMin = SAMPLE_TEXTURE2D_GRAD(_SnowMask, shared_linear_clamp_sampler, maskUV, ddx(maskUV), ddy(maskUV)); #endif #endif float snowAge = _SnowParams.z; float snowErosion = _SnowParams.y; float snowHeight = _SnowParams.x; float3 snowUpVector = _SnowUpVector; float worldHeight = i.worldHeight; #if _PLANETVECTORS snowUpVector = i.worldUpVector; #endif half snowDot = max(snowLevel/2, dot(worldNormal, snowUpVector)); half snowDotVertex = max(snowLevel/2, dot(worldNormalVertex, snowUpVector)); float snowFade = SnowFade(worldHeight, snowMin, snowMax, snowDot, snowDotVertex, snowLevel, puddleHeight); MSBRANCHOTHER(snowFade) { half4 snowAlb = SAMPLE_TEXTURE2D_GRAD(_SnowDiff, sampler_Diffuse, uv, dx, dy); half4 snowNsao = SAMPLE_TEXTURE2D_GRAD(_SnowNormal, sampler_NormalSAO, uv, dx, dy).agrb; COUNTSAMPLE COUNTSAMPLE snowAlb.rgb *= _SnowTint.rgb; #if _SNOWNORMALNOISE { float2 normalUV = uv * _SnowNormalNoiseScaleStrength.x; half3 noise = UnpackNormal(SAMPLE_TEXTURE2D_GRAD(_SnowNormalNoise, sampler_Diffuse, normalUV, dx * _SnowNormalNoiseScaleStrength.x, dy * _SnowNormalNoiseScaleStrength.x)); snowNsao.xy = lerp(snowNsao.xy, BlendNormal2(snowNsao.xy, noise.xy), _SnowNormalNoiseScaleStrength.y); } #endif half3 snowNormal = float3(snowNsao.xy * 2 - 1, 1); half ao = o.Occlusion; half height = saturate(o.Height - (1-snowHeight)); half erosion = saturate(ao * snowErosion); erosion *= erosion; half snowMask = saturate(snowFade - erosion - height); snowMask = snowMask * snowMask * snowMask; half snowAmount = snowMask * saturate(snowDot - (height + erosion) * 0.5); // up snowAmount = saturate(snowAmount * 8); float wetnessMask = saturate((_SnowParams.w * (4.0 * snowFade) - (snowNsao.b) * 0.5)); float snowNormalAmount = snowAmount * snowAmount; float porosity = saturate((((1.0 - o.Smoothness) - 0.5)) / max(surfPorosity, 0.001)); float factor = lerp(1, 0.4, porosity); o.Albedo *= lerp(1.0, factor, wetnessMask); o.Normal = lerp(o.Normal, float3(0,0,1), wetnessMask); o.Smoothness = lerp(o.Smoothness, 0.8, wetnessMask); o.Albedo = lerp(o.Albedo, snowAlb.rgb, snowAmount); o.Normal = lerp(o.Normal, snowNormal, snowNormalAmount); o.Smoothness = lerp(o.Smoothness, (snowNsao.b) * snowAge, snowAmount); o.Occlusion = lerp(o.Occlusion, snowNsao.w, snowAmount); o.Height = lerp(o.Height, snowAlb.a, snowAmount); o.Metallic = lerp(o.Metallic, 0.01, snowAmount); float crystals = saturate(0.65 - snowNsao.b); o.Smoothness = lerp(o.Smoothness, crystals * snowAge, snowAmount); #if _SNOWSPARKLE DoSnowSparkle(i, o, i.viewDir, worldPos, worldNormalVertex, snowAmount); #endif #if _SNOWRIM DoSnowRim(o, i, snowAmount); #endif return snowAmount; } #endif return 0; }