ruby_math/sampling/
mod.rs1use crate::algebra::{vec2d, vec3d, Transform, Vec2d, Vec3d};
2
3mod rng;
4pub use rng::{rng_pcg32, Pcg32, Rng};
5
6pub fn concentric_sample_disk(u: Vec2d) -> Vec2d {
7 let u = u * 2.0 - 1.0;
8
9 if u.x() == 0.0 && u.y() == 0.0 {
10 return vec2d(0.0, 0.0);
11 }
12
13 if u.x().abs() > u.y().abs() {
14 let r = u.x();
15 let theta = std::f64::consts::FRAC_PI_4 * (u.y() / u.x());
16 vec2d(theta.cos(), theta.sin()) * r
17 } else {
18 let r = u.y();
19 let theta = std::f64::consts::FRAC_PI_2 - std::f64::consts::FRAC_PI_4 * (u.x() / u.y());
20 vec2d(theta.cos(), theta.sin()) * r
21 }
22}
23
24pub fn uniform_sample_disk(u: Vec2d) -> Vec2d {
25 let (u1, u2) = u.to_tuple();
26 let r = u1.sqrt();
27 let theta = 2.0 * std::f64::consts::PI * u2;
28
29 vec2d(theta.cos() * r, theta.sin() * r)
30}
31
32pub fn uniform_sample_sphere(u: Vec2d) -> (Vec3d, f64) {
33 let (u1, u2) = u.to_tuple();
34 let z = 1.0 - 2.0 * u1;
35 let r = 0f64.max(1.0 - z * z).sqrt();
36 let phi = std::f64::consts::PI * 2.0 * u2;
37
38 (
39 vec3d(phi.cos() * r, phi.sin() * r, z),
40 1.0 / (4.0 * std::f64::consts::PI),
41 )
42}
43
44pub fn uniform_sample_hemisphere(n: Vec3d, u: Vec2d) -> (Vec3d, f64) {
45 let (mut result, _) = uniform_sample_sphere(u);
46 if result.dot(n) < 0.0 {
47 result = -result;
48 }
49
50 (result, 1.0 / (2.0 * std::f64::consts::PI))
51}
52
53pub fn cosine_sample_hemisphere(n: Vec3d, u: Vec2d) -> (Vec3d, f64) {
54 let d = concentric_sample_disk(u);
55 let z = 0.0f64.max(1.0 - d.x() * d.x() - d.y() * d.y()).sqrt();
56 let pdf = z * std::f64::consts::FRAC_1_PI;
57 if vec3d(0.0, 0.0, 1.0).eq(&n) {
58 return (vec3d(d.x(), d.y(), z), pdf);
59 }
60 if vec3d(0.0, 0.0, -1.0).eq(&n) {
61 return (vec3d(d.x(), d.y(), -z), pdf);
62 }
63 let d = vec3d(d.x(), d.y(), z);
64 let n0 = vec3d(0.0, 0.0, 1.0);
65 let axis = n0.cross(n).normalize();
66 let angle = n0.angle_between(n).to_degrees();
67 let t = Transform::from_rotation_axis(axis, angle);
68 let d = t.transform_vector3d(d);
69 (d.normalize(), pdf)
70}