diff --git a/crates/moonwell-gfx/src/options.rs b/crates/moonwell-gfx/src/options.rs index 91432e81b..94a31849f 100644 --- a/crates/moonwell-gfx/src/options.rs +++ b/crates/moonwell-gfx/src/options.rs @@ -217,6 +217,22 @@ const ITEMS: &[Item] = &[ "Сила резкости", "Sharpen strength", ), + slider( + "post.shadow_depth", + 0.0, + 1.0, + 0.05, + "Глубина теней", + "Shadow depth", + ), + slider( + "post.night_darkness", + 0.0, + 1.0, + 0.05, + "Тёмные ночи", + "Dark nights", + ), switch("post.grading", "Цветокоррекция", "Colour grading"), slider("post.brightness", -0.5, 0.5, 0.01, "Яркость", "Brightness"), slider("post.contrast", 0.5, 2.0, 0.01, "Контраст", "Contrast"), diff --git a/crates/moonwell-gfx/src/post.rs b/crates/moonwell-gfx/src/post.rs index 5d50a688b..d60bd5b8c 100644 --- a/crates/moonwell-gfx/src/post.rs +++ b/crates/moonwell-gfx/src/post.rs @@ -188,7 +188,7 @@ fn publish( } else { 0.0 }; - let night_share = if shadows.moon { 0.35 } else { 0.0 }; + let night_share = if shadows.moon { 0.15 } else { 0.0 }; // A low sun lights the air most: the glow and the rays are the dawn's and the dusk's, and the // noon keeps a third. let low_sun = 1.0 - 0.65 * smoothstep(0.25, 0.8, sun.y); @@ -203,8 +203,20 @@ fn publish( } else { 0.0 }; + // The dark: how much the dark and middle tones lose, deeper at night; and the night's cool, + // grey share. + let night = 1.0 - day; + let dark = (s.shadow_depth as f32 * 0.25 + s.night_darkness as f32 * 0.6 * night).min(0.7); + let night_tint = s.night_darkness as f32 * night; let post_on = s.enabled - && (s.grading || s.ao || s.sun_shafts || vol > 0.0 || sharpen > 0.0 || gi > 0.0 || fxaa); + && (s.grading + || s.ao + || s.sun_shafts + || vol > 0.0 + || sharpen > 0.0 + || gi > 0.0 + || fxaa + || dark > 0.0); let next = (post_on || halo > 0.0).then(|| PostParams { grade: Vec4::new( s.brightness as f32, @@ -212,7 +224,12 @@ fn publish( s.saturation as f32, s.vibrance as f32, ), - tone: Vec4::new(s.warmth as f32, on(post_on && s.grading), 0.0, 0.0), + tone: Vec4::new( + s.warmth as f32, + on(post_on && s.grading), + if post_on { dark } else { 0.0 }, + if post_on { night_tint } else { 0.0 }, + ), ao: Vec4::new( s.ao_strength as f32, s.ao_radius as f32, diff --git a/crates/moonwell-gfx/src/settings.rs b/crates/moonwell-gfx/src/settings.rs index c20d152c5..a02842e1d 100644 --- a/crates/moonwell-gfx/src/settings.rs +++ b/crates/moonwell-gfx/src/settings.rs @@ -120,6 +120,8 @@ impl GfxSettings { s.post.saturation = 1.15; s.post.vibrance = 0.2; s.post.secondary_light_strength = 1.2; + s.post.shadow_depth = 0.5; + s.post.night_darkness = 0.75; s.wind.enabled = true; s.water.enabled = true; s.lights.enabled = true; @@ -353,6 +355,11 @@ pub struct PostSettings { pub secondary_light: bool, /// Its strength, 0..2. pub secondary_light_strength: f64, + /// Deeper shadows at any hour, 0..1: the dark and middle tones go down, the bright stay. + pub shadow_depth: f64, + /// Darker nights, 0..1: with the sun down the same curve goes much further, cooler and a + /// little greyer, so a torch, a lamp or a window stands out of the dark. + pub night_darkness: f64, } impl Default for PostSettings { @@ -382,6 +389,8 @@ impl Default for PostSettings { antialias: true, secondary_light: true, secondary_light_strength: 0.8, + shadow_depth: 0.4, + night_darkness: 0.6, } } } @@ -414,6 +423,8 @@ impl PostSettings { antialias: self.antialias, secondary_light: self.secondary_light, secondary_light_strength: self.secondary_light_strength.clamp(0.0, 2.0), + shadow_depth: self.shadow_depth.clamp(0.0, 1.0), + night_darkness: self.night_darkness.clamp(0.0, 1.0), } } } diff --git a/crates/moonwell-gfx/src/shaders/post.wgsl b/crates/moonwell-gfx/src/shaders/post.wgsl index 136802dfd..19c496c29 100644 --- a/crates/moonwell-gfx/src/shaders/post.wgsl +++ b/crates/moonwell-gfx/src/shaders/post.wgsl @@ -10,7 +10,7 @@ struct Post { // brightness, contrast, saturation, vibrance grade: vec4, - // warmth, grade on, unused, unused + // warmth, grade on, what the dark tones lose (0 off), the night's tint tone: vec4, // strength, radius (yd), ao on, unused ao: vec4, @@ -488,6 +488,25 @@ fn fxaa(uv: vec2, centre: vec3) -> vec3 { return fxaa_at(out_uv); } +// The dark: the dark and middle tones lose `tone.z` of their light and the bright (a torch's +// pool, a window, the moon) keep theirs, eased between by the luma, as WoW Forever's dim places +// and nights are; at night (`tone.w`) also cooler and a little greyer, as the eye sees in the dark. +fn darkened(c: vec3) -> vec3 { + let k = post.tone.z; + if (k <= 0.0) { + return c; + } + let l = dot(c, vec3(0.2126, 0.7152, 0.0722)); + var x = c * mix(1.0 - k, 1.0, smoothstep(0.08, 0.75, l)); + let n = post.tone.w; + if (n > 0.0) { + let grey = dot(x, vec3(0.2126, 0.7152, 0.0722)); + x = mix(x, vec3(grey), 0.25 * n); + x = x * mix(vec3(1.0), vec3(0.86, 0.94, 1.12), 0.6 * n); + } + return x; +} + // The grade, in the gamma bytes the world pass writes. fn grade(c: vec3) -> vec3 { var x = c + vec3(post.grade.x); @@ -518,5 +537,6 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { if (post.tone.y > 0.5) { rgb = grade(rgb); } + rgb = darkened(rgb); return vec4(rgb, color.a); }