Skip to main content

concinnity_core/render/uniforms/
transparent.rs

1//! What the transparent pass binds: the shared per-frame view block, and the
2//! per-record tunables of its producers -- glass panes, see-through glass
3//! meshes, and water surfaces.
4
5/// Per-frame view inputs shared by every draw in the transparent pass (water,
6/// glass), bound once for the whole pass. Matches `TransparentView` in
7/// `shaders/glass.slang` and `shaders/water.slang`. 160 bytes.
8#[derive(Copy, Clone, bytemuck::NoUninit)]
9#[repr(C)]
10pub struct TransparentView {
11    /// View-projection matrix, column-major.
12    pub vp: [[f32; 4]; 4],
13    /// Inverse view-projection matrix, column-major.
14    pub inv_vp: [[f32; 4]; 4],
15    /// World-space camera position (xyz). `.w` is ignored by the shader.
16    pub camera_pos: [f32; 4],
17    /// Render-target width / height in pixels: the shader uses this to
18    /// turn its fragment position into a normalised screen UV.
19    pub viewport: [f32; 2],
20    /// Wall-clock seconds since startup, fed to the Gerstner sum.
21    pub time: f32,
22    /// Mip count of the bound IBL prefilter cube; 0 signals "no environment map",
23    /// where the glass reflection falls back to a white rim. Per-frame state, so
24    /// it rides the shared view rather than a per-draw params block.
25    pub prefilter_mip_count: f32,
26}
27
28/// Per-panel tunables for a `GlassPanel`, uploaded once per panel per frame.
29/// The vec3-ish fields are `[f32; 4]` so the layout is byte-identical to the
30/// shader's `float4`. Matches `GlassParams` in `shaders/glass.slang`. 64 bytes.
31#[derive(Copy, Clone, bytemuck::NoUninit)]
32#[repr(C)]
33pub struct GlassParams {
34    /// `[x, y, z, _]`: world-space panel centre.
35    pub centre: [f32; 4],
36    /// `[nx, ny, nz, _]`: unit panel normal (facing direction).
37    pub normal: [f32; 4],
38    /// `[r, g, b, _]`: colour multiplied into the refracted scene.
39    pub tint: [f32; 4],
40    /// Base alpha at normal incidence.
41    pub opacity: f32,
42    /// Screen-space refraction offset strength.
43    pub refraction_strength: f32,
44    /// Schlick-Fresnel exponent for the grazing-angle rim.
45    pub fresnel_power: f32,
46    /// Planar reflection strength: `> 0.5` selects the sharp planar reflection
47    /// (the scene re-rendered mirrored across this pane's plane, sampled
48    /// projectively at screen UV) over the probe / sky cube. 0 when planar is off
49    /// (RT on, no planar slot, or the plane overflowed the budget), keeping the
50    /// probe / sky path. Patched per-frame in `collect_glass_transparent_draws`.
51    pub planar: f32,
52}
53
54/// Per-draw tunables for a see-through glass MESH: a `Material` flagged
55/// `see_through` on an RT-capable device, drawn in the transparent pass instead
56/// of the opaque one. Unlike `GlassParams` (a pre-baked world-space pane), a
57/// mesh is LOCAL-space, so this carries the model matrix the vertex stage
58/// applies; the fragment uses the interpolated per-vertex world normal. Matches
59/// `GlassMeshParams` in `shaders/glass_mesh.slang`. 96 bytes (model is the first
60/// field, so its 16-byte GPU alignment is satisfied at offset 0).
61#[derive(Copy, Clone, bytemuck::NoUninit)]
62#[repr(C)]
63pub struct GlassMeshParams {
64    /// Column-major local-to-world model matrix.
65    pub model: [[f32; 4]; 4],
66    /// `[r, g, b, _]`: colour multiplied into the refracted scene (material tint).
67    pub tint: [f32; 4],
68    /// Base alpha at normal incidence (from `Material.opacity`).
69    pub opacity: f32,
70    /// Screen-space refraction offset strength.
71    pub refraction_strength: f32,
72    /// Schlick-Fresnel exponent for the grazing-angle rim.
73    pub fresnel_power: f32,
74    /// Mip count of the bound IBL prefilter cube (ray-miss fallback); 0 = none.
75    pub prefilter_mip_count: f32,
76}
77
78/// Maximum waves summed per `WaterParams`. Mirrors `MAX_WATER_WAVES` in
79/// `shaders/water.slang` and in the `WaterSurface` asset.
80pub const WATER_MAX_WAVES: usize = 4;
81
82/// One Gerstner wave coefficient set, packed into two `float4` lanes so the
83/// layout is identical on every target. Matches `WaterWave` in
84/// `shaders/water.slang`. 32 bytes.
85#[derive(Copy, Clone, Default, bytemuck::Zeroable, bytemuck::Pod)]
86#[repr(C)]
87pub struct WaterWaveGpu {
88    /// `[direction.x, direction.y, amplitude, wavelength]`.
89    pub dir_amp_wave: [f32; 4],
90    /// `[speed, steepness, _, _]`.
91    pub speed_steep_pad: [f32; 4],
92}
93
94/// Per-surface tunables for a `WaterSurface`, uploaded once per surface. The
95/// vec3-ish fields are `[f32; 4]` so the layout is byte-identical to the
96/// shader's `float4`. Matches `WaterParams` in `shaders/water.slang`. 224 bytes.
97#[derive(Copy, Clone, bytemuck::NoUninit)]
98#[repr(C)]
99pub struct WaterParams {
100    /// `[x, y, z, _]`: world-space surface centre.
101    pub centre: [f32; 4],
102    /// `[r, g, b, _]`: water tint at full column depth.
103    pub deep_colour: [f32; 4],
104    /// `[r, g, b, _]`: water tint just above the seabed.
105    pub shallow_colour: [f32; 4],
106    /// Depth over which the tint blends from shallow to deep, in metres.
107    pub depth_falloff: f32,
108    /// Width of the shoreline foam band, in world units.
109    pub foam_width: f32,
110    /// Foam brightness multiplier.
111    pub foam_intensity: f32,
112    /// Exponent of the Fresnel reflectance curve.
113    pub fresnel_power: f32,
114    /// Perceptual roughness in `[0, 1]`; picks the reflection's prefilter mip.
115    pub roughness: f32,
116    /// How far refraction offsets the sampled background.
117    pub refraction_strength: f32,
118    /// Live entries in `waves`.
119    pub wave_count: u32,
120    /// Padding so the field layout matches the shader-side struct.
121    pub _pad: f32,
122    /// Wave coefficients; the first `wave_count` entries are live.
123    pub waves: [WaterWaveGpu; WATER_MAX_WAVES],
124    /// Planar reflection control: `[strength, distortion, _, _]`. `strength >
125    /// 0.5` selects the sharp planar reflection (the scene re-rendered mirrored
126    /// across this surface's rest plane, sampled at screen UV) over the probe /
127    /// sky cube; `distortion` scales the wave-normal ripple offset of that
128    /// lookup. 0 when planar is off (RT on, no planar slot, or the plane
129    /// overflowed the budget), keeping the probe / sky path.
130    pub planar: [f32; 4],
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use core::mem::{offset_of, size_of};
137
138    // Every backend binds this block under the same layout, so it is checked
139    // here rather than per backend: a float4x4 model, a float4 tint, then four
140    // scalars. `model` is first, so its 16-byte GPU alignment is satisfied at
141    // offset 0 and the Rust `[[f32; 4]; 4]` matches byte-for-byte.
142    #[test]
143    fn glass_mesh_params_layout_matches_shader() {
144        assert_eq!(size_of::<GlassMeshParams>(), 96);
145        assert_eq!(offset_of!(GlassMeshParams, model), 0);
146        assert_eq!(offset_of!(GlassMeshParams, tint), 64);
147        assert_eq!(offset_of!(GlassMeshParams, opacity), 80);
148        assert_eq!(offset_of!(GlassMeshParams, refraction_strength), 84);
149        assert_eq!(offset_of!(GlassMeshParams, fresnel_power), 88);
150        assert_eq!(offset_of!(GlassMeshParams, prefilter_mip_count), 92);
151        assert_eq!(size_of::<GlassMeshParams>() % 16, 0);
152    }
153
154    // `waves` is an array of float4-carrying structs, so the shader aligns it to
155    // 16 and the seven live scalars ahead of it need `_pad` to reach that
156    // boundary. Nothing else pins the pad, so it is asserted here.
157    #[test]
158    fn water_params_layout_matches_shader() {
159        assert_eq!(size_of::<WaterParams>(), 224);
160        assert_eq!(offset_of!(WaterParams, centre), 0);
161        assert_eq!(offset_of!(WaterParams, deep_colour), 16);
162        assert_eq!(offset_of!(WaterParams, shallow_colour), 32);
163        assert_eq!(offset_of!(WaterParams, depth_falloff), 48);
164        assert_eq!(offset_of!(WaterParams, foam_width), 52);
165        assert_eq!(offset_of!(WaterParams, foam_intensity), 56);
166        assert_eq!(offset_of!(WaterParams, fresnel_power), 60);
167        assert_eq!(offset_of!(WaterParams, roughness), 64);
168        assert_eq!(offset_of!(WaterParams, refraction_strength), 68);
169        assert_eq!(offset_of!(WaterParams, wave_count), 72);
170        assert_eq!(offset_of!(WaterParams, _pad), 76);
171        assert_eq!(offset_of!(WaterParams, waves), 80);
172        assert_eq!(offset_of!(WaterParams, planar), 208);
173        assert_eq!(size_of::<WaterWaveGpu>(), 32);
174    }
175}