1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Henyey-Greenstein volumetric scattering and the frosted-facet BSDF.
//!
//! [`maybe_scatter_or_extinguish`]'s per-bounce extinction/scatter estimator, the HG
//! phase function and its importance sampler, and [`apply_frosted_bounce`]'s diffuse
//! reflect/transmit dispatch.
//!
//! Split from a single `scattering.rs` into this module tree by seam: [`hg`] holds the
//! Henyey-Greenstein volumetric estimator and its own next-event-estimation
//! contribution, [`frosted`] holds the frosted-facet BSDF and its exterior-side NEE
//! contribution, and this file keeps the shared [`NeeContext`]/[`balance_heuristic`]/
//! [`frosted_orthonormal_basis`] plumbing both submodules build on. Every path
//! reachable as `scattering::X` before the split stays reachable at exactly that path
//! via the re-exports below.
use EnvironmentSource;
use Vec3;
// `pub`, not private: a `pub(crate)` item inside a private module is only reachable
// via this file's re-export, which clippy's `redundant_pub_crate` (nursery) then flags
// as suspicious -- see `raytracer::mod`'s own identical comment for this exact pattern.
// Making the submodule `pub` resolves it without widening any item's own effective
// visibility, still capped at its declared visibility.
// hg.rs
pub use ;
pub use ;
// Used only by the GPU twin checks (`renderer::gpu::transport_check`) and tests.
pub use henyey_greenstein_phase;
pub use nee_contribution_hg_scatter;
// frosted.rs
pub use nee_contribution_frosted_exterior;
pub use ;
/// Most boundary crossings the Henyey-Greenstein NEE shadow probe follows on a concave
/// stone before it drops the sample. A groove or dimple costs two crossings (out and
/// back in) per exit, so four covers one cavity plus the final exit.
pub const HG_NEE_MAX_CROSSINGS: usize = 4;
/// Bundles the per-trace next-event-estimation inputs
/// [`nee_contribution_hg_scatter`] and [`nee_contribution_frosted_exterior`] need -- the
/// environment (only [`EnvironmentSource::HdrMap`] has an importance distribution to
/// sample; see `sample_environment_for_nee`), the intersection arena a shadow ray from
/// an interior scattering point probes against (the exterior frosted NEE uses it only for
/// a concave stone's shadow probe, see that function's doc comment), and whether NEE is switched on for this trace at all.
///
/// `enabled = false` (every procedural-rig scene, and any trace that otherwise opts out)
/// skips every NEE draw entirely -- no RNG consumption, no accumulator touched -- so
/// those traces stay bit-identical to one traced with NEE support absent altogether.
/// `Copy`: every field is either a
/// reference or an `EnvironmentSource` (itself `Copy`), so passing by value is as cheap
/// as passing `&NeeContext<'_>` and avoids an extra indirection at each of this struct's
/// several call sites.
///
/// `pub(crate)`, not `pub(super)`: `renderer::gpu::transport_check`'s Tier 2 ULP check
/// for [`apply_frosted_bounce`] constructs an `enabled: false` instance directly (its
/// existing base-physics comparison predates NEE and stays that way until a dedicated
/// NEE harness lands -- see that check's own doc comment), mirroring why
/// `apply_frosted_bounce` itself is `pub(crate)`.
pub
/// Balance-heuristic MIS weight for a light-sampling technique with pdf `pdf_a` against a
/// competing (BSDF/phase) technique with pdf `pdf_b`, evaluated at a direction drawn from
/// technique `a` -- `w_a = pdf_a / (pdf_a + pdf_b)`. `0.0` if both densities are
/// non-positive (a genuinely degenerate case: e.g. an all-black environment row, or a
/// direction the phase function assigns zero density), contributing nothing rather than
/// dividing by zero. Symmetric in the sense Veach's balance heuristic requires:
/// `balance_heuristic(a, b) + balance_heuristic(b, a) == 1.0` for any `a, b > 0`.
pub
/// A stable orthonormal basis `(t, b)` perpendicular to unit vector `n` -- the same
/// branch-minimal construction `birefringence::stable_orthonormal_basis` uses, kept as
/// its own local copy rather than exposing that private helper across modules.
pub