Skip to main content

IMPOSTOR_WGSL

Constant IMPOSTOR_WGSL 

Source
pub const IMPOSTOR_WGSL: &str = r#"
struct Params {
    view_projection: mat4x4f,
    camera_position: vec4f,
    camera_right: vec4f,
    camera_up: vec4f,
    settings: vec4f,
    source: vec4f,
}
struct Instance {
    center_radius: vec4f,
    options: vec4u,
}
struct VertexOut {
    @builtin(position) clip: vec4f,
    @location(0) local: vec2f,
    @location(1) @interpolate(flat) instance_id: u32,
}
struct FragmentOut {
    @location(0) color: vec4f,
    @builtin(frag_depth) depth: f32,
}
struct Shade {
    color: vec4f,
    depth: f32,
}
struct Sample {
    color: vec4f,
    position: vec3f,
    weight: f32,
}
@group(0) @binding(0) var<uniform> params: Params;
@group(0) @binding(1) var<storage, read> instances: array<Instance>;
@group(0) @binding(2) var radiance: texture_2d_array<f32>;
@group(0) @binding(3) var geometry: texture_2d<f32>;
@group(0) @binding(4) var linear_sampler: sampler;
fn oct_decode(p: vec2f) -> vec3f {
    let y = 1.0 - abs(p.x) - abs(p.y);
    if y < 0.0 {
        return normalize(vec3f((1.0 - abs(p.y)) * select(-1.0, 1.0, p.x >= 0.0), y, (1.0 - abs(p.x)) * select(-1.0, 1.0, p.y >= 0.0)));
    }
    return normalize(vec3f(p.x, y, p.y));
}
fn oct_encode(d: vec3f) -> vec2f {
    let p = d.xz / max(abs(d.x) + abs(d.y) + abs(d.z), 1e-20);
    if d.y < 0.0 {
        return vec2f((1.0 - abs(p.y)) * select(-1.0, 1.0, p.x >= 0.0), (1.0 - abs(p.x)) * select(-1.0, 1.0, p.y >= 0.0));
    }
    return p;
}
fn frame_basis(direction: vec3f) -> mat3x3f {
    let forward = -direction;
    var nominal_up = vec3f(0.0, 1.0, 0.0);
    if abs(direction.y) > 0.95 { nominal_up = vec3f(0.0, 0.0, 1.0); }
    let right = normalize(cross(forward, nominal_up));
    let up = normalize(cross(right, forward));
    return mat3x3f(right, up, forward);
}
fn hash(pixel: vec2u, object_id: u32) -> f32 {
    var value = pixel.x * 1664525u + pixel.y * 1013904223u + object_id * 747796405u;
    value = (value ^ (value >> 16u)) * 2246822519u;
    value = (value ^ (value >> 13u)) * 3266489917u;
    return f32(value ^ (value >> 16u)) * (1.0 / 4294967296.0);
}
fn geometry_sample(uv: vec2f) -> vec4f {
    let size = i32(params.settings.x * params.settings.y);
    return textureLoad(geometry, clamp(vec2i(uv * f32(size)), vec2i(0), vec2i(size - 1)), 0);
}
fn frame_sample(cell: vec2i, local: vec2f, inst: Instance, low: i32, high: i32, anchor_mix: f32) -> Sample {
    let n = i32(params.settings.x);
    let bounded = clamp(cell, vec2i(0), vec2i(n - 1));
    let encoded = (vec2f(bounded) + vec2f(0.5)) * (2.0 / f32(n)) - vec2f(1.0);
    var direction = oct_decode(encoded);
    if params.settings.w > 0.5 { direction.y = abs(direction.y); }
    let basis = frame_basis(direction);
    let radius = inst.center_radius.w;
    let depth_scale = radius / params.source.x;
    let eye = inst.center_radius.xyz + direction * radius * 3.0;
    let scale = 1.0 / 3.0;
    var uv = vec2f(local.x * 0.5 + 0.5, 0.5 - local.y * 0.5);
    let cell_size = 1.0 / f32(n);
    var atlas_uv = (vec2f(bounded) + uv) * cell_size;
    var feature = geometry_sample(atlas_uv);
    for (var step = 0; step < 1; step++) {
        if feature.w <= 0.0 || feature.w * depth_scale > radius * 6.0 { break; }
        let ray = normalize(basis[2] + basis[0] * ((uv.x * 2.0 - 1.0) * scale) + basis[1] * ((1.0 - uv.y * 2.0) * scale));
        let point = eye + ray * feature.w * depth_scale;
        let card_point = inst.center_radius.xyz + params.camera_right.xyz * local.x * radius + params.camera_up.xyz * local.y * radius;
        let shift = vec2f(dot(point - card_point, params.camera_right.xyz), -dot(point - card_point, params.camera_up.xyz)) / max(radius, 1e-5);
        uv = clamp(uv - shift * 0.5, vec2f(0.5 / params.settings.y), vec2f(1.0 - 0.5 / params.settings.y));
        atlas_uv = (vec2f(bounded) + uv) * cell_size;
        feature = geometry_sample(atlas_uv);
    }
    let ray = normalize(basis[2] + basis[0] * ((uv.x * 2.0 - 1.0) * scale) + basis[1] * ((1.0 - uv.y * 2.0) * scale));
    let position = eye + ray * feature.w * depth_scale;
    let a = textureSampleLevel(radiance, linear_sampler, atlas_uv, low, 0.0);
    var color = a;
    if high != low { color = mix(a, textureSampleLevel(radiance, linear_sampler, atlas_uv, high, 0.0), anchor_mix); }
    return Sample(color, position, feature.w * depth_scale);
}
@vertex fn vertex(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> VertexOut {
    let corners = array<vec2f, 6>(vec2f(-1.0, -1.0), vec2f(1.0, -1.0), vec2f(-1.0, 1.0), vec2f(-1.0, 1.0), vec2f(1.0, -1.0), vec2f(1.0, 1.0));
    let local = corners[vertex_id];
    let inst = instances[instance_id];
    let world = inst.center_radius.xyz + (params.camera_right.xyz * local.x + params.camera_up.xyz * local.y) * inst.center_radius.w;
    var output: VertexOut;
    output.clip = params.view_projection * vec4f(world, 1.0);
    output.local = local;
    output.instance_id = instance_id;
    return output;
}
fn shade(local: vec2f, instance_id: u32, pixel: vec4f) -> Shade {
    let inst = instances[instance_id];
    var toward = params.camera_position.xyz - inst.center_radius.xyz;
    if params.source.y > 0.5 { toward = params.camera_position.xyz; }
    toward = normalize(toward);
    var uv = oct_encode(toward);
    if params.settings.w > 0.5 { uv = oct_encode(vec3f(toward.x, abs(toward.y), toward.z)); }
    let grid = (uv * 0.5 + vec2f(0.5)) * params.settings.x - vec2f(0.5);
    let base = vec2i(floor(grid));
    let t = fract(grid);
    var cells: array<vec2i, 3>;
    var weights: vec3f;
    if t.x + t.y <= 1.0 {
        cells = array<vec2i, 3>(base, base + vec2i(1, 0), base + vec2i(0, 1));
        weights = vec3f(1.0 - t.x - t.y, t.x, t.y);
    } else {
        cells = array<vec2i, 3>(base + vec2i(1, 1), base + vec2i(0, 1), base + vec2i(1, 0));
        weights = vec3f(t.x + t.y - 1.0, 1.0 - t.x, 1.0 - t.y);
    }
    let low = i32(params.settings.z);
    let high = min(low + 1, i32(textureNumLayers(radiance)) - 1);
    let anchor_mix = fract(params.settings.z);
    var color = vec4f(0.0);
    var position = vec3f(0.0);
    var weight = 0.0;
    for (var i = 0; i < 3; i++) {
        let sample = frame_sample(cells[i], local, inst, low, high, anchor_mix);
        let amount = weights[i] * max(sample.color.a, 0.0);
        color += sample.color * weights[i];
        if sample.weight > 0.0 && sample.weight < inst.center_radius.w * 6.0 {
            position += sample.position * amount;
            weight += amount;
        }
    }
    if color.a <= 0.5 || hash(vec2u(pixel.xy), inst.options.x) < bitcast<f32>(inst.options.y) || weight <= 1e-5 { discard; }
    let clip = params.view_projection * vec4f(position / weight, 1.0);
    var output: Shade;
    output.color = vec4f(color.rgb / max(color.a, 1e-5), 1.0);
    output.depth = clamp(clip.z / clip.w, 0.0, 1.0);
    return output;
}
@fragment fn fragment(input: VertexOut) -> FragmentOut {
    let value = shade(input.local, input.instance_id, input.clip);
    return FragmentOut(value.color, value.depth);
}
@fragment fn shadow_fragment(input: VertexOut) -> @builtin(frag_depth) f32 {
    return shade(input.local, input.instance_id, input.clip).depth;
}
"#;