187 lines
9.2 KiB
HLSL
187 lines
9.2 KiB
HLSL
#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
|