14 entt::meta_factory<spatial_denoise_settings>{}
15 .type(
"ssil_pass::spatial_denoise_settings"_hs)
20 .data<&spatial_denoise_settings::depth_sigma>(
"depth_sigma"_hs)
26 entt::attribute{
"tooltip",
"Depth edge-stopping threshold for the joint-bilateral a-trous filter.\nLower = stricter edges / sharper, higher = smoother."},
28 .data<&spatial_denoise_settings::normal_power>(
"normal_power"_hs)
34 entt::attribute{
"tooltip",
"Normal edge-stopping exponent. Higher = stricter edges"},
36 .data<&spatial_denoise_settings::luma_sigma>(
"luma_sigma"_hs)
42 entt::attribute{
"tooltip",
"Variance-guided luminance edge-stop multiplier (phi). Scales the measured\nspatiotemporal luminance std-dev (temporal moments, spatial fallback when fresh):\nlower = sharper / more detail kept, higher = smoother."},
44 .data<&spatial_denoise_settings::passes>(
"passes"_hs)
50 entt::attribute{
"tooltip",
"Number of a-trous filter passes. More passes = wider blur reach but higher cost"},
52 .data<&spatial_denoise_settings::full_res_passes>(
"full_res_passes"_hs)
58 entt::attribute{
"tooltip",
"Mixed-resolution split: how many a-trous passes run at the trace resolution. The\nREMAINING (wide) passes run at half res (cache-coherent, ~4x cheaper); a bilateral\nupsample then restores sharp silhouettes from the full-res G-buffer. 0 lets the pass\nchoose the cheapest split; when temporal moments exist it is promoted to one full-res\npass so stable variance is consumed before the half-res tier. >= Passes = all full res."},
60 .data<&spatial_denoise_settings::max_step>(
"max_step"_hs)
66 entt::attribute{
"tooltip",
"Caps the a-trous dilation (trace texels). The step normally doubles each pass\n(1,2,4,8,16); the widely-dilated late passes scatter taps and thrash the texture\ncache, costing far more than early passes. Lower this (e.g. 8) to flatten that\ncost spike with minimal quality loss; 16 = uncapped doubling."},
120 if(
auto data = obj.try_cast<ssil_settings>())
122 return data->enable_multi_bounce;
129 if(
auto data = obj.try_cast<ssil_settings>())
131 return data->enable_temporal_accumulation;
138 if(
auto data = obj.try_cast<ssil_settings>())
140 return data->enable_spatial_denoise;
145 entt::meta_factory<ssil_settings>{}
146 .type(
"ssil_pass::ssil_settings"_hs)
151 .data<&ssil_settings::max_rays>(
"max_rays"_hs)
157 entt::attribute{
"tooltip",
"Number of hemisphere rays per pixel. More = better quality, higher cost"},
159 .data<&ssil_settings::max_steps>(
"max_steps"_hs)
167 .data<&ssil_settings::depth_tolerance>(
"depth_tolerance"_hs)
175 .data<&ssil_settings::thickness>(
"thickness"_hs)
181 entt::attribute{
"tooltip",
"Extra view-space hit-acceptance band added on top of Depth Tolerance, scaled by hit\ndistance. Far hits resolve against coarser Hi-Z depth, so a fixed band over-rejects\nthem and leaks the environment fallback through occluders; raising thickness closes\nthat leak. Too high over-occludes (darkening). 0 = fixed band only."},
183 .data<&ssil_settings::brightness>(
"brightness"_hs)
189 entt::attribute{
"tooltip",
"Gain on the on-screen indirect BOUNCE only (the environment-miss fallback stays at\nprobe intensity, so unoccluded ambient matches the SH probe). 1.0 = physically-matched\nsingle bounce (reads strong with saturated albedos); 0.5 is the tamer default. Raise\ntoward 1.0 for full physical colour bleed."},
191 .data<&ssil_settings::max_distance>(
"max_distance"_hs)
197 entt::attribute{
"tooltip",
"Maximum ray distance in world units. SSIL captures local bounce; distant GI comes from probes"},
199 .data<&ssil_settings::resolution>(
"resolution"_hs)
203 entt::attribute{
"tooltip",
"Downscale divisor for SSIL trace buffers.\nFull = reference quality, Half = ~4x faster, Quarter = ~16x faster (viable for indirect lighting because it is inherently low-frequency).\nThe indirect pass samples the upscaled result through the same full-res G-buffer UV."},
205 .data<&ssil_settings::enable_multi_bounce>(
"enable_multi_bounce"_hs)
209 entt::attribute{
"tooltip",
"Feed previous frame's SSIL back into the trace for multi-bounce indirect lighting. Light propagates deeper into corridors and shadowed areas over several frames. Each bounce is attenuated by the hit surface's diffuse albedo for energy conservation."},
211 .data<&ssil_settings::multi_bounce_intensity>(
"multi_bounce_intensity"_hs)
218 entt::attribute{
"tooltip",
"Controls how much indirect light is carried between bounces. Acts as an extra decay factor on top of surface albedo attenuation. 0 = single bounce only, 0.5 = balanced (default), 1.0 = full physical albedo-only attenuation."},
220 .data<&ssil_settings::enable_spatial_denoise>(
"enable_spatial_denoise"_hs)
224 entt::attribute{
"tooltip",
"Enable edge-preserving spatial denoising (a-trous wavelet)"},
226 .data<&ssil_settings::spatial_denoise>(
"spatial_denoise"_hs)
234 .data<&ssil_settings::enable_temporal_accumulation>(
"enable_temporal_accumulation"_hs)
238 entt::attribute{
"tooltip",
"Enable temporal accumulation to reduce noise over multiple frames"},
240 .data<&ssil_settings::temporal>(
"temporal"_hs)
321 try_load(ar, ser20::make_nvp(
"max_rays", obj.max_rays));
322 try_load(ar, ser20::make_nvp(
"max_steps", obj.max_steps));
323 try_load(ar, ser20::make_nvp(
"depth_tolerance", obj.depth_tolerance));
324 try_load(ar, ser20::make_nvp(
"thickness", obj.thickness));
325 try_load(ar, ser20::make_nvp(
"brightness", obj.brightness));
326 try_load(ar, ser20::make_nvp(
"max_distance", obj.max_distance));
329 if(!
try_load(ar, ser20::make_nvp(
"resolution", obj.resolution)))
331 bool legacy_half_res =
false;
332 if(
try_load(ar, ser20::make_nvp(
"enable_half_res", legacy_half_res)))
337 try_load(ar, ser20::make_nvp(
"enable_multi_bounce", obj.enable_multi_bounce));
338 try_load(ar, ser20::make_nvp(
"multi_bounce_intensity", obj.multi_bounce_intensity));
339 try_load(ar, ser20::make_nvp(
"enable_spatial_denoise", obj.enable_spatial_denoise));
340 try_load(ar, ser20::make_nvp(
"spatial_denoise", obj.spatial_denoise));
341 try_load(ar, ser20::make_nvp(
"enable_temporal_accumulation", obj.enable_temporal_accumulation));
342 try_load(ar, ser20::make_nvp(
"temporal", obj.temporal));