1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2019 Esteban Tovagliari, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28
29 // Interface header.
30 #include "lighttypes.h"
31
32 // appleseed.renderer headers.
33 #include "renderer/kernel/intersection/intersector.h"
34 #include "renderer/kernel/lighting/lightsample.h"
35
36 // appleseed.foundation headers.
37 #include "foundation/math/basis.h"
38 #include "foundation/math/distance.h"
39 #include "foundation/math/fp.h"
40 #include "foundation/math/intersection/rayparallelogram.h"
41 #include "foundation/math/intersection/raytrianglemt.h"
42 #include "foundation/math/sampling/mappings.h"
43
44 using namespace foundation;
45
46 namespace renderer
47 {
48
49 //
50 // EmittingShape class implementation.
51 //
52 // References:
53 //
54 // [1] Monte Carlo Techniques for Direct Lighting Calculations.
55 // http://www.cs.virginia.edu/~jdl/bib/globillum/mis/shirley96.pdf
56 //
57 // [2] Stratified Sampling of Spherical Triangles.
58 // https://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf
59 //
60 // [3] An Area-Preserving Parametrization for Spherical Rectangles.
61 // https://www.arnoldrenderer.com/research/egsr2013_spherical_rectangle.pdf
62 //
63
64 namespace
65 {
66 template <typename Shape>
signed_plane_distance(const Shape & shape,const Vector3d & p)67 double signed_plane_distance(const Shape& shape, const Vector3d& p)
68 {
69 return dot(p, shape.m_geometric_normal) + shape.m_plane_dist;
70 }
71 }
72
create_triangle_shape(const AssemblyInstance * assembly_instance,const size_t object_instance_index,const size_t primitive_index,const Material * material,const double area,const Vector3d & v0,const Vector3d & v1,const Vector3d & v2,const Vector3d & n0,const Vector3d & n1,const Vector3d & n2,const Vector3d & geometric_normal)73 EmittingShape EmittingShape::create_triangle_shape(
74 const AssemblyInstance* assembly_instance,
75 const size_t object_instance_index,
76 const size_t primitive_index,
77 const Material* material,
78 const double area,
79 const Vector3d& v0,
80 const Vector3d& v1,
81 const Vector3d& v2,
82 const Vector3d& n0,
83 const Vector3d& n1,
84 const Vector3d& n2,
85 const Vector3d& geometric_normal)
86 {
87 EmittingShape shape(
88 TriangleShape,
89 assembly_instance,
90 object_instance_index,
91 primitive_index,
92 material);
93
94 shape.m_geom.m_triangle.m_v0 = v0;
95 shape.m_geom.m_triangle.m_v1 = v1;
96 shape.m_geom.m_triangle.m_v2 = v2;
97 shape.m_geom.m_triangle.m_n0 = n0;
98 shape.m_geom.m_triangle.m_n1 = n1;
99 shape.m_geom.m_triangle.m_n2 = n2;
100 shape.m_geom.m_triangle.m_geometric_normal = geometric_normal;
101 shape.m_geom.m_triangle.m_plane_dist = -dot(v0, geometric_normal);
102
103 shape.m_bbox.invalidate();
104 shape.m_bbox.insert(v0);
105 shape.m_bbox.insert(v1);
106 shape.m_bbox.insert(v2);
107
108 shape.m_centroid = (v0 + v1 + v2) * (1.0 / 3.0);
109
110 shape.m_area = static_cast<float>(area);
111
112 if (shape.m_area != 0.0f)
113 shape.m_rcp_area = 1.0f / shape.m_area;
114 else
115 shape.m_rcp_area = FP<float>().snan();
116
117
118 return shape;
119 }
120
create_rectangle_shape(const AssemblyInstance * assembly_instance,const size_t object_instance_index,const Material * material,const double area,const Vector3d & o,const Vector3d & x,const Vector3d & y,const Vector3d & n)121 EmittingShape EmittingShape::create_rectangle_shape(
122 const AssemblyInstance* assembly_instance,
123 const size_t object_instance_index,
124 const Material* material,
125 const double area,
126 const Vector3d& o,
127 const Vector3d& x,
128 const Vector3d& y,
129 const Vector3d& n)
130 {
131 EmittingShape shape(
132 RectangleShape,
133 assembly_instance,
134 object_instance_index,
135 0,
136 material);
137
138 shape.m_geom.m_rectangle.m_origin = o;
139 shape.m_geom.m_rectangle.m_x = x;
140 shape.m_geom.m_rectangle.m_y = y;
141 shape.m_geom.m_rectangle.m_width = norm(x);
142 shape.m_geom.m_rectangle.m_height = norm(y);
143 shape.m_geom.m_rectangle.m_geometric_normal = n;
144 shape.m_geom.m_rectangle.m_plane_dist = -dot(o, n);
145
146 shape.m_area = static_cast<float>(area);
147
148 if (shape.m_area != 0.0f)
149 shape.m_rcp_area = 1.0f / shape.m_area;
150 else
151 shape.m_rcp_area = FP<float>().snan();
152
153 return shape;
154 }
155
create_sphere_shape(const AssemblyInstance * assembly_instance,const size_t object_instance_index,const Material * material,const double area,const Vector3d & center,const double radius)156 EmittingShape EmittingShape::create_sphere_shape(
157 const AssemblyInstance* assembly_instance,
158 const size_t object_instance_index,
159 const Material* material,
160 const double area,
161 const Vector3d& center,
162 const double radius)
163 {
164 EmittingShape shape(
165 SphereShape,
166 assembly_instance,
167 object_instance_index,
168 0,
169 material);
170
171 shape.m_geom.m_sphere.m_center = center;
172 shape.m_geom.m_sphere.m_radius = radius;
173
174 shape.m_area = static_cast<float>(area);
175
176 if (shape.m_area != 0.0f)
177 shape.m_rcp_area = 1.0f / shape.m_area;
178 else
179 shape.m_rcp_area = FP<float>().snan();
180
181 return shape;
182 }
183
create_disk_shape(const AssemblyInstance * assembly_instance,const size_t object_instance_index,const Material * material,const double area,const Vector3d & c,const double r,const Vector3d & n,const Vector3d & x,const Vector3d & y)184 EmittingShape EmittingShape::create_disk_shape(
185 const AssemblyInstance* assembly_instance,
186 const size_t object_instance_index,
187 const Material* material,
188 const double area,
189 const Vector3d& c,
190 const double r,
191 const Vector3d& n,
192 const Vector3d& x,
193 const Vector3d& y)
194 {
195 EmittingShape shape(
196 DiskShape,
197 assembly_instance,
198 object_instance_index,
199 0,
200 material);
201
202 shape.m_geom.m_disk.m_center = c;
203 shape.m_geom.m_disk.m_radius = r;
204 shape.m_geom.m_disk.m_geometric_normal = n;
205 shape.m_geom.m_disk.m_x = x;
206 shape.m_geom.m_disk.m_y = y;
207
208 shape.m_area = static_cast<float>(area);
209
210 if (shape.m_area != 0.0f)
211 shape.m_rcp_area = 1.0f / shape.m_area;
212 else
213 shape.m_rcp_area = FP<float>().snan();
214
215 return shape;
216 }
217
EmittingShape(const ShapeType shape_type,const AssemblyInstance * assembly_instance,const size_t object_instance_index,const size_t primitive_index,const Material * material)218 EmittingShape::EmittingShape(
219 const ShapeType shape_type,
220 const AssemblyInstance* assembly_instance,
221 const size_t object_instance_index,
222 const size_t primitive_index,
223 const Material* material)
224 {
225 m_assembly_instance_and_type.set(
226 assembly_instance,
227 static_cast<foundation::uint16>(shape_type));
228
229 m_object_instance_index = object_instance_index;
230 m_primitive_index = primitive_index;
231 m_material = material;
232 m_shape_prob = 0.0f;
233 m_average_flux = 1.0f;
234 }
235
sample_uniform(const Vector2f & s,const float shape_prob,LightSample & light_sample) const236 void EmittingShape::sample_uniform(
237 const Vector2f& s,
238 const float shape_prob,
239 LightSample& light_sample) const
240 {
241 // Store a pointer to the emitting shape.
242 light_sample.m_shape = this;
243
244 const auto shape_type = get_shape_type();
245
246 if (shape_type == TriangleShape)
247 {
248 // Uniformly sample the surface of the shape.
249 const Vector3d bary = sample_triangle_uniform(Vector2d(s));
250
251 // Set the parametric coordinates.
252 light_sample.m_param_coords[0] = static_cast<float>(bary[0]);
253 light_sample.m_param_coords[1] = static_cast<float>(bary[1]);
254
255 // Compute the world space position of the sample.
256 light_sample.m_point =
257 bary[0] * m_geom.m_triangle.m_v0
258 + bary[1] * m_geom.m_triangle.m_v1
259 + bary[2] * m_geom.m_triangle.m_v2;
260
261 // Compute the world space shading normal at the position of the sample.
262 light_sample.m_shading_normal =
263 bary[0] * m_geom.m_triangle.m_n0
264 + bary[1] * m_geom.m_triangle.m_n1
265 + bary[2] * m_geom.m_triangle.m_n2;
266 light_sample.m_shading_normal = normalize(light_sample.m_shading_normal);
267
268 // Set the world space geometric normal.
269 light_sample.m_geometric_normal = m_geom.m_triangle.m_geometric_normal;
270 }
271 else if (shape_type == RectangleShape)
272 {
273 // Set the parametric coordinates.
274 light_sample.m_param_coords = s;
275
276 light_sample.m_point =
277 m_geom.m_rectangle.m_origin +
278 static_cast<double>(s[0]) * m_geom.m_rectangle.m_x +
279 static_cast<double>(s[1]) * m_geom.m_rectangle.m_y;
280
281 // Set the world space shading and geometric normals.
282 light_sample.m_shading_normal = m_geom.m_rectangle.m_geometric_normal;
283 light_sample.m_geometric_normal = m_geom.m_rectangle.m_geometric_normal;
284 }
285 else if (shape_type == SphereShape)
286 {
287 // Set the parametric coordinates.
288 light_sample.m_param_coords = s;
289
290 Vector3d n(sample_sphere_uniform(s));
291
292 // Set the world space shading and geometric normals.
293 light_sample.m_shading_normal = n;
294 light_sample.m_geometric_normal = n;
295
296 // Compute the world space position of the sample.
297 light_sample.m_point = m_geom.m_sphere.m_center + n * m_geom.m_sphere.m_radius;
298 }
299 else if (shape_type == DiskShape)
300 {
301 const Vector2f param_coords = sample_disk_uniform(s);
302
303 // Compute the world space position of the sample.
304 light_sample.m_point =
305 m_geom.m_disk.m_center +
306 static_cast<double>(param_coords[0]) * m_geom.m_disk.m_x +
307 static_cast<double>(param_coords[1]) * m_geom.m_disk.m_y;
308
309 // Set the parametric coordinates.
310 light_sample.m_param_coords = param_coords;
311
312 // Set the world space shading and geometric normals.
313 light_sample.m_shading_normal = m_geom.m_disk.m_geometric_normal;
314 light_sample.m_geometric_normal = m_geom.m_disk.m_geometric_normal;
315 }
316 else
317 {
318 assert(false && "Unknown emitter shape type");
319 }
320
321 // Compute the probability density of this sample.
322 light_sample.m_probability = shape_prob * get_rcp_area();
323 }
324
evaluate_pdf_uniform() const325 float EmittingShape::evaluate_pdf_uniform() const
326 {
327 return get_shape_prob() * get_rcp_area();
328 }
329
make_shading_point(ShadingPoint & shading_point,const Vector3d & point,const Vector3d & direction,const Vector2f & param_coords,const Intersector & intersector) const330 void EmittingShape::make_shading_point(
331 ShadingPoint& shading_point,
332 const Vector3d& point,
333 const Vector3d& direction,
334 const Vector2f& param_coords,
335 const Intersector& intersector) const
336 {
337 const ShadingRay ray(
338 point,
339 direction,
340 0.0,
341 0.0,
342 ShadingRay::Time(),
343 VisibilityFlags::CameraRay, 0);
344
345 const auto shape_type = get_shape_type();
346
347 if (shape_type == TriangleShape)
348 {
349 intersector.make_triangle_shading_point(
350 shading_point,
351 ray,
352 param_coords,
353 get_assembly_instance(),
354 get_assembly_instance()->transform_sequence().get_earliest_transform(),
355 get_object_instance_index(),
356 get_primitive_index(),
357 m_shape_support_plane);
358 }
359 else if (shape_type == RectangleShape)
360 {
361 const Vector3d p =
362 m_geom.m_rectangle.m_origin +
363 static_cast<double>(param_coords[0]) * m_geom.m_rectangle.m_x +
364 static_cast<double>(param_coords[1]) * m_geom.m_rectangle.m_y;
365
366 intersector.make_procedural_surface_shading_point(
367 shading_point,
368 ray,
369 param_coords,
370 get_assembly_instance(),
371 get_assembly_instance()->transform_sequence().get_earliest_transform(),
372 get_object_instance_index(),
373 get_primitive_index(),
374 p,
375 m_geom.m_rectangle.m_geometric_normal,
376 m_geom.m_rectangle.m_x,
377 cross(m_geom.m_rectangle.m_x, m_geom.m_rectangle.m_geometric_normal));
378 }
379 else if (shape_type == SphereShape)
380 {
381 const double theta = static_cast<double>(param_coords[0]);
382 const double phi = static_cast<double>(param_coords[1]);
383
384 const Vector3d n = Vector3d::make_unit_vector(theta, phi);
385 const Vector3d p = m_geom.m_sphere.m_center + m_geom.m_sphere.m_radius * n;
386
387 const Vector3d dpdu(-TwoPi<double>() * n.y, TwoPi<double>() + n.x, 0.0);
388 const Vector3d dpdv = cross(dpdu, n);
389
390 intersector.make_procedural_surface_shading_point(
391 shading_point,
392 ray,
393 param_coords,
394 get_assembly_instance(),
395 get_assembly_instance()->transform_sequence().get_earliest_transform(),
396 get_object_instance_index(),
397 get_primitive_index(),
398 p,
399 n,
400 dpdu,
401 dpdv);
402 }
403 else if (shape_type == DiskShape)
404 {
405 const Vector3d p =
406 m_geom.m_disk.m_center +
407 static_cast<double>(param_coords[0]) * m_geom.m_disk.m_x +
408 static_cast<double>(param_coords[1]) * m_geom.m_disk.m_y;
409
410 intersector.make_procedural_surface_shading_point(
411 shading_point,
412 ray,
413 param_coords,
414 get_assembly_instance(),
415 get_assembly_instance()->transform_sequence().get_earliest_transform(),
416 get_object_instance_index(),
417 get_primitive_index(),
418 p,
419 m_geom.m_disk.m_geometric_normal,
420 m_geom.m_disk.m_x,
421 cross(m_geom.m_disk.m_x, m_geom.m_disk.m_geometric_normal));
422 }
423 else
424 {
425 assert(false && "Unknown emitter shape type");
426 }
427 }
428
estimate_flux()429 void EmittingShape::estimate_flux()
430 {
431 // todo:
432 /*
433 if (constant EDF)
434 return EDF->radiance();
435
436 // Varying EDF or OSL emission case.
437 for i = 0..N:
438 {
439 s = random2d()
440 make_shading_point(shading_point, p, d, s, intersector);
441 radiance += eval EDF or ShaderGroup
442 }
443
444 radiance /= N;
445 return radiance;
446 */
447
448 m_average_flux = 1.0f;
449 m_max_flux = 1.0f;
450 }
451
452 } // namespace renderer
453