Skip to main content

PLATE_WGSL

Constant PLATE_WGSL 

Source
pub const PLATE_WGSL: &str = r#"
struct PlateUniform {
    inverse: mat4x4f,
    still_inverse: mat4x4f,
    view_projection: mat4x4f,
    current: mat4x4f,
    previous: mat4x4f,
    view: mat4x4f,
    shadow: mat4x4f,
    origin: vec4f,
    forward: vec4f,
    right: vec4f,
    up: vec4f,
    size: vec4f,
    blend: vec4f,
    eye: vec4f,
    params: vec4f,
    sun: vec4f,
    extra: vec4f,
    light_a: vec4f,
    light_b: vec4f,
    light_now: vec4f,
    toward_a: vec4f,
    toward_b: vec4f,
}
struct PlateCascades {
    splits: vec4f,
    filter_params: vec4f,
    sun: vec4f,
    texels: vec4f,
    view_proj_0: mat4x4f,
    view_proj_1: mat4x4f,
    view_proj_2: mat4x4f,
}
@group(0) @binding(0) var<uniform> plate: PlateUniform;
@group(0) @binding(1) var plate_color: texture_2d_array<f32>;
@group(0) @binding(2) var plate_sun: texture_2d_array<f32>;
@group(0) @binding(3) var plate_depth_codes: texture_2d<u32>;
@group(0) @binding(4) var plate_depth_floats: texture_2d<f32>;
@group(0) @binding(5) var plate_normal: texture_2d<u32>;
@group(0) @binding(6) var plate_ids: texture_2d<u32>;
@group(0) @binding(7) var plate_sampler: sampler;
@group(0) @binding(8) var plate_shadow: texture_depth_2d;
@group(0) @binding(9) var plate_compare: sampler_comparison;
@group(0) @binding(10) var plate_transfer: texture_2d_array<f32>;
@group(0) @binding(11) var plate_point: sampler;
@group(1) @binding(0) var<uniform> cascades: PlateCascades;
@group(1) @binding(1) var cascade_atlas: texture_depth_2d_array;

struct PlateOut {
    @location(0) color: vec4f,
    @location(1) id: u32,
    @location(2) velocity: vec2f,
    @location(3) normal_roughness: vec4f,
    @location(4) reactive: f32,
    @builtin(frag_depth) depth: f32,
}

fn plate_inverse_at(texel: vec2i) -> f32 {
    let size = vec2i(plate.size.xy);
    let at = clamp(texel, vec2i(0), size - vec2i(1));
    if (plate.forward.w < 0.5) {
        let code = textureLoad(plate_depth_codes, at, 0).r;
        return f32(code) / (65535.0 * plate.origin.w);
    }
    let depth = textureLoad(plate_depth_floats, at, 0).r;
    return select(0.0, 1.0 / depth, depth > 0.0);
}

fn plate_inverse(position: vec2f) -> f32 {
    let q = position - vec2f(0.5);
    let base = vec2i(floor(q));
    let f = q - floor(q);
    var a: f32;
    var b: f32;
    var c: f32;
    var d: f32;
    if (plate.forward.w < 0.5) {
        let codes = vec4f(textureGather(0, plate_depth_codes, plate_point, (vec2f(base) + vec2f(1.0)) / plate.size.xy)) / (65535.0 * plate.origin.w);
        a = codes.w;
        b = codes.z;
        c = codes.x;
        d = codes.y;
    } else {
        a = plate_inverse_at(base);
        b = plate_inverse_at(base + vec2i(1, 0));
        c = plate_inverse_at(base + vec2i(0, 1));
        d = plate_inverse_at(base + vec2i(1, 1));
    }
    let lo = min(min(a, b), min(c, d));
    let hi = max(max(a, b), max(c, d));
    if (lo > 0.0 && hi <= lo * 1.03) {
        return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
    }
    return plate_inverse_at(vec2i(floor(position)));
}

fn plate_texel(world: vec3f) -> vec3f {
    let v = world - plate.origin.xyz;
    let z = dot(v, plate.forward.xyz);
    let r = plate.right.xyz;
    let u = plate.up.xyz;
    let a = dot(v, r) / (dot(r, r) * z);
    let b = dot(v, u) / (dot(u, u) * z);
    let x = a - plate.right.w;
    let y = plate.up.w - b;
    return vec3f((x + 1.0) * 0.5 * plate.size.x, (y + 1.0) * 0.5 * plate.size.y, z);
}

