ruby_math/sampling/
mod.rs

1use 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}