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}