1use crate::core::prelude::*;
2
3use std::sync::Arc;
4
5pub struct DiffuseAreaLight {
6 base: BaseLight,
7 lemit: Spectrum,
8 shape: Arc<dyn Shape>,
9 two_sided: bool,
10 area: Float,
11}
12
13impl DiffuseAreaLight {
14 pub fn new(
15 light_to_world: &Transform,
16 medium_interface: &MediumInterface,
17 le: &Spectrum,
18 n_samples: u32,
19 shape: &Arc<dyn Shape>,
20 two_sided: bool,
21 ) -> Self {
22 let base = BaseLight::new(
23 LightFlags::Area as u32,
24 light_to_world,
25 medium_interface,
26 n_samples,
27 );
28 let lemit = *le;
29 let shape = shape.clone();
30 let area = shape.area();
31 DiffuseAreaLight {
32 base,
33 lemit,
34 shape,
35 two_sided,
36 area,
37 }
38 }
39
40 fn sample_wo(&self, _u1: &Vector2f, u2: &Vector2f) -> (Vector3f, Float) {
41 if self.two_sided {
42 let mut u = *u2;
43 let mut w;
46 if u[0] < 0.5 {
47 u.x = Float::min(u[0] * 2.0, ONE_MINUS_EPSILON);
48 w = cosine_sample_hemisphere(&u);
49 } else {
50 u.x = Float::min((u[0] - 0.5) * 2.0, ONE_MINUS_EPSILON);
51 w = cosine_sample_hemisphere(&u);
52 w.z *= -1.0;
53 };
54 let pdf = 0.5 * cosine_hemisphere_pdf(Float::abs(w.z));
55 return (w, pdf);
56 } else {
57 let w = cosine_sample_hemisphere(u2);
58 let pdf = cosine_hemisphere_pdf(w.z);
59 return (w, pdf);
60 }
61 }
62}
63
64impl Light for DiffuseAreaLight {
65 fn power(&self) -> Spectrum {
66 let n = if self.two_sided { 2.0 } else { 1.0 };
67 return self.lemit * (n * self.area * PI);
68 }
69
70 fn sample_li(
71 &self,
72 inter: &Interaction,
73 u: &Point2f,
74 ) -> Option<(Spectrum, Vector3f, Float, VisibilityTester)> {
75 let _p = ProfilePhase::new(Prof::LightSample);
76
77 let shape = self.shape.as_ref();
78 let (mut p_shape, pdf) = shape.sample_from(inter, u)?;
79 p_shape.set_medium_interface(&self.base.medium_interface);
80 if pdf <= 0.0 || (p_shape.get_p() - inter.get_p()).length_squared() <= 0.0 {
81 return None;
82 }
83 let wi = (p_shape.get_p() - inter.get_p()).normalize();
84 let vis = VisibilityTester::from((inter, &p_shape));
85 let spec = self.l(&p_shape, &-wi);
86 return Some((spec, wi, pdf, vis));
87 }
88
89 fn pdf_li(&self, inter: &Interaction, wi: &Vector3f) -> Float {
90 let _p = ProfilePhase::new(Prof::LightPdf);
91
92 let shape = self.shape.as_ref();
93 return shape.pdf_from(inter, wi);
94 }
95
96 fn sample_le(
97 &self,
98 u1: &Point2f,
99 u2: &Point2f,
100 _time: Float,
101 ) -> Option<(Spectrum, Ray, Normal3f, Float, Float)> {
102 let _p = ProfilePhase::new(Prof::LightSample);
103
104 let shape = self.shape.as_ref();
105 let (mut p_shape, pdf_pos) = shape.sample(u1)?;
106 p_shape.set_medium_interface(&self.base.medium_interface);
107 let n = p_shape.get_n();
108
109 let (w, pdf_dir) = self.sample_wo(u1, u2);
111 let (v1, v2) = coordinate_system(&n);
112 let w = w.x * v1 + w.y * v2 + w.z * n;
113 let ray = p_shape.spawn_ray(&w);
114 let spec = self.l(&p_shape, &w);
115 return Some((spec, ray, n, pdf_pos, pdf_dir));
116 }
117
118 fn pdf_le(&self, ray: &Ray, n: &Normal3f) -> Option<(Float, Float)> {
119 let _p = ProfilePhase::new(Prof::LightPdf);
120
121 let shape = self.shape.as_ref();
122 let n = *n;
123 let wo = Vector3f::from(n);
124 let medium_interface = self.base.medium_interface.clone();
125 let it = Interaction::from((ray.o, n, Vector3f::zero(), wo, ray.time, medium_interface));
126 let pdf_pos = shape.pdf(&it);
127 let pdf_dir = if self.two_sided {
128 0.5 * cosine_hemisphere_pdf(Vector3::abs_dot(&n, &ray.d))
129 } else {
130 cosine_hemisphere_pdf(Vector3f::dot(&n, &ray.d)).max(0.0)
133 };
135 if pdf_pos > 0.0 || pdf_dir > 0.0 {
136 return Some((pdf_pos, pdf_dir));
137 } else {
138 return None;
139 }
140 }
141
142 fn get_light_flags(&self) -> u32 {
143 return self.base.flags;
144 }
145
146 fn get_sample_count(&self) -> u32 {
147 return self.base.n_samples;
148 }
149
150 fn as_area_light(&self) -> Option<&dyn AreaLight> {
151 return Some(self);
152 }
153}
154
155impl AreaLight for DiffuseAreaLight {
156 fn l(&self, inter: &Interaction, w: &Vector3f) -> Spectrum {
157 if self.two_sided || Vector3f::dot(&inter.get_n(), w) > 0.0 {
158 return self.lemit;
159 } else {
160 return Spectrum::zero();
161 }
162 }
163}
164
165unsafe impl Sync for DiffuseAreaLight {}
166unsafe impl Send for DiffuseAreaLight {}
167
168pub fn create_diffuse_area_light(
169 light2world: &Transform,
170 medium: &Option<Arc<dyn Medium>>,
171 params: &ParamSet,
172 shape: &Arc<dyn Shape>,
173) -> Result<Arc<dyn Light>, PbrtError> {
174 let l = params.find_one_spectrum("L", &Spectrum::one());
175 let sc = params.find_one_spectrum("scale", &Spectrum::one());
176 let mi = MediumInterface::from(medium);
177 let mut n_samples = params.find_one_int("nsamples", 1) as u32;
178 let two_sided = params.find_one_bool("twosided", false);
179 {
180 let options = PbrtOptions::get();
181 if options.quick_render {
182 n_samples = (n_samples / 4).max(1);
183 }
184 }
185 return Ok(Arc::new(DiffuseAreaLight::new(
186 light2world,
187 &mi,
188 &(l * sc),
189 n_samples,
190 shape,
191 two_sided,
192 )));
193}