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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::cmp;
use enum_set::EnumSet;
use linalg::{self, Normal, Vector, Point};
use film::Colorf;
use geometry::DifferentialGeometry;
use bxdf::{BxDF, BxDFType};
use sampler::Sample;
pub struct BSDF<'a> {
pub p: Point,
pub n: Normal,
pub ng: Normal,
pub tan: Vector,
pub bitan: Vector,
pub eta: f32,
bxdfs: &'a [&'a BxDF],
}
impl<'a> BSDF<'a> {
pub fn new<'b>(bxdfs: &'a [&'a BxDF], eta: f32, dg: &DifferentialGeometry<'b>) -> BSDF<'a> {
let n = dg.n.normalized();
let mut bitan = dg.dp_du.normalized();
let tan = linalg::cross(&n, &bitan);
bitan = linalg::cross(&tan, &n);
BSDF { p: dg.p, n: n, ng: dg.ng, tan: tan, bitan: bitan, bxdfs: bxdfs, eta: eta }
}
pub fn num_bxdfs(&self) -> usize { self.bxdfs.len() }
pub fn num_matching(&self, flags: EnumSet<BxDFType>) -> usize {
self.bxdfs.iter().filter(|x| x.matches(flags)).count()
}
pub fn to_shading(&self, v: &Vector) -> Vector {
Vector::new(linalg::dot(v, &self.bitan), linalg::dot(v, &self.tan),
linalg::dot(v, &self.n))
}
pub fn from_shading(&self, v: &Vector) -> Vector {
Vector::new(self.bitan.x * v.x + self.tan.x * v.y + self.n.x * v.z,
self.bitan.y * v.x + self.tan.y * v.y + self.n.y * v.z,
self.bitan.z * v.x + self.tan.z * v.y + self.n.z * v.z)
}
pub fn eval(&self, wo_world: &Vector, wi_world: &Vector, mut flags: EnumSet<BxDFType>) -> Colorf {
let w_o = self.to_shading(wo_world).normalized();
let w_i = self.to_shading(wi_world).normalized();
if w_o.z * w_i.z > 0.0 {
flags.remove(&BxDFType::Transmission);
} else {
flags.remove(&BxDFType::Reflection);
}
self.bxdfs.iter().filter_map(|x| if x.matches(flags) { Some(x.eval(&w_o, &w_i)) } else { None })
.fold(Colorf::broadcast(0.0), |x, y| x + y)
}
pub fn sample(&self, wo_world: &Vector, flags: EnumSet<BxDFType>, samples: &Sample)
-> (Colorf, Vector, f32, EnumSet<BxDFType>)
{
let n_matching = self.num_matching(flags);
if n_matching == 0 {
return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
}
let comp = cmp::min((samples.one_d * n_matching as f32) as usize, n_matching - 1);
let bxdf = self.matching_at(comp, flags);
let w_o = self.to_shading(wo_world).normalized();
let (mut f, w_i, mut pdf) = bxdf.sample(&w_o, &samples.two_d);
if w_i.length_sqr() == 0.0 {
return (Colorf::broadcast(0.0), Vector::broadcast(0.0), 0.0, EnumSet::new());
}
let wi_world = self.from_shading(&w_i).normalized();
if !bxdf.bxdf_type().contains(&BxDFType::Specular) && n_matching > 1 {
pdf = self.pdf(wo_world, &wi_world, flags);
}
if !bxdf.bxdf_type().contains(&BxDFType::Specular) {
f = self.eval(wo_world, &wi_world, flags);
}
(f, wi_world, pdf, bxdf.bxdf_type())
}
pub fn pdf(&self, wo_world: &Vector, wi_world: &Vector, flags: EnumSet<BxDFType>) -> f32 {
let w_o = self.to_shading(wo_world).normalized();
let w_i = self.to_shading(wi_world).normalized();
let (pdf_val, n_comps) = self.bxdfs.iter()
.filter_map(|x| if x.matches(flags) { Some(x.pdf(&w_o, &w_i)) } else { None })
.fold((0.0, 0), |(p, n), y| (p + y, n + 1));
if n_comps > 0 {
pdf_val / n_comps as f32
} else {
0.0
}
}
fn matching_at(&self, i: usize, flags: EnumSet<BxDFType>) -> &BxDF {
let mut it = self.bxdfs.iter().filter(|x| x.matches(flags)).skip(i);
match it.next() {
Some(b) => *b,
None => panic!("Out of bounds index for BxDF type {:?}", flags)
}
}
}