Skip to main content

concinnity_render/uniforms/
probe.rs

1//! The reflection-probe set the forward, SSR and ray-traced resolves all read.
2//! Matches `ProbeUniforms` / `ProbeSet` in `shaders/probe_types.slang`, whose
3//! `MAX_PROBES` is baked in from the constant below.
4
5/// Maximum reflection probes a frame can bind. The shader's `MAX_PROBES` define
6/// is injected from this value, so the two cannot drift.
7pub const MAX_PROBES: usize = 8;
8
9// Every automatically seeded probe has to fit in the bound set.
10const _: () = assert!(crate::reflection_probe::AUTO_SEED_BUDGET <= MAX_PROBES);
11
12/// One reflection probe's parallax box. The specular IBL term box-projects the
13/// reflection vector against [box_min, box_max] (the probe's influence volume)
14/// and re-anchors the cube sample at the box hit relative to `probe_pos` (the
15/// capture point), so a static captured cube tracks a moving first-person
16/// camera. Three float4s keep every field 16-byte aligned. `box_min.w` is the
17/// enabled flag: 0 disables parallax (and signals no baked probe), so the shader
18/// samples the raw reflection vector.
19#[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
20#[repr(C)]
21pub struct ProbeUniforms {
22    /// xyz = influence-box min; w = enabled (1.0 = parallax on, 0.0 = off).
23    pub box_min: [f32; 4],
24    /// xyz = influence-box max; w unused.
25    pub box_max: [f32; 4],
26    /// xyz = probe capture position; w unused.
27    pub probe_pos: [f32; 4],
28}
29
30impl ProbeUniforms {
31    /// The "no probe" value: parallax disabled, so the shader samples the raw
32    /// reflection vector (which, with the probe cube slot aliasing the sky until
33    /// a bake, reproduces the pre-probe reflection exactly).
34    pub const DISABLED: ProbeUniforms = ProbeUniforms {
35        box_min: [0.0; 4],
36        box_max: [0.0; 4],
37        probe_pos: [0.0; 4],
38    };
39}
40
41/// The full set of reflection probes. `count` is how many of `probes` are live;
42/// the fragment shader blends every probe whose influence box covers the surface
43/// (a partition-of-unity weight by signed box distance), falling back to the
44/// nearest when the surface is outside all boxes, and samples those slices of
45/// the probe cube array. Slices beyond `count` hold the sky fallback cube and a
46/// `DISABLED` box.
47#[derive(Copy, Clone, bytemuck::NoUninit)]
48#[repr(C)]
49pub struct ProbeSet {
50    /// Live entries in `probes`.
51    pub count: u32,
52    /// Padding so the field layout matches the shader-side struct.
53    /// Padding so the field layout matches the shader-side struct.
54    pub _pad: [u32; 3],
55    /// Probe entries; the first `count` are live.
56    pub probes: [ProbeUniforms; MAX_PROBES],
57}
58
59impl ProbeSet {
60    /// An empty set: no probes, so the shader falls back to the sky.
61    pub const EMPTY: ProbeSet = ProbeSet {
62        count: 0,
63        _pad: [0; 3],
64        probes: [ProbeUniforms::DISABLED; MAX_PROBES],
65    };
66}