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
109
110
111
112
use std::f32;
use geometry::{Geometry, DifferentialGeometry, Boundable, BBox, Sampleable};
use linalg::{self, Normal, Vector, Ray, Point};
use mc;
#[derive(Clone, Copy)]
pub struct Disk {
radius: f32,
inner_radius: f32,
}
impl Disk {
pub fn new(radius: f32, inner_radius: f32) -> Disk {
Disk { radius: radius, inner_radius: inner_radius }
}
}
impl Geometry for Disk {
fn intersect(&self, ray: &mut Ray) -> Option<DifferentialGeometry> {
if f32::abs(ray.d.z) == 0.0 {
return None;
}
let t = -ray.o.z / ray.d.z;
if t < ray.min_t || t > ray.max_t {
return None;
}
let p = ray.at(t);
let dist_sqr = p.x * p.x + p.y * p.y;
if dist_sqr > self.radius * self.radius || dist_sqr < self.inner_radius * self.inner_radius {
return None;
}
let mut phi = f32::atan2(p.y, p.x);
if phi < 0.0 {
phi += f32::consts::PI * 2.0;
}
if phi > f32::consts::PI * 2.0 {
return None;
}
ray.max_t = t;
let hit_radius = f32::sqrt(dist_sqr);
let u = phi / (2.0 * f32::consts::PI);
let v = 1.0 - (hit_radius - self.inner_radius) / (self.radius - self.inner_radius);
let dp_du = Vector::new(-f32::consts::PI * 2.0 * p.y, f32::consts::PI * 2.0 * p.x, 0.0);
let dp_dv = ((self.inner_radius - self.radius) / hit_radius) * Vector::new(p.x, p.y, 0.0);
Some(DifferentialGeometry::new(&p, &Normal::new(0.0, 0.0, 1.0),
u, v, ray.time, &dp_du, &dp_dv, self))
}
}
impl Boundable for Disk {
fn bounds(&self, _: f32, _: f32) -> BBox {
BBox::span(Point::new(-self.radius, -self.radius, -0.1), Point::new(self.radius, self.radius, 0.1))
}
}
impl Sampleable for Disk {
fn sample_uniform(&self, samples: &(f32, f32)) -> (Point, Normal) {
let disk_pos = mc::concentric_sample_disk(samples);
let p = Point::new(disk_pos.0 * self.radius, disk_pos.1 * self.radius, 0.0);
let n = Normal::new(0.0, 0.0, 1.0);
(p, n)
}
fn sample(&self, _: &Point, samples: &(f32, f32)) -> (Point, Normal) {
self.sample_uniform(samples)
}
fn surface_area(&self) -> f32 {
f32::consts::PI * (self.radius * self.radius - self.inner_radius * self.inner_radius)
}
fn pdf(&self, p: &Point, w_i: &Vector) -> f32 {
let mut ray = Ray::segment(p, w_i, 0.001, f32::INFINITY, 0.0);
match self.intersect(&mut ray) {
Some(d) => {
let w = -*w_i;
let pdf = p.distance_sqr(&ray.at(ray.max_t))
/ (f32::abs(linalg::dot(&d.n, &w)) * self.surface_area());
if f32::is_finite(pdf) { pdf } else { 0.0 }
},
None => 0.0
}
}
}