Files
Emberwild/Assets/GAME/Shaders/StylizedGrass.shader
T
2026-06-22 16:18:34 +02:00

542 lines
22 KiB
GLSL

Shader "GAME/StylizedGrass"
{
Properties
{
[Header(Surface)][Space]
_MainTex("Albedo", 2D) = "white" {}
[Normal] _NormalMap("Normal Map", 2D) = "bump" {}
_NormalStrength("Normal Strength", Range(0,2)) = 1
_NormalUpBlend("Normal Up Blend (stylized lighting)", Range(0,1)) = 0.6
[Toggle(_MASKMAP)] _UseMaskMap("Use Mask Map (R:Metallic G:AO A:Smooth)", Float) = 0
_MaskMap("Mask Map", 2D) = "white" {}
_Smoothness("Smoothness", Range(0,1)) = 0.2
_Metallic("Metallic", Range(0,1)) = 0
_Occlusion("Occlusion", Range(0,1)) = 1
_Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
[Header(Stylized Lighting)][Space]
_ShadowLift("Shadow Lift (lighten grass shadows)", Range(0,0.6)) = 0.25
[HDR] _TranslucencyColor("Translucency Color", Color) = (0.30,0.60,0.15,1)
_TranslucencyStrength("Translucency Strength", Range(0,3)) = 1
_TranslucencyPower("Translucency Power", Range(1,16)) = 4
[Header(Gradient common settings)][Space]
_HeightBlend("Vertical Gradient Height", Float) = 1
_SeasonVariation("Per-Blade Variation Scale", Float) = 0.15
_TextureColorInfluence("Texture Color Influence (0 = season drives color)", Range(0,1)) = 0
[Header(Summer green season 0)][Space]
[HDR] _SummerTopA(" Top A", Color) = (0.35,0.70,0.25,1)
[HDR] _SummerTopB(" Top B", Color) = (0.55,0.80,0.30,1)
[HDR] _SummerBottom(" Bottom", Color) = (0.15,0.35,0.10,1)
[HDR] _SummerGust(" Gust", Color) = (0.85,1.00,0.70,1)
[HDR] _SummerFar(" Far Tint", Color) = (0.70,0.80,0.65,1)
[Header(Autumn orange season 1)][Space]
[HDR] _AutumnTopA(" Top A", Color) = (0.85,0.45,0.12,1)
[HDR] _AutumnTopB(" Top B", Color) = (0.85,0.68,0.20,1)
[HDR] _AutumnBottom(" Bottom", Color) = (0.35,0.22,0.10,1)
[HDR] _AutumnGust(" Gust", Color) = (1.00,0.85,0.55,1)
[HDR] _AutumnFar(" Far Tint", Color) = (0.85,0.70,0.50,1)
[Header(Winter pale season 2)][Space]
[HDR] _WinterTopA(" Top A", Color) = (0.65,0.70,0.62,1)
[HDR] _WinterTopB(" Top B", Color) = (0.80,0.82,0.78,1)
[HDR] _WinterBottom(" Bottom", Color) = (0.40,0.42,0.40,1)
[HDR] _WinterGust(" Gust", Color) = (0.90,0.95,1.00,1)
[HDR] _WinterFar(" Far Tint", Color) = (0.70,0.78,0.90,1)
[Header(Distance Fade uses season Far Tint)][Space]
_NearFarRange("Near/Far Distance (x,y)", Vector) = (10,60,0,0)
_FarBlend("Far Blend", Range(0,1)) = 0
[Header(Winter Frost)][Space]
[HDR] _FrostColor("Frost / Snow Color", Color) = (0.85,0.90,1.0,1)
_FrostAmount("Frost Amount", Range(0,1)) = 0.5
_FrostDesaturation("Winter Desaturation", Range(0,1)) = 0.4
_WindWinterStiffness("Winter Wind Stiffness", Range(0,1)) = 0.4
[Header(Wind Sway)][Space]
_WindDirection("Wind Direction (xy)", Vector) = (1,0,0,0)
_WindStrength("Wind Strength", Float) = 0.2
_WindSpeed("Wind Speed", Float) = 1
_WindFrequency("Wind Frequency", Float) = 0.5
_WindTurbulence("Wind Turbulence", Range(0,1)) = 0.3
_WindHeight("Wind Height Mask", Float) = 1
_WindGustStrength("Gust Strength", Range(0,2)) = 0.5
_WindGustFreq("Gust Frequency", Float) = 0.3
[Header(Wind Gust Overlay color is per season)][Space]
_GustIntensity("Gust Intensity", Range(0,1)) = 0.3
_GustScale("Gust Scale", Float) = 0.1
_GustSpeed("Gust Speed", Float) = 0.3
_GustContrast("Gust Contrast (smoothstep x,y)", Vector) = (0.4,0.7,0,0)
[Header(Rendering)][Space]
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull (Off = double sided)", Float) = 0
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
"Queue" = "Geometry"
"UniversalMaterialType" = "Lit"
}
// =================================================================================
// ForwardLit
// =================================================================================
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }
Cull [_Cull]
ZWrite On
ZTest LEqual
HLSLPROGRAM
#pragma target 3.0
#pragma vertex GrassForwardVertex
#pragma fragment GrassForwardFragment
// Material keywords
#pragma shader_feature_local _NORMALMAP
#pragma shader_feature_local _MASKMAP
// URP lighting keywords (alignés sur Lit.shader d'URP 17)
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
#pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
#pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#pragma multi_compile _ _LIGHT_LAYERS
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP
#pragma multi_compile _ LIGHTMAP_SHADOW_MIXING
#pragma multi_compile _ SHADOWS_SHADOWMASK
#pragma multi_compile _ DIRLIGHTMAP_COMBINED
#pragma multi_compile _ LIGHTMAP_ON
#pragma multi_compile_fog
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "StylizedGrassCommon.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
float2 staticLightmapUV : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 positionOS : TEXCOORD2;
half3 normalWS : TEXCOORD3;
half4 tangentWS : TEXCOORD4; // .w = sign
DECLARE_LIGHTMAP_OR_SH(staticLightmapUV, vertexSH, 5);
float fogFactor : TEXCOORD6;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings GrassForwardVertex(Attributes IN)
{
Varyings OUT = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
positionWS = GrassApplyWind(positionWS, GrassHeightMask(IN.positionOS.xyz));
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
OUT.positionWS = positionWS;
OUT.positionOS = IN.positionOS.xyz;
OUT.positionCS = TransformWorldToHClip(positionWS);
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
OUT.normalWS = normalInput.normalWS;
real sign = IN.tangentOS.w * GetOddNegativeScale();
OUT.tangentWS = half4(normalInput.tangentWS, sign);
OUT.fogFactor = ComputeFogFactor(OUT.positionCS.z);
OUTPUT_LIGHTMAP_UV(IN.staticLightmapUV, unity_LightmapST, OUT.staticLightmapUV);
OUTPUT_SH(OUT.normalWS, OUT.vertexSH);
return OUT;
}
half4 GrassForwardFragment(Varyings IN, FRONT_FACE_TYPE faceSign : FRONT_FACE_SEMANTIC) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN);
half4 baseSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
half alpha = baseSample.a;
clip(alpha - _Cutoff);
// Mask map (optionnel)
half metallic = _Metallic;
half smoothness = _Smoothness;
half occlusion = _Occlusion;
#ifdef _MASKMAP
half4 mask = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, IN.uv);
metallic *= mask.r;
occlusion *= mask.g;
smoothness *= mask.a;
#endif
// Normale
half3 normalWS = normalize(IN.normalWS);
#ifdef _NORMALMAP
half3 normalTS = UnpackNormalScale(
SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv), _NormalStrength);
float sgn = IN.tangentWS.w;
float3 bitangent = sgn * cross(normalWS, IN.tangentWS.xyz);
half3x3 tbn = half3x3(IN.tangentWS.xyz, bitangent, normalWS);
normalWS = normalize(TransformTangentToWorld(normalTS, tbn));
#endif
// Double-sided : flip vers la caméra sur les faces arrière.
normalWS *= IS_FRONT_VFACE(faceSign, 1.0, -1.0);
// Rabat la normale vers le haut -> éclairage uniforme stylisé (évite les brins sombres).
normalWS = normalize(lerp(normalWS, float3(0, 1, 0), _NormalUpBlend));
// Couleur de surface pilotée par la saison (a besoin de la normale finale pour le givre).
half3 albedo = GrassSurfaceColor(IN.uv, IN.positionWS, IN.positionOS, normalWS);
InputData inputData = (InputData)0;
inputData.positionWS = IN.positionWS;
inputData.normalWS = normalWS;
inputData.viewDirectionWS = normalize(GetWorldSpaceViewDir(IN.positionWS));
inputData.shadowCoord = TransformWorldToShadowCoord(IN.positionWS);
inputData.fogCoord = IN.fogFactor;
inputData.bakedGI = SAMPLE_GI(IN.staticLightmapUV, IN.vertexSH, normalWS);
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(IN.positionCS);
inputData.shadowMask = SAMPLE_SHADOWMASK(IN.staticLightmapUV);
SurfaceData surfaceData = (SurfaceData)0;
surfaceData.albedo = albedo;
surfaceData.metallic = metallic;
surfaceData.smoothness = smoothness;
surfaceData.occlusion = occlusion;
surfaceData.normalTS = half3(0, 0, 1);
surfaceData.alpha = 1.0;
half4 color = UniversalFragmentPBR(inputData, surfaceData);
// Translucency (fausse SSS) : la lumière traverse le brin à contre-jour.
Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask);
half trans = pow(saturate(dot(inputData.viewDirectionWS, -mainLight.direction)), _TranslucencyPower);
half lightAtten = mainLight.shadowAttenuation * mainLight.distanceAttenuation;
color.rgb += albedo * _TranslucencyColor.rgb * (_TranslucencyStrength * trans * lightAtten) * mainLight.color;
// Shadow lift : plancher pour que l'herbe à l'ombre garde sa couleur (pas de noir-teal).
color.rgb = max(color.rgb, albedo * _ShadowLift);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
color.a = 1.0;
return color;
}
ENDHLSL
}
// =================================================================================
// ShadowCaster
// =================================================================================
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
ZTest LEqual
Cull [_Cull]
ColorMask 0
HLSLPROGRAM
#pragma target 3.0
#pragma vertex GrassShadowVertex
#pragma fragment GrassShadowFragment
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
#include "StylizedGrassCommon.hlsl"
float3 _LightDirection;
float3 _LightPosition;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 GetShadowClipPos(float3 positionWS, float3 normalWS)
{
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
return positionCS;
}
Varyings GrassShadowVertex(Attributes IN)
{
Varyings OUT = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
positionWS = GrassApplyWind(positionWS, GrassHeightMask(IN.positionOS.xyz));
float3 normalWS = TransformObjectToWorldNormal(IN.normalOS);
OUT.positionCS = GetShadowClipPos(positionWS, normalWS);
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
return OUT;
}
half4 GrassShadowFragment(Varyings IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
half alpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv).a;
clip(alpha - _Cutoff);
return 0;
}
ENDHLSL
}
// =================================================================================
// DepthOnly
// =================================================================================
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
ZWrite On
ColorMask R
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex GrassDepthVertex
#pragma fragment GrassDepthFragment
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "StylizedGrassCommon.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings GrassDepthVertex(Attributes IN)
{
Varyings OUT = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
positionWS = GrassApplyWind(positionWS, GrassHeightMask(IN.positionOS.xyz));
OUT.positionCS = TransformWorldToHClip(positionWS);
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
return OUT;
}
half4 GrassDepthFragment(Varyings IN) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
half alpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv).a;
clip(alpha - _Cutoff);
return 0;
}
ENDHLSL
}
// =================================================================================
// DepthNormals
// =================================================================================
Pass
{
Name "DepthNormals"
Tags { "LightMode" = "DepthNormals" }
ZWrite On
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex GrassDepthNormalsVertex
#pragma fragment GrassDepthNormalsFragment
#pragma shader_feature_local _NORMALMAP
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "StylizedGrassCommon.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
half3 normalWS : TEXCOORD1;
half4 tangentWS : TEXCOORD2;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings GrassDepthNormalsVertex(Attributes IN)
{
Varyings OUT = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(IN);
UNITY_TRANSFER_INSTANCE_ID(IN, OUT);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
positionWS = GrassApplyWind(positionWS, GrassHeightMask(IN.positionOS.xyz));
VertexNormalInputs normalInput = GetVertexNormalInputs(IN.normalOS, IN.tangentOS);
OUT.positionCS = TransformWorldToHClip(positionWS);
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
OUT.normalWS = normalInput.normalWS;
real sign = IN.tangentOS.w * GetOddNegativeScale();
OUT.tangentWS = half4(normalInput.tangentWS, sign);
return OUT;
}
half4 GrassDepthNormalsFragment(Varyings IN, FRONT_FACE_TYPE faceSign : FRONT_FACE_SEMANTIC) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(IN);
half alpha = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv).a;
clip(alpha - _Cutoff);
half3 normalWS = normalize(IN.normalWS);
#ifdef _NORMALMAP
half3 normalTS = UnpackNormalScale(
SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv), _NormalStrength);
float sgn = IN.tangentWS.w;
float3 bitangent = sgn * cross(normalWS, IN.tangentWS.xyz);
half3x3 tbn = half3x3(IN.tangentWS.xyz, bitangent, normalWS);
normalWS = normalize(TransformTangentToWorld(normalTS, tbn));
#endif
normalWS *= IS_FRONT_VFACE(faceSign, 1.0, -1.0);
return half4(NormalizeNormalPerPixel(normalWS), 0.0);
}
ENDHLSL
}
// =================================================================================
// Meta (lightmapping)
// =================================================================================
Pass
{
Name "Meta"
Tags { "LightMode" = "Meta" }
Cull Off
HLSLPROGRAM
#pragma target 3.0
#pragma vertex GrassMetaVertex
#pragma fragment GrassMetaFragment
#pragma shader_feature_local _MASKMAP
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/MetaInput.hlsl"
#include "StylizedGrassCommon.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float2 uv2 : TEXCOORD2;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
};
Varyings GrassMetaVertex(Attributes IN)
{
Varyings OUT = (Varyings)0;
OUT.positionCS = UnityMetaVertexPosition(IN.positionOS.xyz, IN.uv1, IN.uv2);
OUT.uv = TRANSFORM_TEX(IN.uv0, _MainTex);
return OUT;
}
half4 GrassMetaFragment(Varyings IN) : SV_Target
{
half4 baseSample = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
clip(baseSample.a - _Cutoff);
MetaInput metaInput = (MetaInput)0;
// Pour le baking on prend une teinte représentative (sommet d'été).
metaInput.Albedo = baseSample.rgb * _SummerTopA.rgb;
metaInput.Emission = 0;
return UnityMetaFragment(metaInput);
}
ENDHLSL
}
}
FallBack "Universal Render Pipeline/Lit"
}