fn plate_along(eye: vec3f, direction: vec3f, depth: f32) -> f32 {
    let offset = dot(eye - plate.origin.xyz, plate.forward.xyz);
    return (depth - offset) / max(dot(direction, plate.forward.xyz), 1e-6);
}

struct PlateHit {
    t: f32,
    texel: vec2f,
    inverse: f32,
}

fn plate_march(eye: vec3f, direction: vec3f, start: f32, steps: u32) -> PlateHit {
    var t = start;
    var hit = PlateHit(t, vec2f(0.0), 0.0);
    for (var i = 0u; i < steps; i++) {
        let at = plate_texel(eye + direction * t);
        let inverse = plate_inverse(at.xy);
        hit.texel = at.xy;
        hit.inverse = inverse;
        if (inverse <= 0.0) {
            return hit;
        }
        let next = plate_along(eye, direction, 1.0 / inverse);
        let settled = abs(next - t) <= 1e-5 * max(t, 1e-3);
        t = next;
        hit.t = t;
        if (settled) {
            break;
        }
    }
    return hit;
}

fn plate_ray(inverse: mat4x4f, ndc: vec2f) -> vec3f {
    let far = inverse * vec4f(ndc, 1.0, 1.0);
    return normalize(far.xyz / far.w - plate.eye.xyz);
}

fn plate_start(eye: vec3f, direction: vec3f) -> f32 {
    let far = plate_texel(eye + direction * 1e4);
    let inverse = plate_inverse(far.xy);
    return plate_along(eye, direction, select(1e4, 1.0 / inverse, inverse > 0.0));
}

fn plate_octahedral(packed: u32) -> vec3f {
    let x = max(f32(bitcast<i32>(packed << 16u) >> 16u) / 32767.0, -1.0);
    let y = max(f32(bitcast<i32>(packed) >> 16u) / 32767.0, -1.0);
    var n = vec3f(x, y, 1.0 - abs(x) - abs(y));
    if (n.z < 0.0) {
        let flip = vec2f(select(-1.0, 1.0, n.x >= 0.0), select(-1.0, 1.0, n.y >= 0.0));
        n = vec3f((1.0 - abs(n.yx)) * flip, n.z);
    }
    return normalize(n);
}

fn plate_shadowed(world: vec3f, normal: vec3f) -> f32 {
    let offset = world + normal * plate.params.z * 1.5 + plate.sun.xyz * plate.params.z;
    let clip = plate.shadow * vec4f(offset, 1.0);
    let uv = vec2f(clip.x, -clip.y) * 0.5 + vec2f(0.5);
    if (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0)) || clip.z >= 1.0) {
        return 1.0;
    }
    let texel = 1.0 / f32(textureDimensions(plate_shadow).x);
    var lit = 0.0;
    for (var y = -1; y <= 1; y++) {
        for (var x = -1; x <= 1; x++) {
            let at = uv + vec2f(f32(x), f32(y)) * texel * 1.5;
            lit += textureSampleCompareLevel(plate_shadow, plate_compare, at, clip.z);
        }
    }
    return lit / 9.0;
}

fn plate_sunlit(world: vec3f, normal: vec3f) -> f32 {
    if (cascades.splits.w <= 0.0) {
        return 1.0;
    }
    let distance = length(world - plate.eye.xyz);
    var layer = 0;
    var matrix = cascades.view_proj_0;
    var texel = cascades.texels.x;
    if (distance > cascades.splits.y) {
        layer = 1;
        matrix = cascades.view_proj_1;
        texel = cascades.texels.y;
    }
    if (distance > cascades.splits.z) {
        layer = 2;
        matrix = cascades.view_proj_2;
        texel = cascades.texels.z;
    }
    let clip = matrix * vec4f(world + normal * texel * 1.5, 1.0);
    let point = clip.xyz / clip.w;
    let uv = point.xy * vec2f(0.5, -0.5) + vec2f(0.5);
    if (any(uv < vec2f(0.0)) || any(uv > vec2f(1.0)) || point.z < 0.0 || point.z > 1.0) {
        return 1.0;
    }
    let size = vec2f(textureDimensions(cascade_atlas));
    let centre = uv * size;
    var lit = 0.0;
    for (var y = -1; y <= 1; y++) {
        for (var x = -1; x <= 1; x++) {
            let at = clamp(vec2i(centre + vec2f(f32(x), f32(y))), vec2i(0), vec2i(size) - vec2i(1));
            lit += select(0.0, 1.0, point.z - cascades.sun.w <= textureLoad(cascade_atlas, at, layer, 0));
        }
    }
    return lit / 9.0;
}

