Skip to main content

scirs2_vision/nerf/
types.rs

1//! Core types for Neural Radiance Fields (NeRF) and Instant-NGP.
2//!
3//! Defines configuration structs, ray/sample types, and rendering result types
4//! used across the NeRF implementation.
5
6/// Configuration for a standard NeRF MLP model (Mildenhall et al. 2020).
7#[derive(Debug, Clone)]
8#[non_exhaustive]
9pub struct NerfConfig {
10    /// Number of hidden layers in the geometry network.
11    pub n_layers: usize,
12    /// Width (number of units) of each hidden layer.
13    pub hidden_dim: usize,
14    /// Number of frequency bands for positional encoding of 3-D location.
15    pub n_freq_pos: usize,
16    /// Number of frequency bands for positional encoding of view direction.
17    pub n_freq_dir: usize,
18    /// Near clipping distance along the ray.
19    pub near: f64,
20    /// Far clipping distance along the ray.
21    pub far: f64,
22    /// Number of coarse stratified samples per ray.
23    pub n_samples: usize,
24    /// Number of additional importance samples per ray (hierarchical sampling).
25    pub n_importance: usize,
26}
27
28impl Default for NerfConfig {
29    fn default() -> Self {
30        Self {
31            n_layers: 8,
32            hidden_dim: 256,
33            n_freq_pos: 10,
34            n_freq_dir: 4,
35            near: 2.0,
36            far: 6.0,
37            n_samples: 64,
38            n_importance: 128,
39        }
40    }
41}
42
43/// Configuration for Instant-NGP multi-resolution hash encoding (Müller et al. 2022).
44#[derive(Debug, Clone)]
45#[non_exhaustive]
46pub struct NgpConfig {
47    /// Number of resolution levels in the hash grid hierarchy.
48    pub n_levels: usize,
49    /// Number of feature dimensions stored per hash entry per level.
50    pub n_features_per_level: usize,
51    /// log₂ of the hash table capacity at each level.
52    pub log2_hashmap_size: usize,
53    /// Grid resolution at the coarsest level.
54    pub base_resolution: usize,
55    /// Grid resolution at the finest level.
56    pub finest_resolution: usize,
57}
58
59impl Default for NgpConfig {
60    fn default() -> Self {
61        Self {
62            n_levels: 16,
63            n_features_per_level: 2,
64            log2_hashmap_size: 19,
65            base_resolution: 16,
66            finest_resolution: 512,
67        }
68    }
69}
70
71/// A camera ray defined by an origin point and a unit-length direction vector.
72#[derive(Debug, Clone, Copy)]
73pub struct Ray {
74    /// World-space origin of the ray (camera position).
75    pub origin: [f64; 3],
76    /// Unit-length direction vector of the ray in world space.
77    pub direction: [f64; 3],
78}
79
80impl Ray {
81    /// Construct a new [`Ray`], normalising `direction` to unit length.
82    ///
83    /// Returns `None` when `direction` has zero (or near-zero) magnitude.
84    pub fn new(origin: [f64; 3], direction: [f64; 3]) -> Option<Self> {
85        let mag = (direction[0] * direction[0]
86            + direction[1] * direction[1]
87            + direction[2] * direction[2])
88            .sqrt();
89        if mag < 1e-12 {
90            return None;
91        }
92        Some(Self {
93            origin,
94            direction: [direction[0] / mag, direction[1] / mag, direction[2] / mag],
95        })
96    }
97
98    /// Evaluate the ray at parameter `t`: `origin + t * direction`.
99    #[inline]
100    pub fn at(&self, t: f64) -> [f64; 3] {
101        [
102            self.origin[0] + t * self.direction[0],
103            self.origin[1] + t * self.direction[1],
104            self.origin[2] + t * self.direction[2],
105        ]
106    }
107}
108
109/// A single volumetric sample along a ray.
110#[derive(Debug, Clone)]
111pub struct SamplePoint {
112    /// World-space 3-D position of the sample.
113    pub position: [f64; 3],
114    /// Distance along the ray at which this sample was taken.
115    pub t: f64,
116    /// Volume density σ predicted by the MLP (non-negative).
117    pub density: f64,
118    /// RGB radiance (each channel in [0, 1]) predicted by the MLP.
119    pub color: [f64; 3],
120}
121
122impl SamplePoint {
123    /// Create a new sample, clamping `density` to ≥ 0.
124    pub fn new(position: [f64; 3], t: f64, density: f64, color: [f64; 3]) -> Self {
125        Self {
126            position,
127            t,
128            density: density.max(0.0),
129            color: [
130                color[0].clamp(0.0, 1.0),
131                color[1].clamp(0.0, 1.0),
132                color[2].clamp(0.0, 1.0),
133            ],
134        }
135    }
136}
137
138/// Output of the discrete volume-rendering integral.
139#[derive(Debug, Clone)]
140pub struct VolumeRenderResult {
141    /// Rendered RGB color for the ray (each channel in [0, 1]).
142    pub color: [f64; 3],
143    /// Expected depth — weighted sum of sample distances.
144    pub depth: f64,
145    /// Accumulated transmittance remaining after all samples.
146    pub transmittance: f64,
147    /// Per-sample alpha-compositing weights (Tᵢ · αᵢ).
148    pub weights: Vec<f64>,
149}