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