Files
2026-09-29 15:35:09 +02:00

168 lines
6.7 KiB
HLSL

#ifndef FOG_INCLUDED
#define FOG_INCLUDED
// Animated fog: the exact noise pattern doesn't matter, use the cheaper hash
#define NOISES_FAST_HASH
#include "Assets/_Content/Shaders/Include/Noises.hlsl"
// ------------------------------------------------------------
// FOG - meme famille stylistique que Water.hlsl :
// - densite "posterizee" en bandes plutot qu'un degrade lisse
// - bord de silhouette irregulier (jitter), comme dessine a la main
// Les couleurs exposees (ColorA/ColorB/DepthAtten) ne sont pas touchees,
// seule la facon dont la densite/alpha est construite change.
// ------------------------------------------------------------
// ============================================================
// TUNING
// ============================================================
#define FOG_POSTERIZE_STEPS 5.0 // nombre de "bandes" de densite (moins = plus graphique)
#define FOG_COLOR_BAND_STEPS 4.0 // nombre de poches de couleur distinctes entre ColorA/ColorB
#define FOG_EDGE_JITTER 0.16 // irregularite du bord de la silhouette
#define FOG_LINE_WIDTH 1.2 // epaisseur des lignes (meme logique que l'horizon de l'eau)
#define FOG_LINE_OPACITY 0.3 // intensite des lignes
// Always-on fine grain, added on top of the banded color output to
// break up the flat look of each posterized band/pocket. Additive
// (not multiplicative like a simple *grain), so it stays visible in
// dark bands instead of fading out near black.
#define FOG_FINE_GRAIN_SCALE_A 6.0
#define FOG_FINE_GRAIN_SCALE_B 23.0
#define FOG_FINE_GRAIN_WEIGHT_A 0.6
#define FOG_FINE_GRAIN_WEIGHT_B 0.4
#define FOG_FINE_GRAIN_STRENGTH 0.03
#define FOG_FINE_GRAIN_SCROLL 0.02 // world-space drift speed, keeps the grain from looking screen-locked
// ============================================================
// UTILS
// ============================================================
float FogPosterize(float v, float steps)
{
return floor(saturate(v) * steps) / steps;
}
// Isoligne fine sur un champ continu (meme technique que les lignes
// d'horizon de l'eau) : une ligne independante a une valeur donnee,
// PAS une bordure de region remplie -> lecture "courant qui coule"
// plutot que "carte/cellules".
float FogThinContour(float field, float level, float pixelWidth)
{
float fw = fwidth(field) * pixelWidth;
fw = max(fw, 1e-5);
return 1.0 - smoothstep(0.0, fw, abs(field - level));
}
// Cheap value noise: 4 hash taps + bilinear interpolation, no FBM
// octaves needed. Much cheaper than another FBMNoise3D call and
// plenty for fine dither-grain -- this is meant to break flatness,
// not add structure.
float FogHash(float n)
{
return frac(sin(n) * 43758.5453123);
}
float FogCheapValueNoise(float2 uv)
{
float2 i = floor(uv);
float2 f = frac(uv);
float a = FogHash(i.x + i.y * 57.0);
float b = FogHash(i.x + 1.0 + i.y * 57.0);
float c = FogHash(i.x + (i.y + 1.0) * 57.0);
float d = FogHash(i.x + 1.0 + (i.y + 1.0) * 57.0);
f = f * f * (3.0 - 2.0 * f); // smoothstep interpolation
return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y);
}
void Fog_float
(
float4 ColorA,
float4 ColorB,
float DepthAtten,
float3 PositionWS,
float3 PositionOS,
float3 PivotWS,
float3 Scale,
out float3 Output,
out float Alpha
)
{
// Test
//float test = ValueNoise3D(PositionWS, 40);
//float test = PerlinNoise3D(PositionWS, 40);
//float test = PerlinNoise3DLOD(PositionWS, 100, 3.5);
//float test = SimplexNoise3D(PositionWS, 40);
//float test = WorleyNoise3D(PositionWS, 40, 1);
//float id;
//float test = VoronoiNoise3D(PositionWS, 10, 1, .05, id);
//float test = FBMNoise3D(PositionWS, 10, 2, 2, .5);
//float test = RidgedNoise3D(PositionWS, 10, 2, 2, .5);
float3 camPos = _WorldSpaceCameraPos.xyz;
float depth = distance(camPos, PositionWS);
float depthAtten = lerp(1, smoothstep(5, 30, depth), DepthAtten);
float t = _Time.y * .7;
float s = lerp(Scale.x, 1, .3);
float3 p = PositionWS;// - PivotWS;
p += float3(t, t * .2, t);
float noise = RidgedNoise3D(p - PivotWS * .9, .1, 5, 1.6, 1);
p += noise * 3;
float fog = FBMNoise3D((p - PivotWS * .5), 10 / s, 3, 1.5, .5);
// --- densite "posterizee" en bandes, comme l'eau : casse le
// degrade lisse en paliers, plus graphique/grave ---
float fogBanded = FogPosterize(fog, FOG_POSTERIZE_STEPS);
// --- blend ColorA/ColorB en poches distinctes plutot qu'un
// degrade continu -> evite l'effet "voile uniforme" ---
float colorFactor = saturate(RidgedNoise3D((p - PivotWS * .9) * float3(1, 1.6, 1), 3 / s, 3, 1.6, 1));
float colorFactorBanded = FogPosterize(colorFactor, FOG_COLOR_BAND_STEPS);
float4 col = lerp(ColorA, ColorB, colorFactorBanded);
// --- bord de silhouette irregulier : on jitter le seuil utilise
// par le mask, pour que le contour du brouillard ne soit pas une
// courbe parfaitement lisse mais quelque chose de dessine a la main ---
float edgeJitter = (FBMNoise3D(PositionWS * 0.5 + float3(t * 0.4, t * 0.15, t * 0.3) + float3(820.0, 137.0, 55.0), 1.0, 3, 2.0, 0.5) - 0.5) * FOG_EDGE_JITTER;
float mask = smoothstep(0, lerp(.3, .1, depthAtten), PositionOS.y + edgeJitter) * pow(smoothstep(1, .5, PositionOS.y - .15 + noise * .3 + edgeJitter * 0.5), 2);
// --- lignes fines flottantes, comme l'horizon de l'eau : plusieurs
// isolignes independantes sur le champ continu (pas des bordures
// de region), elles bougent naturellement car le champ derive
// deja dans le temps (via "t" ajoute a "p" plus haut) ---
float fogLines = 0.0;
fogLines += FogThinContour(colorFactor, 0.30, FOG_LINE_WIDTH);
fogLines += FogThinContour(colorFactor, 0.50, FOG_LINE_WIDTH);
fogLines += FogThinContour(colorFactor, 0.70, FOG_LINE_WIDTH);
fogLines = saturate(fogLines) * mask;
// Output
Output = lerp(col.rgb, float3(0.0, 0.0, 0.0), fogLines * FOG_LINE_OPACITY);
// Always-on fine grain, applied last so it sits on top of the
// banded color/lines instead of getting smoothed away by them.
// Two octaves at different scales avoid an obvious repeating
// pattern; sampled in world XZ + a slow time offset so it drifts
// instead of looking baked onto the mesh.
float2 grainUV = PositionWS.xz + t * FOG_FINE_GRAIN_SCROLL;
float fineGrain = FogCheapValueNoise(grainUV * FOG_FINE_GRAIN_SCALE_A) * FOG_FINE_GRAIN_WEIGHT_A
+ FogCheapValueNoise(grainUV * FOG_FINE_GRAIN_SCALE_B) * FOG_FINE_GRAIN_WEIGHT_B;
Output += (fineGrain - 0.5) * FOG_FINE_GRAIN_STRENGTH;
Output = saturate(Output);
float baseAlpha = lerp(fogBanded, smoothstep(1, .5, PositionOS.y), depthAtten * mask) * lerp(.2, 1, smoothstep(0, 15, depth)) * mask * col.a;
Alpha = saturate(baseAlpha);
}
#endif