#ifndef STYLIZED_GRASS_COMMON_INCLUDED #define STYLIZED_GRASS_COMMON_INCLUDED // ===================================================================================== // Stylized Grass - shared logic // Inclus par TOUTES les passes (ForwardLit, ShadowCaster, DepthOnly, DepthNormals, Meta) // pour que le balancement du vent soit STRICTEMENT identique partout // (sinon les ombres / le depth ne suivent pas l'herbe qui bouge). // ===================================================================================== #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" // ------------------------------------------------------------------------------------- // Textures // ------------------------------------------------------------------------------------- TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURE2D(_MaskMap); SAMPLER(sampler_MaskMap); // R: Metallic, G: Occlusion, A: Smoothness // ------------------------------------------------------------------------------------- // Material constants (SRP Batcher compatible) // ------------------------------------------------------------------------------------- CBUFFER_START(UnityPerMaterial) float4 _MainTex_ST; float4 _NormalMap_ST; // Couleurs PAR SAISON (3 saisons : Été/vert, Automne, Hiver). // TopA / TopB = sommet du brin (2 teintes -> variation par brin) // Bottom = base du brin // Gust = couleur de la rafale de vent qui passe sur l'herbe // Far = teinte atmosphérique au loin (fondu distance) half4 _SummerTopA; half4 _SummerTopB; half4 _SummerBottom; half4 _SummerGust; half4 _SummerFar; half4 _AutumnTopA; half4 _AutumnTopB; half4 _AutumnBottom; half4 _AutumnGust; half4 _AutumnFar; half4 _WinterTopA; half4 _WinterTopB; half4 _WinterBottom; half4 _WinterGust; half4 _WinterFar; float _SeasonVariation; // échelle du bruit de variation par brin float _HeightBlend; // hauteur (objet) du dégradé bas -> haut float _TextureColorInfluence; // 0 = texture en détail N&B (saison pilote la couleur), 1 = couleur texture // Fondu atmosphérique de distance (la teinte vient de la saison ci-dessus, _xxxFar) float2 _NearFarRange; // x = début, y = fin du fondu float _FarBlend; // 0 = aucun fondu distance, 1 = plein // Givre / hiver half4 _FrostColor; // HDR, givre/neige d'hiver float _FrostAmount; // quantité de givre en hiver (sur faces vers le ciel) float _FrostDesaturation; // désaturation en hiver, 0..1 float _WindWinterStiffness; // multiplie le vent en hiver (1 = inchangé, <1 = brins raides) // Surface PBR float _Smoothness; float _Metallic; float _Occlusion; float _NormalStrength; float _NormalUpBlend; // 0 = normale du mesh, 1 = world-up (éclairage uniforme stylisé) float _Cutoff; // Éclairage stylisé float _ShadowLift; // plancher d'ombre : empêche l'herbe à l'ombre de virer au noir half4 _TranslucencyColor; // teinte de la lumière qui traverse les brins (fausse SSS) float _TranslucencyStrength; // force de la translucidité float _TranslucencyPower; // netteté du halo de contre-jour // Vent - balancement des sommets (procédural) float4 _WindDirection; // .xy = direction du vent en monde (XZ) float _WindStrength; // amplitude du déplacement float _WindSpeed; // vitesse de l'oscillation principale float _WindFrequency; // densité spatiale des vagues float _WindTurbulence; // quantité d'oscillation secondaire (chaos) float _WindHeight; // hauteur objet à laquelle le sommet bouge à 100% float _WindGustStrength; // force de la modulation "rafale" float _WindGustFreq; // fréquence des rafales // Vent - overlay "rafale" stylisé (fragment ; la couleur vient de la saison, _xxxGust) float _GustIntensity; // 0 = invisible float _GustScale; // échelle du bruit de rafale float _GustSpeed; // vitesse de défilement float2 _GustContrast; // smoothstep(x, y, noise) CBUFFER_END // Logique de saison + bruit partagés (global _Season, SeasonBlend, SeasonWinterWeight, bruit). #include "SeasonCore.hlsl" // ------------------------------------------------------------------------------------- // Vent : déplacement procédural en espace monde. // positionWS : position monde non animée // heightMask01 : 0 à la base du brin, 1 au sommet (déjà calculé depuis positionOS.y) // Retourne la position monde animée. // ------------------------------------------------------------------------------------- float3 GrassApplyWind(float3 positionWS, float heightMask01) { float t = _Time.y; float2 windDir = _WindDirection.xy; windDir = (dot(windDir, windDir) > 1e-5) ? normalize(windDir) : float2(1.0, 0.0); // Phase spatiale : les vagues se propagent dans le sens du vent. float phase = dot(positionWS.xz, windDir) * _WindFrequency + t * _WindSpeed; // Oscillation principale + turbulence (harmonique décalée). float sway = sin(phase); sway += sin(phase * 2.37 + 1.7) * 0.5 * _WindTurbulence; // Rafales : enveloppe lente qui module l'amplitude globale. float gust = sin(t * _WindGustFreq + dot(positionWS.xz, windDir) * 0.15) * 0.5 + 0.5; float amplitude = _WindStrength * (1.0 + gust * _WindGustStrength); // Hiver : brins plus raides (vent réduit). amplitude *= lerp(1.0, _WindWinterStiffness, SeasonWinterWeight()); float bend = sway * amplitude * heightMask01; float3 offset = float3(windDir.x, 0.0, windDir.y) * bend; // Conserve approximativement la longueur du brin : on baisse légèrement le sommet. offset.y -= abs(bend) * 0.25 * heightMask01; return positionWS + offset; } // Masque de hauteur normalisé depuis la position objet (base y=0 -> sommet). float GrassHeightMask(float3 positionOS) { return saturate(positionOS.y / max(_WindHeight, 1e-4)); } // ------------------------------------------------------------------------------------- // Couleur de surface stylisée, PILOTÉE PAR LA SAISON. // Chaque saison définit son dégradé (Top A/B + Bottom). La texture sert de détail // (luminance) par défaut -> la couleur vient vraiment de la saison. // positionWS : position monde animée (fondu distance + variation par brin) // positionOS : position objet (dégradé vertical) // normalWS : normale finale (givre d'hiver sur les faces vers le ciel) // ------------------------------------------------------------------------------------- half3 GrassSurfaceColor(float2 uv, float3 positionWS, float3 positionOS, float3 normalWS) { half3 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, uv).rgb; int i0, i1; float f; SeasonBlend(i0, i1, f); half3 topA[3] = { _SummerTopA.rgb, _AutumnTopA.rgb, _WinterTopA.rgb }; half3 topB[3] = { _SummerTopB.rgb, _AutumnTopB.rgb, _WinterTopB.rgb }; half3 bottom[3] = { _SummerBottom.rgb, _AutumnBottom.rgb, _WinterBottom.rgb }; half3 gust[3] = { _SummerGust.rgb, _AutumnGust.rgb, _WinterGust.rgb }; half3 far[3] = { _SummerFar.rgb, _AutumnFar.rgb, _WinterFar.rgb }; // Couleurs de la saison interpolée (transition fluide entre 2 saisons). half3 topAcur = lerp(topA[i0], topA[i1], f); half3 topBcur = lerp(topB[i0], topB[i1], f); half3 bottomCur = lerp(bottom[i0], bottom[i1], f); half3 gustCol = lerp(gust[i0], gust[i1], f); half3 farCol = lerp(far[i0], far[i1], f); // Variation par brin entre Top A et Top B (bruit world-space). float v = SeasonGradientNoise(positionWS.xz * _SeasonVariation); half3 topCol = lerp(topAcur, topBcur, v); // Dégradé vertical bas -> haut. float heightT = saturate(positionOS.y / max(_HeightBlend, 1e-4)); half3 grad = lerp(bottomCur, topCol, heightT); // Texture : détail N&B (préserve la luminosité) -> couleur texture, selon influence. float texLum = dot(tex, half3(0.299, 0.587, 0.114)); half3 texMod = lerp(saturate(texLum * 2.0).xxx, tex, _TextureColorInfluence); half3 col = grad * texMod; // Fondu atmosphérique de distance (teinte saisonnière, neutre si _FarBlend = 0). float dist = distance(positionWS, _WorldSpaceCameraPos); float farT = smoothstep(_NearFarRange.x, _NearFarRange.y, dist) * _FarBlend; col = lerp(col, col * farCol, saturate(farT)); // Overlay rafale colorée saisonnière (le vent qu'on "voit" passer sur l'herbe). float2 gustUV = positionWS.xz * _GustScale + _WindDirection.xy * (_Time.y * _GustSpeed); float gustNoise = SeasonGradientNoise(gustUV); float gustMask = smoothstep(_GustContrast.x, _GustContrast.y, gustNoise) * _GustIntensity; col = lerp(col, gustCol, saturate(gustMask)); // Hiver : désaturation globale + givre/neige sur les faces vers le ciel. float winter = SeasonWinterWeight(); float lum = dot(col, half3(0.299, 0.587, 0.114)); col = lerp(col, lum.xxx, winter * _FrostDesaturation); float frost = winter * _FrostAmount * saturate(normalWS.y); col = lerp(col, _FrostColor.rgb, saturate(frost)); return col; } #endif // STYLIZED_GRASS_COMMON_INCLUDED