oxiphysics_geometry/signed_distance_field/
ray_marcher.rs1use super::helpers::{add3, normalize3, scale3};
7use super::operators::sdf_normal;
8
9#[derive(Debug, Clone)]
15pub struct RayMarchHit {
16 pub t: f64,
18 pub point: [f64; 3],
20 pub normal: [f64; 3],
22 pub sdf_value: f64,
24 pub steps: usize,
26}
27
28#[derive(Debug, Clone)]
30pub struct RayMarcher {
31 pub t_max: f64,
33 pub tolerance: f64,
35 pub max_steps: usize,
37 pub step_scale: f64,
39}
40
41impl RayMarcher {
42 pub fn new() -> Self {
44 Self {
45 t_max: 100.0,
46 tolerance: 1e-4,
47 max_steps: 256,
48 step_scale: 0.95,
49 }
50 }
51
52 pub fn with_params(t_max: f64, tolerance: f64, max_steps: usize, step_scale: f64) -> Self {
54 Self {
55 t_max,
56 tolerance,
57 max_steps,
58 step_scale,
59 }
60 }
61
62 pub fn march<F>(&self, f: &F, origin: [f64; 3], dir: [f64; 3]) -> Option<RayMarchHit>
67 where
68 F: Fn([f64; 3]) -> f64,
69 {
70 let d = normalize3(dir);
71 let mut t = 0.0;
72 for step in 0..self.max_steps {
73 let p = add3(origin, scale3(d, t));
74 let sdf = f(p);
75 if sdf.abs() < self.tolerance {
76 let normal = sdf_normal(f, p, 1e-4);
77 return Some(RayMarchHit {
78 t,
79 point: p,
80 normal,
81 sdf_value: sdf,
82 steps: step + 1,
83 });
84 }
85 if t > self.t_max {
86 break;
87 }
88 t += sdf.abs() * self.step_scale;
89 }
90 None
91 }
92
93 pub fn shadow<F>(&self, f: &F, p: [f64; 3], light_dir: [f64; 3], max_dist: f64) -> f64
96 where
97 F: Fn([f64; 3]) -> f64,
98 {
99 let d = normalize3(light_dir);
100 let mut t = 0.01; let mut shadow = 1.0_f64;
102 for _ in 0..self.max_steps {
103 if t >= max_dist {
104 break;
105 }
106 let q = add3(p, scale3(d, t));
107 let h = f(q);
108 if h < self.tolerance {
109 return 0.0;
110 }
111 shadow = shadow.min(8.0 * h / t);
112 t += h;
113 }
114 shadow.clamp(0.0, 1.0)
115 }
116
117 pub fn ambient_occlusion<F>(&self, f: &F, p: [f64; 3], n: [f64; 3], step: f64) -> f64
119 where
120 F: Fn([f64; 3]) -> f64,
121 {
122 let mut occ = 0.0;
123 let mut scale = 1.0;
124 for i in 0..5 {
125 let dist = (i + 1) as f64 * step;
126 let q = add3(p, scale3(n, dist));
127 occ += scale * (dist - f(q));
128 scale *= 0.5;
129 }
130 1.0 - occ.clamp(0.0, 1.0)
131 }
132}
133
134impl Default for RayMarcher {
135 fn default() -> Self {
136 Self::new()
137 }
138}