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" }