// Eldenwood sky dome: three-stop vertical gradient with sun disc and // a mist band near the horizon. Drawn as a fullscreen triangle; the // view ray is reconstructed from the inverse view-projection matrix. struct SkyUniforms { inv_view_proj: mat4x4, sun_dir: vec3, // normalized, world space time_of_day: f32, // 0.0 = midnight, 0.5 = noon zenith_color: vec3, mist_amount: f32, // 0..1, from region weather horizon_color: vec3, _pad: f32, }; @group(0) @binding(0) var sky: SkyUniforms; struct VsOut { @builtin(position) clip_pos: vec4, @location(0) ndc: vec2, }; @vertex fn vs_main(@builtin(vertex_index) idx: u32) -> VsOut { // Fullscreen triangle without a vertex buffer. var pos = array, 3>( vec2(-1.0, -1.0), vec2(3.0, -1.0), vec2(-1.0, 3.0)); var out: VsOut; out.clip_pos = vec4(pos[idx], 1.0, 1.0); // z = far plane out.ndc = pos[idx]; return out; } @fragment fn fs_main(in: VsOut) -> @location(0) vec4 { // Reconstruct the world-space view ray for this pixel. let far = sky.inv_view_proj * vec4(in.ndc, 1.0, 1.0); let ray = normalize(far.xyz / far.w); // Three-stop gradient keyed on ray elevation. let elev = clamp(ray.y, -0.05, 1.0); let ground = vec3(0.24, 0.26, 0.22); let low = mix(ground, sky.horizon_color, smoothstep(-0.05, 0.02, elev)); let color = mix(low, sky.zenith_color, pow(smoothstep(0.0, 0.9, elev), 0.6)); // Sun disc with a soft halo, faded at night. let sun_amount = pow(max(dot(ray, sky.sun_dir), 0.0), 900.0); let halo = pow(max(dot(ray, sky.sun_dir), 0.0), 12.0) * 0.15; let daylight = smoothstep(0.22, 0.30, sky.time_of_day) * (1.0 - smoothstep(0.70, 0.78, sky.time_of_day)); let sun = (sun_amount + halo) * daylight * vec3(1.0, 0.93, 0.78); // Eldenwood mist: a pale green band hugging the horizon. let mist_band = exp(-abs(elev) * 9.0) * sky.mist_amount; let misted = mix(color, vec3(0.66, 0.77, 0.63), mist_band); return vec4(misted + sun, 1.0); }