Round A: sunset family + ACES tonemap (#1-4, #6)
shader.wgsl:
- sky_dome: zenith now tints toward a dusky-purple zenith_twi during
twilight (was previously only the horizon picking up the warm tint).
Top faces at sunset will read warm through the sky_vis pathway
instead of staying cold blue. [#1]
- apply_fog: at twilight the fog tints toward sun_tint by twi×0.45,
so distant terrain reads warm against an orange sky instead of
cold. [#2]
- sky_color stars: gate flipped from (1 - day) — which still showed
stars during dusk while the sky was bright — to a direct
smoothstep on sun.y (-0.22..0.04). Stars now fade in only after
civil twilight. [#3]
- sun disc / halo: sharpness + intensity now altitude-dependent. At
zenith it's a sharp pinpoint (sharpness 800, intensity 1.5); near
the horizon it softens to a big atmospheric bloom (sharpness 160,
intensity 2.2). Halo also widens and brightens at low sun. [#4]
post.wgsl:
- ACES filmic tonemap (Narkowicz approximation) runs as the final
step. Brightens midtones, compresses highlights smoothly into
[0,1] instead of the previous hard clamp. Output stays linear; the
sRGB surface handles display encoding. Foundation for everything
HDR that follows (god rays, bloom). [#6]
This commit is contained in:
parent
511798b6eb
commit
94585b1ab2
2 changed files with 50 additions and 13 deletions
|
|
@ -1,6 +1,10 @@
|
|||
// Step 1 of the post-process rebuild: minimal pass-through. Samples the
|
||||
// offscreen scene_color and writes it straight to the surface. Effects
|
||||
// (FXAA, sun shafts, tonemap) layer on top of this in later steps.
|
||||
// Post pipeline. Currently:
|
||||
// 1. Sample the offscreen scene_color (linear HDR-ish)
|
||||
// 2. ACES tonemap to compress highlights gracefully
|
||||
// 3. Output to the surface (which sRGB-encodes automatically)
|
||||
//
|
||||
// Effects layered on top (god rays, FXAA) will slot in between (1) and (2)
|
||||
// — tonemap stays last so everything benefits from the curve.
|
||||
|
||||
@group(0) @binding(0) var scene_color_tex: texture_2d<f32>;
|
||||
@group(0) @binding(1) var scene_color_sampler: sampler;
|
||||
|
|
@ -25,7 +29,21 @@ fn vs_post(@builtin(vertex_index) idx: u32) -> PostOut {
|
|||
return out;
|
||||
}
|
||||
|
||||
// Narkowicz's ACES filmic approximation. Brightens midtones, compresses
|
||||
// highlights smoothly into [0, 1]. Output is linear; the sRGB surface
|
||||
// encodes for display.
|
||||
fn aces_tonemap(c: vec3<f32>) -> vec3<f32> {
|
||||
let a = 2.51;
|
||||
let b = 0.03;
|
||||
let cc = 2.43;
|
||||
let d = 0.59;
|
||||
let e = 0.14;
|
||||
return clamp((c * (a * c + b)) / (c * (cc * c + d) + e), vec3<f32>(0.0), vec3<f32>(1.0));
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_post(in: PostOut) -> @location(0) vec4<f32> {
|
||||
return textureSample(scene_color_tex, scene_color_sampler, in.uv);
|
||||
let scene = textureSample(scene_color_tex, scene_color_sampler, in.uv);
|
||||
let tonemapped = aces_tonemap(scene.rgb);
|
||||
return vec4<f32>(tonemapped, 1.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,10 +64,15 @@ fn sky_dome(dir: vec3<f32>, sun: vec3<f32>) -> vec3<f32> {
|
|||
let twi = twilight_amount(sun);
|
||||
let zenith_day = vec3<f32>(0.30, 0.55, 0.88);
|
||||
let zenith_night = vec3<f32>(0.02, 0.03, 0.10);
|
||||
// Dusky-purple zenith during twilight — in reality the entire sky
|
||||
// tints warm at sunset, not just the horizon. Without this, top
|
||||
// faces stay cold blue while the horizon visibly burns orange.
|
||||
let zenith_twi = vec3<f32>(0.45, 0.28, 0.40);
|
||||
let horizon_day = vec3<f32>(0.82, 0.92, 0.99);
|
||||
let horizon_twi = vec3<f32>(1.00, 0.55, 0.28);
|
||||
let horizon_night = vec3<f32>(0.03, 0.04, 0.10);
|
||||
let zenith = mix(zenith_night, zenith_day, day);
|
||||
let zenith_base = mix(zenith_night, zenith_day, day);
|
||||
let zenith = mix(zenith_base, zenith_twi, twi * 0.65);
|
||||
let horizon = mix(mix(horizon_night, horizon_day, day), horizon_twi, twi);
|
||||
let up = clamp(dir.y, -1.0, 1.0);
|
||||
let gradient_t = pow(max(up, 0.0), 0.55);
|
||||
|
|
@ -127,11 +132,15 @@ fn sky_color(dir: vec3<f32>) -> vec3<f32> {
|
|||
let below = step(up, 0.0) * 0.2;
|
||||
sky = sky * (1.0 - below);
|
||||
|
||||
// Stars: fade in at night with slight twinkle.
|
||||
if (night > 0.05) {
|
||||
// Stars: only visible once the sun is well below the horizon. The
|
||||
// old `1 - day` gate showed stars during twilight (when day < 1 but
|
||||
// the sky was still bright). The new gate is tied to sun altitude
|
||||
// directly so stars fade in *after* civil twilight, not during it.
|
||||
let star_fade = 1.0 - smoothstep(-0.22, 0.04, sun.y);
|
||||
if (star_fade > 0.05) {
|
||||
let st = star_field(dir);
|
||||
let twinkle = 0.7 + 0.3 * sin(t * 6.0 + dir.x * 100.0 + dir.z * 130.0);
|
||||
sky = sky + vec3<f32>(st * night * twinkle);
|
||||
sky = sky + vec3<f32>(st * star_fade * twinkle);
|
||||
}
|
||||
|
||||
// Cloud layer — fbm scrolled across an imaginary plane high above.
|
||||
|
|
@ -146,11 +155,16 @@ fn sky_color(dir: vec3<f32>) -> vec3<f32> {
|
|||
sky = mix(sky, cloud_col, mask * (0.55 + 0.25 * day));
|
||||
}
|
||||
|
||||
// Sun disc + halo.
|
||||
// Sun disc + halo. The disc softens and spreads as the sun nears
|
||||
// the horizon — atmospheric scattering blooms the apparent disc at
|
||||
// low angles. Sharp pin-point at zenith, big soft circle at dusk.
|
||||
let sun_col = sun_tint(sun);
|
||||
let cos_s = max(dot(dir, sun), 0.0);
|
||||
let disc = pow(cos_s, 800.0) * 1.5 * smoothstep(-0.05, 0.05, sun.y);
|
||||
let halo = pow(cos_s, 5.0) * 0.20 * day;
|
||||
let alt = clamp(sun.y, 0.0, 1.0);
|
||||
let disc_sharpness = mix(160.0, 800.0, alt);
|
||||
let disc_intensity = mix(2.2, 1.5, alt);
|
||||
let disc = pow(cos_s, disc_sharpness) * disc_intensity * smoothstep(-0.05, 0.05, sun.y);
|
||||
let halo = pow(cos_s, mix(3.0, 5.0, alt)) * mix(0.35, 0.20, alt) * day;
|
||||
sky = sky + sun_col * (disc + halo);
|
||||
|
||||
// Moon disc — opposite the sun, faint white, night only.
|
||||
|
|
@ -234,14 +248,19 @@ fn fog_factor(dist: f32) -> f32 {
|
|||
|
||||
/// Blend `lit` toward sky color along the view ray when the fragment
|
||||
/// is far enough to be fogged. Defers the (expensive) full `sky_color`
|
||||
/// call until the factor is actually nonzero.
|
||||
/// call until the factor is actually nonzero. At twilight the fog
|
||||
/// further biases toward warm sun-tint so distant terrain reads
|
||||
/// orange/pink against an orange sky instead of cold against orange.
|
||||
fn apply_fog(lit: vec3<f32>, dist: f32, view_dir: vec3<f32>) -> vec3<f32> {
|
||||
let t = fog_factor(dist);
|
||||
if (t <= 0.001) {
|
||||
return lit;
|
||||
}
|
||||
let sun = sun_direction(scene_time());
|
||||
let twi = twilight_amount(sun);
|
||||
let sky = sky_color(-view_dir);
|
||||
return mix(lit, sky, t);
|
||||
let fog_col = mix(sky, sky * sun_tint(sun), twi * 0.45);
|
||||
return mix(lit, fog_col, t);
|
||||
}
|
||||
|
||||
// ---------------- 5a. Terrain pipeline ----------------
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue