sky.wgsl

webgpu-based path tracer

src/shaders/sky.wgsl

2.46 KB
const sun_angular_size = 1.0 * (PI / 180.0);

fn dir_in_cone(u: vec2f) -> vec3<f32> {
    let sun_cos_theta_max = cos(0.255f * PI / 180f);
    let cos_theta = 1f - u.x * (1f - sun_cos_theta_max);
    let sin_theta = sqrt(1f - cos_theta * cos_theta);
    let phi = 2f * PI * u.y;
    let x = cos(phi) * sin_theta;
    let y = sin(phi) * sin_theta;
    let z = cos_theta;
    return vec3(x, y, z);
}

fn sample_sun_cone_dir(u: vec2f) -> vec3<f32> {
    let v = dir_in_cone(u);
    let onb = pixar_onb(normalize(uniforms.sun_direction));
    return normalize(onb * v);
}

// https://www.jcgt.org/published/0006/01/01/paper-lowres.pdf
fn pixar_onb(n: vec3f) -> mat3x3<f32> {
    let s = select(- 1f, 1f, n.z >= 0f);
    let a = - 1f / (s + n.z);
    let b = n.x * n.y * a;
    let u = vec3(1f + s * n.x * n.x * a, s * b, - s * n.x);
    let v = vec3(b, s + n.y * n.y * a, - n.y);
    return mat3x3(u, v, n);
}

// simplified approximation of preetham
fn sky_glow(dir: vec3f, sun_dir: vec3f) -> vec3f {
    let view_dir = normalize(dir);
    let sun_dir_n = normalize(sun_dir);
    let cos_theta = dot(view_dir, sun_dir_n);
    if sun_dir_n.z <= 0.0 {
        return vec3f(0.0);
    }
    // sun altitude still helps modulate overall warmth
    let sun_altitude = clamp(sun_dir_n.z, 0.0, 1.0);
    // more saturated warm tone for horizon, and deeper blue for zenith
    let horizon_color = vec3f(1.1, 0.4, 0.2);
    // rich orange
    let zenith_color = vec3f(0.05, 0.2, 0.6);
    // deep blue
    // exaggerated curve to preserve saturation
    let sky_color = mix(horizon_color, zenith_color, pow(sun_altitude, 0.1));
    // rayleigh-like gradient with vertical bias
    let rayleigh = sky_color * (0.6 + 0.4 * cos_theta * cos_theta);
    // warm sun glow with stronger color (no gray falloff)
    let mie = vec3f(1.3, 0.6, 0.3) * pow(max(cos_theta, 0.0), 12.0) * 0.6;
    return clamp(rayleigh + mie, vec3f(0.0), vec3f(100.0));
}

fn sun_glow(dir: vec3f, sun_dir: vec3f) -> vec3f {
    let view_dir = normalize(dir);
    let sun_n = normalize(sun_dir);
    let cos_theta = dot(view_dir, sun_n);
    // angular radius (half the angular size)
    let angular_radius = 0.5 * uniforms.sun_angular_size;
    let inner = cos(angular_radius * 0.9);
    let outer = cos(angular_radius * 1.1);
    let sun_disk = smoothstep(outer, inner, max(cos_theta, 0.0));
    // compute sun altitude (z-up): 0 = horizon, 1 = overhead
    let sun_altitude = clamp(sun_n.z, 0.0, 1.0);
    return uniforms.sun_radiance * /*tint*/
    sun_disk;
}