Skip to main content

PROBE_WGSL

Constant PROBE_WGSL 

Source
pub const PROBE_WGSL: &str = "\nfn ssr_encode_normal(n: vec3f) -> vec2f {\n    let p = n.xy / (abs(n.x) + abs(n.y) + abs(n.z));\n    if (n.z >= 0.0) { return p; }\n    return (vec2f(1.0) - abs(p.yx)) * select(vec2f(-1.0), vec2f(1.0), p >= vec2f(0.0));\n}\nfn ssr_decode_normal(e: vec2f) -> vec3f {\n    var n = vec3f(e, 1.0 - abs(e.x) - abs(e.y));\n    let t = max(-n.z, 0.0);\n    n.x += select(t, -t, n.x >= 0.0);\n    n.y += select(t, -t, n.y >= 0.0);\n    return normalize(n);\n}\nfn ssr_similarity(depth: f32, normal: vec3f, other_depth: f32, other_normal: vec3f, tolerance: f32) -> f32 {\n    if (depth <= 0.0 || other_depth <= 0.0) { return 0.0; }\n    let near = clamp(1.0 - abs(depth - other_depth) / max(tolerance, 0.000001), 0.0, 1.0);\n    let facing = smoothstep(0.6, 0.85, dot(normal, other_normal));\n    return near * facing;\n}\nconst SSR_DEPTH_TOLERANCE: f32 = 0.03;\n\noverride use_local_reflections: bool = true;\nconst LOCAL_DEPTH_LEVELS: f32 = 6.0;\nconst LOCAL_DEPTH_START: f32 = 0.02;\nconst LOCAL_DEPTH_GROWTH: f32 = 2.5;\nconst LOCAL_DEPTH_STEPS: u32 = 9u;\nconst LOCAL_DEPTH_REFINE: u32 = 4u;\nconst LOCAL_DEPTH_TOLERANCE: f32 = 0.02;\nconst LOCAL_DEPTH_THICKNESS: f32 = 0.03;\nstruct ReflectionProbe {\n    center: vec3f,\n    layer: i32,\n    box_min: vec3f,\n    priority: f32,\n    box_max: vec3f,\n    fade: f32,\n}\nfn local_probe_direction(probe: ReflectionProbe, world: vec3f, direction: vec3f) -> vec3f {\n    let safe = select(vec3f(-1.0), vec3f(1.0), direction >= vec3f(0.0)) * max(abs(direction), vec3f(0.00001));\n    let a = (probe.box_min - world) / safe;\n    let b = (probe.box_max - world) / safe;\n    let reach = min(max(a.x, b.x), min(max(a.y, b.y), max(a.z, b.z)));\n    return normalize(world + direction * max(reach, 0.0) - probe.center);\n}\nfn ssr_resolve(colour: texture_2d<f32>, guide: texture_2d<f32>, samp: sampler, uv: vec2f, written: vec2f, depth: f32, normal: vec3f) -> vec4f {\n    let dimensions = vec2i(textureDimensions(colour));\n    if (any(dimensions != vec2i(textureDimensions(guide)))) { return vec4f(0.0); }\n    let size = min(dimensions, vec2i(written));\n    let f = uv * vec2f(size) - vec2f(0.5);\n    let base = vec2i(floor(f));\n    let t = f - floor(f);\n    let tolerance = depth * SSR_DEPTH_TOLERANCE;\n    let first = textureLoad(guide, clamp(base, vec2i(0), size - vec2i(1)), 0);\n    if (first.w >= 0.0 && ssr_similarity(depth, normal, first.x, ssr_decode_normal(first.yz), tolerance) > 0.8) {\n        let inner = any(size < dimensions);\n        let texel = select(uv * vec2f(size), min(uv * vec2f(size), vec2f(size) - vec2f(0.5)), inner);\n        return textureSampleLevel(colour, samp, select(uv, texel / vec2f(dimensions), inner), 0.0);\n    }\n    var sum = vec4f(0.0);\n    var total = 0.0;\n    for (var i = 0; i < 4; i++) {\n        let offset = vec2i(i & 1, i >> 1u);\n        let at = clamp(base + offset, vec2i(0), size - vec2i(1));\n        let bilinear = select(1.0 - t.x, t.x, offset.x == 1) * select(1.0 - t.y, t.y, offset.y == 1);\n        let g = textureLoad(guide, at, 0);\n        let weight = (bilinear + 0.01) * ssr_similarity(depth, normal, g.x, ssr_decode_normal(g.yz), tolerance);\n        sum += textureLoad(colour, at, 0) * weight;\n        total += weight;\n    }\n    if (total < 0.001) { return vec4f(0.0); }\n    return sum / total;\n}\nfn local_probe_weight(probe: ReflectionProbe, world: vec3f) -> vec2f {\n    if (any(world < probe.box_min) || any(world > probe.box_max)) {\n        return vec2f(0.0);\n    }\n    let low = world - probe.box_min;\n    let high = probe.box_max - world;\n    let edge = min(min(min(low.x, low.y), low.z), min(min(high.x, high.y), high.z));\n    let seam = select(clamp(edge / probe.fade, 0.0, 1.0), 1.0, probe.fade == 0.0);\n    return vec2f(seam * (1.0 + max(probe.priority, 0.0)) / (length(world - probe.center) + 0.001), seam);\n}\nfn local_probe_weights(probe_a: ReflectionProbe, probe_b: ReflectionProbe, present: vec2f, world: vec3f) -> vec2f {\n    let a = local_probe_weight(probe_a, world) * present.x;\n    let b = local_probe_weight(probe_b, world) * present.y;\n    let total = a.x + b.x;\n    if (total <= 0.0) {\n        return vec2f(0.0);\n    }\n    let coverage = max(a.y, b.y);\n    return vec2f(a.x, b.x) * coverage / total;\n}\nfn local_probe_gap(probe: ReflectionProbe, reached: vec3f, local: texture_cube_array<f32>, samp: sampler, level: f32) -> f32 {\n    let distance = max(length(reached), 0.000001);\n    let stored = 1.0 / max(-1.0 - textureSampleLevel(local, samp, reached / distance, probe.layer, level).a, 0.00001);\n    return (distance - stored) / distance;\n}\nfn local_probe_lookup(probe: ReflectionProbe, world: vec3f, direction: vec3f, local: texture_cube_array<f32>, samp: sampler, local_mip: f32) -> vec3f {\n    let level = max(local_mip - LOCAL_DEPTH_LEVELS, 0.0);\n    if (textureSampleLevel(local, samp, direction, probe.layer, level).a >= 0.0) {\n        return local_probe_direction(probe, world, direction);\n    }\n    let offset = world - probe.center;\n    var ahead = local_probe_gap(probe, offset, local, samp, level) < LOCAL_DEPTH_THICKNESS;\n    var near = 0.0;\n    var t = LOCAL_DEPTH_START;\n    for (var step = 0u; step < LOCAL_DEPTH_STEPS; step++) {\n        let outer = local_probe_gap(probe, offset + direction * t, local, samp, level);\n        if (outer <= LOCAL_DEPTH_TOLERANCE) {\n            ahead = true;\n        } else {\n            if (ahead) {\n                var low = near;\n                var high = t;\n                var gap = outer;\n                for (var refine = 0u; refine < LOCAL_DEPTH_REFINE; refine++) {\n                    let middle = (low + high) * 0.5;\n                    let found = local_probe_gap(probe, offset + direction * middle, local, samp, level);\n                    if (found > LOCAL_DEPTH_TOLERANCE) {\n                        high = middle;\n                        gap = found;\n                    } else {\n                        low = middle;\n                    }\n                }\n                let reached = offset + direction * high;\n                if (gap < LOCAL_DEPTH_THICKNESS + (high - low) / max(length(reached), 0.000001)) {\n                    return select(direction, normalize(reached), dot(reached, reached) > 0.0000001);\n                }\n            }\n            ahead = false;\n        }\n        near = t;\n        t *= LOCAL_DEPTH_GROWTH;\n    }\n    return direction;\n}\nfn local_probe_reference(probe: ReflectionProbe, world: vec3f, n: vec3f, local: texture_cube_array<f32>, samp: sampler, local_mip: f32) -> vec3f {\n    let along = textureSampleLevel(local, samp, n, probe.layer, local_mip);\n    if (along.a < 0.0) {\n        return along.rgb;\n    }\n    return textureSampleLevel(local, samp, local_probe_direction(probe, world, n), probe.layer, local_mip).rgb;\n}\nstruct LocalLookup {\n    weights: vec2f,\n    first: vec3f,\n    second: vec3f,\n}\nfn local_lookup(probe_a: ReflectionProbe, probe_b: ReflectionProbe, present: vec2f, world: vec3f, direction: vec3f, local: texture_cube_array<f32>, samp: sampler, local_mip: f32) -> LocalLookup {\n    var lookup = LocalLookup(vec2f(0.0), direction, direction);\n    if (!use_local_reflections) {\n        return lookup;\n    }\n    let weights = local_probe_weights(probe_a, probe_b, present, world);\n    let a = clamp(weights.x, 0.0, 1.0);\n    lookup.weights = vec2f(a, clamp(weights.y, 0.0, 1.0 - a));\n    if (lookup.weights.x > 0.0) {\n        lookup.first = local_probe_lookup(probe_a, world, direction, local, samp, local_mip);\n    }\n    if (lookup.weights.y > 0.0) {\n        lookup.second = local_probe_lookup(probe_b, world, direction, local, samp, local_mip);\n    }\n    return lookup;\n}\nfn reflection_dfg(roughness: f32, ndv: f32) -> vec2f {\n    let u = 2.0 * clamp(roughness, 0.05, 1.0) - 1.0;\n    let n = clamp(ndv, 0.02, 1.0);\n    let s = 2.0 * sqrt(n) - 1.0;\n    let p = vec3f(1.0, s, s * s);\n    let f5 = pow(1.0 - n, 5.0);\n    var dfg = vec2f(0.0);\n    dfg = dfg * u + vec2f(dot(p, vec3f(-0.4534661, 0.6691472, -0.1939928) + f5 * vec3f(2.698063, 3.846977, 1.655182)), dot(p, vec3f(0.1391939, -0.3242128, 0.1875587) + f5 * vec3f(-0.3898746, -2.342672, -3.369676)));\n    dfg = dfg * u + vec2f(dot(p, vec3f(0.02489464, 0.1060682, 0.07428504) + f5 * vec3f(-0.6803499, -2.683833, -1.808813)), dot(p, vec3f(0.2148219, -0.494718, 0.2827545) + f5 * vec3f(-1.484991, -0.5113827, 0.5854544)));\n    dfg = dfg * u + vec2f(dot(p, vec3f(0.462142, -0.411372, -0.06177693) + f5 * vec3f(-2.763494, -3.524146, -0.6253516)), dot(p, vec3f(-0.3198297, 0.7459178, -0.4316107) + f5 * vec3f(1.550273, 4.079052, 4.483775)));\n    dfg = dfg * u + vec2f(dot(p, vec3f(-0.02032731, -0.4825735, 0.04973246) + f5 * vec3f(0.2092212, 2.050402, 1.003509)), dot(p, vec3f(-0.1640982, 0.3660604, -0.2035734) + f5 * vec3f(1.369269, -0.2278983, -0.9679774)));\n    dfg = dfg * u + vec2f(dot(p, vec3f(-0.3511763, -0.1810853, 0.1673936) + f5 * vec3f(0.5484627, 0.5771128, -0.3130064)), dot(p, vec3f(0.1204449, -0.2777732, 0.1592967) + f5 * vec3f(-1.316489, -1.468577, -1.091297)));\n    dfg = dfg * u + vec2f(dot(p, vec3f(0.8148172, 0.1125372, -0.01374308) + f5 * vec3f(0.05596158, -0.1952609, -0.01097813)), dot(p, vec3f(0.03557167, -0.07356264, 0.03812399) + f5 * vec3f(0.1873088, 0.3210023, 0.1747016)));\n    return clamp(dfg, vec2f(0.0), vec2f(1.0));\n}\nfn reflection_environment(surface: Shaded, ndv: f32, film: vec3f, film_normal: vec3f) -> vec3f {\n    let f0 = reflection_filmed(surface, 1.0, film_normal);\n    if (all(f0 <= vec3f(0.0)) && surface.thin_film_amount <= 0.0) {\n        return vec3f(0.0);\n    }\n    let dfg = reflection_dfg(surface.roughness, ndv);\n    let fitted = clamp(f0 * (dfg.x - dfg.y) + vec3f(dfg.y), vec3f(0.0), vec3f(1.0));\n    let filmed = reflection_filmed(surface, ndv, film) - (f0 + (vec3f(1.0) - f0) * pow(1.0 - ndv, 5.0));\n    return clamp(fitted + select(vec3f(0.0), filmed, surface.thin_film_amount > 0.0), vec3f(0.0), vec3f(1.0));\n}\nfn reflection_diffuse_weight(surface: Shaded, view: vec3f, normal: vec3f, film: vec3f, film_normal: vec3f) -> vec3f {\n    let ndv = clamp(dot(normal, view), 0.0, 1.0);\n    let fresnel = reflection_environment(surface, ndv, film, film_normal);\n    let coat = clamp(surface.clearcoat, 0.0, 1.0);\n    let coat_f = schlick(vec3f(0.04), ndv, 5.0).r;\n    return (vec3f(1.0) - fresnel) * (1.0 - clamp(surface.metalness, 0.0, 1.0)) * (1.0 - clamp(surface.transmission, 0.0, 1.0)) * (1.0 - coat * coat_f);\n}\nfn reflection_fallback(\n    direction: vec3f,\n    lookup: LocalLookup,\n    rough: f32,\n    open: f32,\n    sky: texture_cube<f32>,\n    local: texture_cube_array<f32>,\n    probe_a: ReflectionProbe,\n    probe_b: ReflectionProbe,\n    samp: sampler,\n    local_mip: f32,\n    sky_mip: f32,\n) -> vec3f {\n    var fallback = vec3f(0.0);\n    if (open != 0.0) {\n        fallback = textureSampleLevel(sky, samp, direction, rough * sky_mip).rgb * open;\n    }\n    if (lookup.weights.x != 0.0) {\n        fallback += textureSampleLevel(local, samp, lookup.first, probe_a.layer, rough * local_mip).rgb * lookup.weights.x;\n    }\n    if (lookup.weights.y != 0.0) {\n        fallback += textureSampleLevel(local, samp, lookup.second, probe_b.layer, rough * local_mip).rgb * lookup.weights.y;\n    }\n    return fallback;\n}\nfn reflection_radiance(\n    surface: Shaded,\n    view: vec3f,\n    normal: vec3f,\n    hit: vec4f,\n    sky: texture_cube<f32>,\n    local: texture_cube_array<f32>,\n    probe_a: ReflectionProbe,\n    probe_b: ReflectionProbe,\n    lookup: LocalLookup,\n    samp: sampler,\n    local_mip: f32,\n    sky_mip: f32,\n    film: vec3f,\n    film_normal: vec3f,\n    sky_visible: f32,\n) -> vec3f {\n    let ndv = clamp(dot(normal, view), 0.0, 1.0);\n    let direction = reflect(-view, normal);\n    let rough = clamp(surface.roughness, 0.0, 1.0);\n    let open = (1.0 - lookup.weights.x - lookup.weights.y) * sky_visible;\n    let confidence = clamp(hit.a, 0.0, 1.0);\n    let caught = max(hit.rgb, vec3f(0.0));\n    let radiance = reflection_fallback(direction, lookup, rough, open, sky, local, probe_a, probe_b, samp, local_mip, sky_mip) * (1.0 - confidence) + caught;\n    let fresnel = reflection_environment(surface, ndv, film, film_normal);\n    let coat = clamp(surface.clearcoat, 0.0, 1.0);\n    let coat_f = schlick(vec3f(0.04), ndv, 5.0).r;\n    let coat_spec = coat * coat_f;\n    let substrate = min(fresnel * (1.0 - coat_spec), vec3f(1.0 - coat_spec));\n    if (coat_spec <= 0.0) {\n        return radiance * substrate;\n    }\n    let coat_rough = clamp(surface.clearcoat_roughness, 0.0, 1.0);\n    var coated = radiance;\n    if (coat_rough != rough) {\n        coated = reflection_fallback(direction, lookup, coat_rough, open, sky, local, probe_a, probe_b, samp, local_mip, sky_mip) * (1.0 - confidence) + caught;\n    }\n    return radiance * substrate + coated * coat_spec;\n}\n";