Skip to main content

GLASS_WGSL

Constant GLASS_WGSL 

Source
pub const GLASS_WGSL: &str = r#"
struct Scene {
    projection: mat4x4f,
    world0: vec4f,
    world1: vec4f,
    world2: vec4f,
    extent: vec4f,
    caustic_rect: vec4f,
    caustic_plane: vec4f,
    caustic_info: vec4f,
    sky: array<vec4f, 9>,
}
struct Object {
    model_view: mat4x4f,
    mvp: mat4x4f,
    normal0: vec4f,
    normal1: vec4f,
    normal2: vec4f,
    base_roughness: vec4f,
    tint_absorption: vec4f,
    optics: vec4f,
    film: vec4f,
    liquid: vec4f,
    tinted_base: vec4f,
    tinted_subsurface: vec4f,
    pick: vec4u,
    clip0: vec4f,
    clip1: vec4f,
}
@group(0) @binding(0) var<uniform> scene: Scene;
@group(0) @binding(1) var<storage, read> objects: array<Object>;
@group(0) @binding(2) var fluid_tex: texture_2d<f32>;
@group(0) @binding(3) var ripple_tex: texture_2d<f32>;
@group(1) @binding(0) var front_id_tex: texture_2d<u32>;
@group(2) @binding(0) var front_tex: texture_2d<f32>;
@group(2) @binding(1) var back_tex: texture_2d<f32>;
@group(2) @binding(2) var front_depth_tex: texture_depth_2d;
@group(2) @binding(3) var opaque_tex: texture_2d<f32>;
@group(2) @binding(4) var reflection_tex: texture_cube<f32>;
@group(2) @binding(5) var caustic_tex: texture_2d<f32>;
@group(2) @binding(6) var opaque_depth_tex: texture_depth_2d;
@group(2) @binding(7) var linear_sampler: sampler;
@group(2) @binding(8) var front_look_tex: texture_2d<f32>;

