#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