297 lines
12 KiB
HLSL
297 lines
12 KiB
HLSL
#ifndef FOG_INCLUDED
|
|
#define FOG_INCLUDED
|
|
|
|
#include "Assets/_Content/Shaders/Include/Noises.hlsl"
|
|
|
|
void Fog_float
|
|
(
|
|
float4 ColorA,
|
|
float4 ColorB,
|
|
float4 ColorC,
|
|
float4 ColorD,
|
|
float DepthAtten,
|
|
float3 PositionWS,
|
|
float3 PositionOS,
|
|
float3 PivotWS,
|
|
float3 Scale,
|
|
float SpikeAmount,
|
|
float SpikeScale,
|
|
float SpikeTilt,
|
|
float SpikeSpeed,
|
|
float PaintSteps,
|
|
float GrainAmount,
|
|
float FogStartTime,
|
|
out float3 Output,
|
|
out float Alpha
|
|
)
|
|
{
|
|
float3 camPos = _WorldSpaceCameraPos.xyz;
|
|
float depth = distance(camPos, PositionWS);
|
|
float depthAtten = lerp(1, smoothstep(5, 30, depth), DepthAtten);
|
|
|
|
|
|
|
|
float t = (_Time.y - FogStartTime) * .7;
|
|
float s = lerp(Scale.x, 1, .3);
|
|
|
|
// Base sampling position, scrolling over time
|
|
float3 p = PositionWS;
|
|
p += float3(t, t * .2, t);
|
|
|
|
// Domain warp for organic fog shapes
|
|
float noise = RidgedNoise3D(p - PivotWS * .9, .1, 5, 1.6, 1);
|
|
p += noise * 3;
|
|
|
|
// Base fog density
|
|
float fog = saturate(FBMNoise3D((p - PivotWS * .5), 10 / s, 3, 1.5, .5));
|
|
|
|
// --- Painterly grain ---
|
|
float grain = FBMNoise3D(PositionWS * 25, 4, 2, 2, .5);
|
|
grain = grain * 2 - 1;
|
|
|
|
// --- Color bands ---
|
|
// Transitions narrowed around the same centers (.2 / .5 / .8) as before: each
|
|
// quantized PaintSteps level now resolves closer to a pure A/B/C/D color instead
|
|
// of a muddier blend, giving a cleaner, more graphic/poster-like read.
|
|
float colorMask = saturate(RidgedNoise3D((p - PivotWS * .9) * float3(1, 1.6, 1), 3 / s, 3, 1.6, 1));
|
|
float paintMask = saturate(colorMask + grain * 0.03);
|
|
paintMask = floor(paintMask * PaintSteps) / max(PaintSteps - 1, 1);
|
|
|
|
float4 col = lerp(ColorA, ColorB, smoothstep(0.16, 0.24, paintMask));
|
|
col = lerp(col, ColorC, smoothstep(0.46, 0.54, paintMask));
|
|
col = lerp(col, ColorD, smoothstep(0.76, 0.84, paintMask));
|
|
|
|
float luma = dot(col.rgb, float3(0.299, 0.587, 0.114));
|
|
col.rgb = lerp(col.rgb, luma.xxx, 0.2); // slightly more desaturated toward gray -> muted, graphic-novel mood
|
|
col.rgb *= (1 + grain * GrainAmount);
|
|
|
|
// --- Ground glow ---
|
|
float height01 = saturate(PositionOS.y / 0.3);
|
|
float groundMask = pow(1 - height01, 2);
|
|
|
|
// --- Reference vector from pivot ---
|
|
float2 radial = PositionWS.xz - PivotWS.xz;
|
|
|
|
// Smooth left/right factor, zero exactly at the front/back seams of the circle
|
|
float sideSign = clamp(radial.x / (Scale.x * 0.3 + 0.001), -1, 1);
|
|
|
|
// Tangential direction (= "around the circle")
|
|
float2 tangentDir = normalize(float2(-radial.y, radial.x));
|
|
|
|
// --- Spikes: lean + drift tangentially, direction flips per side ---
|
|
float2 spikeXZ = radial + tangentDir * (SpikeTilt * height01 * sideSign);
|
|
|
|
// Tangential animation, same sign as the lean -> each half rotates outward, opposite directions
|
|
spikeXZ += tangentDir * (t * SpikeSpeed * sideSign);
|
|
// Horizontal-only animation, drifting outward (same direction as the tilt)
|
|
spikeXZ.y -= t * SpikeSpeed * sideSign;
|
|
|
|
// Low frequency field defines the spike's overall height/silhouette
|
|
// (low freq -> the lean doesn't break the shape into zigzags)
|
|
float spikeField = saturate(FBMNoise3D(float3(spikeXZ.x, spikeXZ.y, 0), SpikeScale * 0.3, 3, 2, .5));
|
|
spikeField = pow(spikeField, 4) * SpikeAmount;
|
|
spikeField = min(spikeField, 0.7); // never reaches the sky
|
|
|
|
// High frequency detail -> subtle jagged/organic edges only
|
|
float edgeWarp = RidgedNoise3D(float3(spikeXZ * 4, t * 0.1), .1, 3, 2, .5);
|
|
float veins = RidgedNoise3D(float3(spikeXZ * 12, t * 0.05), .1, 4, 2.2, .6);
|
|
spikeField *= lerp(0.75, 1.0, veins);
|
|
|
|
float spikeMask = smoothstep(spikeField, spikeField - 0.04, height01 + edgeWarp * 0.02);
|
|
|
|
// --- Tall organic bars reaching toward the sky ---
|
|
float2 barXZ = radial + float2(SpikeTilt * 0.3 * height01 * sideSign, 0);
|
|
|
|
float phase = FBMNoise3D(float3(radial * 0.2, t * 0.02), 1, 2, 2, .5) * 6.2831;
|
|
float wobble = sin(height01 * 6.2831 * 1.5 + phase) * 2.5 * height01;
|
|
barXZ.x += wobble;
|
|
|
|
float barField = saturate(FBMNoise3D(float3(barXZ * 0.4, t * 0.02), 1, 3, 2, .5));
|
|
barField = saturate(pow(barField, 3) * 1.1);
|
|
|
|
float barMask = smoothstep(barField, barField - 0.2, height01);
|
|
|
|
// --- Combine ---
|
|
float combinedMask = saturate(max(groundMask, max(spikeMask, barMask)));
|
|
|
|
// Spikes read as red glowing veins; bars keep the painted color bands
|
|
col.rgb = lerp(col.rgb, ColorA.rgb, spikeMask);
|
|
|
|
// --- Heavy black ink outline + scattered grime/dirt (Enki Bilal style) ---
|
|
// mask*(1-mask) peaks exactly where a smoothstep is mid-transition, so the line always
|
|
// lands right on the silhouette boundary no matter how small/large spikeField or barField
|
|
// happen to be -- no fragile inset constants, no screen-space derivatives needed.
|
|
// A low power + a gain pass fattens that peak into a bold, mostly-solid core instead of
|
|
// a thin soft gradient line; the existing noise fields break it up so it reads as a rough
|
|
// ink/charcoal stroke rather than a clean vector ring.
|
|
float spikeRim = saturate(pow(4 * spikeMask * (1 - spikeMask), 0.4));
|
|
spikeRim = saturate(spikeRim * 1.6);
|
|
spikeRim *= lerp(0.4, 1.0, veins);
|
|
|
|
float barRim = saturate(pow(4 * barMask * (1 - barMask), 0.4));
|
|
barRim = saturate(barRim * 1.6);
|
|
barRim *= lerp(0.45, 1.0, saturate(grain * 0.5 + 0.5));
|
|
|
|
float inkRim = saturate(spikeRim + barRim);
|
|
col.rgb = lerp(col.rgb, float3(0, 0, 0), inkRim); // genuinely black, not a tinted wash
|
|
|
|
// Grime: independent dark smudges scattered across the whole surface, not tied to any silhouette
|
|
float grimeNoise = saturate(FBMNoise3D(PositionWS * 4 + float3(t * 0.05, 0, 0), 5, 3, 2, .5));
|
|
float grimeMask = smoothstep(0.5, 0.58, grimeNoise) * (1 - smoothstep(0.8, 0.9, grimeNoise));
|
|
col.rgb = lerp(col.rgb, float3(0, 0, 0), grimeMask * 0.75);
|
|
|
|
// --- Final output ---
|
|
Output = saturate(col.rgb);
|
|
Alpha = saturate(fog * combinedMask * lerp(.2, 1, smoothstep(0, 15, depth)) * col.a);
|
|
}
|
|
|
|
#endif
|
|
#ifndef FOG_INCLUDED
|
|
#define FOG_INCLUDED
|
|
|
|
#include "Assets/_Content/Shaders/Include/Noises.hlsl"
|
|
|
|
void Fog_float
|
|
(
|
|
float4 ColorA,
|
|
float4 ColorB,
|
|
float4 ColorC,
|
|
float4 ColorD,
|
|
float DepthAtten,
|
|
float3 PositionWS,
|
|
float3 PositionOS,
|
|
float3 PivotWS,
|
|
float3 Scale,
|
|
float SpikeAmount,
|
|
float SpikeScale,
|
|
float SpikeTilt,
|
|
float SpikeSpeed,
|
|
float PaintSteps,
|
|
float GrainAmount,
|
|
float FogStartTime,
|
|
out float3 Output,
|
|
out float Alpha
|
|
)
|
|
{
|
|
float3 camPos = _WorldSpaceCameraPos.xyz;
|
|
float depth = distance(camPos, PositionWS);
|
|
float depthAtten = lerp(1, smoothstep(5, 30, depth), DepthAtten);
|
|
|
|
|
|
|
|
float t = (_Time.y - FogStartTime) * .7;
|
|
float s = lerp(Scale.x, 1, .3);
|
|
|
|
// Base sampling position, scrolling over time
|
|
float3 p = PositionWS;
|
|
p += float3(t, t * .2, t);
|
|
|
|
// Domain warp for organic fog shapes
|
|
float noise = RidgedNoise3D(p - PivotWS * .9, .1, 5, 1.6, 1);
|
|
p += noise * 3;
|
|
|
|
// Base fog density
|
|
float fog = saturate(FBMNoise3D((p - PivotWS * .5), 10 / s, 3, 1.5, .5));
|
|
|
|
// --- Painterly grain ---
|
|
float grain = FBMNoise3D(PositionWS * 25, 4, 2, 2, .5);
|
|
grain = grain * 2 - 1;
|
|
|
|
// --- Color bands ---
|
|
// Transitions narrowed around the same centers (.2 / .5 / .8) as before: each
|
|
// quantized PaintSteps level now resolves closer to a pure A/B/C/D color instead
|
|
// of a muddier blend, giving a cleaner, more graphic/poster-like read.
|
|
float colorMask = saturate(RidgedNoise3D((p - PivotWS * .9) * float3(1, 1.6, 1), 3 / s, 3, 1.6, 1));
|
|
float paintMask = saturate(colorMask + grain * 0.03);
|
|
paintMask = floor(paintMask * PaintSteps) / max(PaintSteps - 1, 1);
|
|
|
|
float4 col = lerp(ColorA, ColorB, smoothstep(0.16, 0.24, paintMask));
|
|
col = lerp(col, ColorC, smoothstep(0.46, 0.54, paintMask));
|
|
col = lerp(col, ColorD, smoothstep(0.76, 0.84, paintMask));
|
|
|
|
float luma = dot(col.rgb, float3(0.299, 0.587, 0.114));
|
|
col.rgb = lerp(col.rgb, luma.xxx, 0.2); // slightly more desaturated toward gray -> muted, graphic-novel mood
|
|
col.rgb *= (1 + grain * GrainAmount);
|
|
|
|
// --- Ground glow ---
|
|
float height01 = saturate(PositionOS.y / 0.3);
|
|
float groundMask = pow(1 - height01, 2);
|
|
|
|
// --- Reference vector from pivot ---
|
|
float2 radial = PositionWS.xz - PivotWS.xz;
|
|
|
|
// Smooth left/right factor, zero exactly at the front/back seams of the circle
|
|
float sideSign = clamp(radial.x / (Scale.x * 0.3 + 0.001), -1, 1);
|
|
|
|
// Tangential direction (= "around the circle")
|
|
float2 tangentDir = normalize(float2(-radial.y, radial.x));
|
|
|
|
// --- Spikes: lean + drift tangentially, direction flips per side ---
|
|
float2 spikeXZ = radial + tangentDir * (SpikeTilt * height01 * sideSign);
|
|
|
|
// Tangential animation, same sign as the lean -> each half rotates outward, opposite directions
|
|
spikeXZ += tangentDir * (t * SpikeSpeed * sideSign);
|
|
// Horizontal-only animation, drifting outward (same direction as the tilt)
|
|
spikeXZ.y -= t * SpikeSpeed * sideSign;
|
|
|
|
// Low frequency field defines the spike's overall height/silhouette
|
|
// (low freq -> the lean doesn't break the shape into zigzags)
|
|
float spikeField = saturate(FBMNoise3D(float3(spikeXZ.x, spikeXZ.y, 0), SpikeScale * 0.3, 3, 2, .5));
|
|
spikeField = pow(spikeField, 4) * SpikeAmount;
|
|
spikeField = min(spikeField, 0.7); // never reaches the sky
|
|
|
|
// High frequency detail -> subtle jagged/organic edges only
|
|
float edgeWarp = RidgedNoise3D(float3(spikeXZ * 4, t * 0.1), .1, 3, 2, .5);
|
|
float veins = RidgedNoise3D(float3(spikeXZ * 12, t * 0.05), .1, 4, 2.2, .6);
|
|
spikeField *= lerp(0.75, 1.0, veins);
|
|
|
|
float spikeMask = smoothstep(spikeField, spikeField - 0.04, height01 + edgeWarp * 0.02);
|
|
|
|
// --- Tall organic bars reaching toward the sky ---
|
|
float2 barXZ = radial + float2(SpikeTilt * 0.3 * height01 * sideSign, 0);
|
|
|
|
float phase = FBMNoise3D(float3(radial * 0.2, t * 0.02), 1, 2, 2, .5) * 6.2831;
|
|
float wobble = sin(height01 * 6.2831 * 1.5 + phase) * 2.5 * height01;
|
|
barXZ.x += wobble;
|
|
|
|
float barField = saturate(FBMNoise3D(float3(barXZ * 0.4, t * 0.02), 1, 3, 2, .5));
|
|
barField = saturate(pow(barField, 3) * 1.1);
|
|
|
|
float barMask = smoothstep(barField, barField - 0.2, height01);
|
|
|
|
// --- Combine ---
|
|
float combinedMask = saturate(max(groundMask, max(spikeMask, barMask)));
|
|
|
|
// Spikes read as red glowing veins; bars keep the painted color bands
|
|
col.rgb = lerp(col.rgb, ColorA.rgb, spikeMask);
|
|
|
|
// --- Heavy black ink outline + scattered grime/dirt (Enki Bilal style) ---
|
|
// mask*(1-mask) peaks exactly where a smoothstep is mid-transition, so the line always
|
|
// lands right on the silhouette boundary no matter how small/large spikeField or barField
|
|
// happen to be -- no fragile inset constants, no screen-space derivatives needed.
|
|
// A low power + a gain pass fattens that peak into a bold, mostly-solid core instead of
|
|
// a thin soft gradient line; the existing noise fields break it up so it reads as a rough
|
|
// ink/charcoal stroke rather than a clean vector ring.
|
|
float spikeRim = saturate(pow(4 * spikeMask * (1 - spikeMask), 0.4));
|
|
spikeRim = saturate(spikeRim * 1.6);
|
|
spikeRim *= lerp(0.4, 1.0, veins);
|
|
|
|
float barRim = saturate(pow(4 * barMask * (1 - barMask), 0.4));
|
|
barRim = saturate(barRim * 1.6);
|
|
barRim *= lerp(0.45, 1.0, saturate(grain * 0.5 + 0.5));
|
|
|
|
float inkRim = saturate(spikeRim + barRim);
|
|
col.rgb = lerp(col.rgb, float3(0, 0, 0), inkRim); // genuinely black, not a tinted wash
|
|
|
|
// Grime: independent dark smudges scattered across the whole surface, not tied to any silhouette
|
|
float grimeNoise = saturate(FBMNoise3D(PositionWS * 4 + float3(t * 0.05, 0, 0), 5, 3, 2, .5));
|
|
float grimeMask = smoothstep(0.5, 0.58, grimeNoise) * (1 - smoothstep(0.8, 0.9, grimeNoise));
|
|
col.rgb = lerp(col.rgb, float3(0, 0, 0), grimeMask * 0.75);
|
|
|
|
// --- Final output ---
|
|
Output = saturate(col.rgb);
|
|
Alpha = saturate(fog * combinedMask * lerp(.2, 1, smoothstep(0, 15, depth)) * col.a);
|
|
}
|
|
|
|
#endif
|