struct SurfaceVertex {
    @location(0) position: vec4f,
    @location(1) normal: vec4f,
    @location(2) uv: vec4f,
    @location(3) look: vec4f,
}
struct SurfaceOut {
    @builtin(position) position: vec4f,
    @location(0) view_normal: vec3f,
    @location(1) view_depth: f32,
    @location(2) @interpolate(flat) object_id: u32,
    @location(3) look: vec4f,
    @location(4) world: vec3f,
}
struct FrontOut {
    @location(0) normal_depth: vec4f,
    @location(1) id: u32,
    @location(2) look: vec4f,
}
struct ComposeOut {
    @location(0) color: vec4f,
    @location(1) reactive: f32,
}
fn height_at(uv: vec2f, object: Object) -> f32 {
    let fluid_size = vec2f(textureDimensions(fluid_tex));
    let ripple_size = vec2f(textureDimensions(ripple_tex));
    let fluid_xy = vec2i(clamp(uv * fluid_size, vec2f(0.0), fluid_size - vec2f(1.0)));
    let ripple_xy = vec2i(clamp(uv * ripple_size, vec2f(0.0), ripple_size - vec2f(1.0)));
    return textureLoad(fluid_tex, fluid_xy, 0).r * object.liquid.y + textureLoad(ripple_tex, ripple_xy, 0).r * object.liquid.z;
}
fn to_world(view_point: vec3f) -> vec3f {
    return mat3x3f(scene.world0.xyz, scene.world1.xyz, scene.world2.xyz) * view_point + vec3f(scene.world0.w, scene.world1.w, scene.world2.w);
}
fn clip_plane(plane: vec4f, world: vec3f) -> bool {
    return any(plane.xyz != vec3f(0.0)) && dot(plane.xyz, world) > plane.w;
}
fn clipped(object: Object, world: vec3f) -> bool {
    return clip_plane(object.clip0, world) || clip_plane(object.clip1, world);
}
fn cap(plane: vec4f, world: vec3f, t: f32) -> f32 {
    if (!clip_plane(plane, world)) { return t; }
    let eye = vec3f(scene.world0.w, scene.world1.w, scene.world2.w);
    let along = dot(plane.xyz, world - eye);
    if (abs(along) < 1e-8) { return 0.0; }
    return min(t, (plane.w - dot(plane.xyz, eye)) / along);
}
@vertex fn surface_vertex(v: SurfaceVertex, @builtin(instance_index) index: u32) -> SurfaceOut {
    let object = objects[index];
    var p = v.position;
    var n = v.normal.xyz;
    if (object.liquid.x > 0.5 && n.z > 0.5) {
        let step = 1.0 / vec2f(textureDimensions(fluid_tex));
        let dx = height_at(v.uv.xy + vec2f(step.x, 0.0), object) - height_at(v.uv.xy - vec2f(step.x, 0.0), object);
        let dy = height_at(v.uv.xy + vec2f(0.0, step.y), object) - height_at(v.uv.xy - vec2f(0.0, step.y), object);
        p.z += height_at(v.uv.xy, object);
        n = normalize(vec3f(-dx / (2.0 * step.x), -dy / (2.0 * step.y), 1.0));
    }
    let view = object.model_view * p;
    var out: SurfaceOut;
    out.position = object.mvp * p;
    out.view_normal = normalize(mat3x3f(object.normal0.xyz, object.normal1.xyz, object.normal2.xyz) * n);
    out.view_depth = -view.z;
    out.object_id = index + 1u;
    out.look = v.look;
    out.world = to_world(view.xyz);
    return out;
}
@fragment fn front_fragment(input: SurfaceOut) -> FrontOut {
    if (input.look.z < 0.0) { discard; }
    if (clipped(objects[input.object_id - 1u], input.world)) { discard; }
    var out: FrontOut;
    out.normal_depth = vec4f(input.view_normal, input.view_depth);
    out.id = input.object_id;
    out.look = vec4f(input.look.xy, select(0.0, 1.0, input.look.w > 0.5), 0.0);
    return out;
}
@fragment fn back_fragment(input: SurfaceOut) -> @location(0) vec4f {
    let pixel = vec2i(input.position.xy);
    if (textureLoad(front_id_tex, pixel, 0).r != input.object_id) { discard; }
    let object = objects[input.object_id - 1u];
    if (!clipped(object, input.world)) {
        return vec4f(input.view_normal, input.view_depth);
    }
    let first = cap(object.clip0, input.world, 1.0);
    let t = cap(object.clip1, input.world, first);
    if (t <= 0.0) { discard; }
    var plane = object.clip1.xyz;
    if (t == first) {
        plane = object.clip0.xyz;
    }
    let normal = normalize(transpose(mat3x3f(scene.world0.xyz, scene.world1.xyz, scene.world2.xyz)) * plane);
    return vec4f(normal, input.view_depth * t);
}
@vertex fn fullscreen_vertex(@builtin(vertex_index) vertex: u32) -> @builtin(position) vec4f {
    let xy = vec2f(f32((vertex << 1u) & 2u), f32(vertex & 2u));
    return vec4f(xy * 2.0 - vec2f(1.0), 0.0, 1.0);
}
fn refract_ray(incident: vec3f, normal: vec3f, eta: f32) -> vec3f {
    let cosine = -dot(incident, normal);
    let k = 1.0 - eta * eta * (1.0 - cosine * cosine);
    if (k < 0.0) { return reflect(incident, normal); }
    return eta * incident + (eta * cosine - sqrt(k)) * normal;
}
fn landing(uv: vec2f, front_depth: f32, back_depth: f32, front_normal: vec3f, back_normal: vec3f, ior: f32, receiver_depth: f32) -> vec4f {
    let ndc = vec2f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0);
    let entry = vec3f(ndc * front_depth / vec2f(scene.projection[0].x, scene.projection[1].y), -front_depth);
    let incident = normalize(entry);
    let inside = refract_ray(incident, front_normal, 1.0 / ior);
    let travel = max(back_depth - front_depth, 0.0) / max(-inside.z, 0.1);
    let exit = entry + inside * travel;
    let outgoing = refract_ray(inside, -back_normal, ior);
    let reach = max(receiver_depth - back_depth, 0.0) / max(-outgoing.z, 0.1);
    let hit = exit + outgoing * reach;
    return vec4f(hit, max(receiver_depth, back_depth));
}
fn sample_uv(uv: vec2f, front_depth: f32, back_depth: f32, front_normal: vec3f, back_normal: vec3f, ior: f32, receiver_depth: f32) -> vec2f {
    let land = landing(uv, front_depth, back_depth, front_normal, back_normal, ior, receiver_depth);
    return vec2f(0.5 + land.x * scene.projection[0].x / (2.0 * land.w), 0.5 - land.y * scene.projection[1].y / (2.0 * land.w));
}
fn caustic_light(view_point: vec3f, strength: f32) -> vec3f {
    if (scene.caustic_info.z < 0.5 || strength == 0.0) {
        return vec3f(0.0);
    }
    let world = to_world(view_point);
    let toward = scene.caustic_plane.xyz;
    let along = (scene.caustic_plane.w - world.y) / max(toward.y, 1e-4);
    let at = (world.xz + toward.xz * along - scene.caustic_rect.xy) / scene.caustic_rect.zw;
    if (any(at < vec2f(0.0)) || any(at > vec2f(1.0))) {
        return vec3f(0.0);
    }
    return textureSampleLevel(caustic_tex, linear_sampler, at, 0.0).rgb * strength;
}
fn sky_irradiance(n: vec3f) -> vec3f {
    let d = normalize(n);
    let basis0 = vec3f(0.282095, 0.488603 * d.y, 0.488603 * d.z);
    let basis1 = vec3f(0.488603 * d.x, 1.092548 * d.x * d.y, 1.092548 * d.y * d.z);
    let basis2 = vec3f(0.315392 * (3.0 * d.z * d.z - 1.0), 1.092548 * d.x * d.z, 0.546274 * (d.x * d.x - d.y * d.y));
    let sh = scene.sky;
    let value = sh[0].xyz * basis0.x + sh[1].xyz * basis0.y + sh[2].xyz * basis0.z
        + sh[3].xyz * basis1.x + sh[4].xyz * basis1.y + sh[5].xyz * basis1.z
        + sh[6].xyz * basis2.x + sh[7].xyz * basis2.y + sh[8].xyz * basis2.z;
    return max(value, vec3f(0.0));
}
@fragment fn compose_fragment(@builtin(position) position: vec4f) -> ComposeOut {
    let pixel = vec2i(position.xy);
    let id = textureLoad(front_id_tex, pixel, 0).r;
    if (id == 0u) { discard; }
    let front_z = textureLoad(front_depth_tex, pixel, 0);
    let opaque_z = textureLoad(opaque_depth_tex, pixel, 0);
    if (opaque_z < front_z) { discard; }
    let front = textureLoad(front_tex, pixel, 0);
    let back = textureLoad(back_tex, pixel, 0);
    if (back.w <= front.w) { discard; }
    let object = objects[id - 1u];
    let look = textureLoad(front_look_tex, pixel, 0);
    var base = object.base_roughness.xyz;
    var scatter_tint = object.tint_absorption.xyz;
    var subsurface = object.optics.w;
    var film_amount = object.film.z;
    if (look.z > 0.5) {
        let t = clamp(look.x, 0.0, 1.0);
        base = mix(base, object.tinted_base.xyz, t);
        scatter_tint = mix(scatter_tint, object.tinted_subsurface.xyz, t);
        film_amount = mix(film_amount, object.tinted_base.w, t);
        subsurface = mix(subsurface, object.tinted_subsurface.w, t) * max(look.y, 0.0);
    }
    let uv = position.xy / scene.extent.xy;
    let ray = normalize(vec3f(vec2f(uv.x * 2.0 - 1.0, 1.0 - uv.y * 2.0) / vec2f(scene.projection[0].x, scene.projection[1].y), -1.0));
    let normal = normalize(front.xyz);
    let inner = refract_ray(ray, normal, 1.0 / max(object.optics.x, 1.01));
    let distance = (back.w - front.w) / max(-inner.z, 0.1);
    var background_depth = back.w + max(back.w - front.w, 0.01) * 2.0;
    if (opaque_z < 0.999) {
        let linear = scene.projection[3].z / (opaque_z + scene.projection[2].z);
        background_depth = max(linear, back.w);
    }
    let max_lod = max(scene.extent.z - 1.0, 0.0);
    let lod = clamp(object.base_roughness.w * object.base_roughness.w * max_lod, 0.0, max_lod);
    let ior = vec3f(object.optics.x - 0.016 * object.optics.y, object.optics.x, object.optics.x + 0.022 * object.optics.y);
    var refracted = vec3f(0.0);
    for (var channel = 0u; channel < 3u; channel++) {
        let sample_at = sample_uv(uv, front.w, back.w, normal, normalize(back.xyz), ior[channel], background_depth);
        let fit = scene.extent.xy / vec2f(textureDimensions(opaque_tex));
        let sample_color = textureSampleLevel(opaque_tex, linear_sampler, clamp(sample_at, vec2f(0.0), vec2f(1.0)) * fit, lod).rgb;
        refracted[channel] = sample_color[channel];
    }
    let absorption = exp(-max(object.tint_absorption.w, 0.0) * distance * (vec3f(1.0) - clamp(scatter_tint, vec3f(0.0), vec3f(1.0))));
    refracted *= absorption * base;
    var scattered = clamp(subsurface * (1.0 - exp(-distance)), 0.0, 1.0);
    if (look.z > 0.5) {
        scattered = 1.0 - exp(-subsurface * distance);
    }
    let world = mat3x3f(scene.world0.xyz, scene.world1.xyz, scene.world2.xyz);
    let lit_scatter = scatter_tint * sky_irradiance(world * normal) / 3.14159265;
    refracted = mix(refracted, lit_scatter, scattered);
    let cosine = clamp(-dot(ray, normal), 0.0, 1.0);
    let f0 = pow((object.optics.x - 1.0) / (object.optics.x + 1.0), 2.0);
    var fresnel = vec3f(f0 + (1.0 - f0) * pow(1.0 - cosine, 5.0));
    if (film_amount > 0.0 && object.film.x > 0.0) {
        fresnel = mix(fresnel, film_dielectric(cosine, object.film.x, object.film.y, object.optics.x), clamp(film_amount, 0.0, 1.0));
    }
    let reflected_world = normalize(world * reflect(ray, normal));
    let reflected = textureSampleLevel(reflection_tex, linear_sampler, reflected_world, lod).rgb;
    let caustic = caustic_light(landing(uv, front.w, back.w, normal, normalize(back.xyz), ior.y, background_depth).xyz, object.film.w);
    let color = mix(reflected, refracted + caustic, (vec3f(1.0) - fresnel) * clamp(object.optics.z, 0.0, 1.0));
    var out: ComposeOut;
    out.color = vec4f(color, 1.0);
    out.reactive = 1.0;
    return out;
}
@fragment fn pick_fragment(@builtin(position) position: vec4f) -> @location(0) u32 {
    let pixel = vec2i(position.xy);
    let id = textureLoad(front_id_tex, pixel, 0).r;
    if (id == 0u) { discard; }
    let front_z = textureLoad(front_depth_tex, pixel, 0);
    let opaque_z = textureLoad(opaque_depth_tex, pixel, 0);
    if (opaque_z < front_z) { discard; }
    let front = textureLoad(front_tex, pixel, 0);
    let back = textureLoad(back_tex, pixel, 0);
    if (back.w <= front.w) { discard; }
    let pick = objects[id - 1u].pick.x;
    if (pick == 0u) { discard; }
    return pick;
}
"#;