@fragment fn fragment(@builtin(position) position: vec4f) -> PlateOut {
    let ndc = vec2f(position.x * plate.size.z * 2.0 - 1.0, 1.0 - position.y * plate.size.w * 2.0);
    let eye = plate.eye.xyz;
    let steps = u32(plate.params.y);
    let jittered = plate_ray(plate.inverse, ndc);
    let hit = plate_march(eye, jittered, plate_start(eye, jittered), steps);
    if (hit.inverse <= 0.0) {
        discard;
    }
    let still = plate_ray(plate.still_inverse, ndc);
    let seen = plate_march(eye, still, plate_along(eye, still, 1.0 / hit.inverse), 3u);
    let world = eye + still * seen.t;
    let uv = seen.texel / plate.size.xy;
    let a = i32(plate.blend.x);
    let b = i32(plate.blend.y);
    let color_a = textureSampleLevel(plate_color, plate_sampler, uv, a, 0.0).rgb;
    let color_b = textureSampleLevel(plate_color, plate_sampler, uv, b, 0.0).rgb;
    var color = mix(color_a, color_b, plate.blend.z);
    let texel = clamp(vec2i(floor(seen.texel)), vec2i(0), vec2i(plate.size.xy) - vec2i(1));
    var normal = vec3f(0.0, 1.0, 0.0);
    if (plate.extra.x > 0.5) {
        normal = plate_octahedral(textureLoad(plate_normal, texel, 0).r);
    }
    if (plate.blend.w > 0.5) {
        let share_a = textureSampleLevel(plate_sun, plate_sampler, uv, a, 0.0).rgb;
        let share_b = textureSampleLevel(plate_sun, plate_sampler, uv, b, 0.0).rgb;
        let unit_a = select(vec3f(0.0), share_a / plate.light_a.rgb, plate.light_a.rgb > vec3f(0.0));
        let unit_b = select(vec3f(0.0), share_b / plate.light_b.rgb, plate.light_b.rgb > vec3f(0.0));
        var share = mix(unit_a, unit_b, plate.blend.z) * plate.light_now.rgb;
        let rest = max(mix(color_a - share_a, color_b - share_b, plate.blend.z), vec3f(0.0));
        if (plate.sun.w > 0.5) {
            share *= 1.0 - (1.0 - plate_shadowed(world, normal)) * plate.eye.w;
        }
        let weight = (1.0 - abs(1.0 - 2.0 * plate.blend.z)) * plate.extra.w;
        if (weight > 0.0) {
            let open_a = textureSampleLevel(plate_transfer, plate_sampler, uv, a, 0.0).rgb;
            let open_b = textureSampleLevel(plate_transfer, plate_sampler, uv, b, 0.0).rgb;
            let facing_a = max(dot(normal, plate.toward_a.xyz), 0.0);
            let facing_b = max(dot(normal, plate.toward_b.xyz), 0.0);
            let facing = max(dot(normal, plate.sun.xyz), 0.0);
            let per_a = select(vec3f(0.0), open_a / plate.light_a.rgb, plate.light_a.rgb > vec3f(0.0));
            let per_b = select(vec3f(0.0), open_b / plate.light_b.rgb, plate.light_b.rgb > vec3f(0.0));
            let albedo = (per_a * facing_a + per_b * facing_b) / max(facing_a * facing_a + facing_b * facing_b, 1e-4);
            let relit = albedo * facing * plate.light_now.rgb * plate_sunlit(world, normal);
            share = mix(share, relit, weight);
        }
        color = rest + share;
    }
    var out: PlateOut;
    out.color = vec4f(color, 1.0);
    out.id = select(0u, textureLoad(plate_ids, texel, 0).r, plate.extra.y > 0.5);
    let front = eye + jittered * hit.t * (1.0 + plate.params.x);
    let clip = plate.view_projection * vec4f(front, 1.0);
    out.depth = clamp(clip.z / clip.w, 0.0, 1.0);
    let now = plate.current * vec4f(world, 1.0);
    let before = plate.previous * vec4f(world, 1.0);
    let moved = (before.xy / before.w - now.xy / now.w) * vec2f(0.5, -0.5);
    out.velocity = select(moved, vec2f(0.0), abs(moved) < vec2f(1e-8));
    out.normal_roughness = vec4f(normalize((plate.view * vec4f(normal, 0.0)).xyz), 1.0);
    out.reactive = plate.extra.z;
    return out;
}
"#;