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) 2010-2013 Francois Beaune, Jupiter Jazz Limited
9 // Copyright (c) 2014-2018 Francois Beaune, The appleseedhq Organization
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining a copy
12 // of this software and associated documentation files (the "Software"), to deal
13 // in the Software without restriction, including without limitation the rights
14 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 // copies of the Software, and to permit persons to whom the Software is
16 // furnished to do so, subject to the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be included in
19 // all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 // THE SOFTWARE.
28 //
29 
30 #pragma once
31 
32 // appleseed.foundation headers.
33 #include "foundation/math/scalar.h"
34 #include "foundation/math/sphericaltriangle.h"
35 #include "foundation/math/vector.h"
36 
37 // Standard headers.
38 #include <cassert>
39 #include <cmath>
40 #include <cstddef>
41 
42 namespace foundation
43 {
44 
45 //
46 // References:
47 //
48 //   http://www.cs.kuleuven.ac.be/~phil/GI/TotalCompendium.pdf
49 //   http://jgt.akpeters.com/papers/ShirleyChiu97/
50 //   http://psgraphics.blogspot.com/2011/01/improved-code-for-concentric-map.html
51 //
52 
53 
54 //
55 // Sphere sampling functions.
56 //
57 
58 // Map a uniform sample in [0,1)^2 to a direction over the unit sphere
59 // with a uniform probability density p(theta) = 1/(4*Pi).
60 template <typename T>
61 Vector<T, 3> sample_sphere_uniform(const Vector<T, 2>& s);
62 
63 
64 //
65 // Hemisphere sampling functions.
66 //
67 
68 // Map a uniform sample in [0,1)^2 to a direction over the unit hemisphere
69 // with a uniform probability density p(theta) = 1/(2*Pi).
70 template <typename T>
71 Vector<T, 3> sample_hemisphere_uniform(const Vector<T, 2>& s);
72 
73 // Map a uniform sample in [0,1)^2 to a direction over the unit hemisphere
74 // with a cosine-weighted probability density p(theta) = cos(theta)/Pi.
75 template <typename T>
76 Vector<T, 3> sample_hemisphere_cosine(const Vector<T, 2>& s);
77 
78 // Map a uniform sample in [0,1)^2 to a direction over the unit hemisphere
79 // with a cosine lobe probability density p(theta) = (n+1)/(2*Pi)*cos(theta)^n.
80 template <typename T>
81 Vector<T, 3> sample_hemisphere_cosine_power(const Vector<T, 2>& s, const T n);
82 template <typename T>
83 T sample_hemisphere_cosine_power_pdf(const T cos_theta, const T n);
84 
85 
86 //
87 // Disk sampling functions.
88 //
89 
90 // Map a uniform sample in [0,1)^2 to a point on the surface of the unit disk
91 // with a uniform probability density p(x) = 1/Pi.
92 template <typename T>
93 Vector<T, 2> sample_disk_uniform(const Vector<T, 2>& s);
94 
95 // An alternate implementation of foundation::sample_disk_uniform() based on
96 // a polar parameterization of the unit disk. This implementation is about
97 // twice as fast as foundation::sample_disk_uniform() but has high distortion
98 // around the center of the disk. In practice it is fine for random or even
99 // QMC sampling.
100 template <typename T>
101 Vector<T, 2> sample_disk_uniform_alt(const Vector<T, 2>& s);
102 
103 // Map a uniform random sample in [0,1) to a radius on the unit disk such
104 // that samples follow a 2D Gaussian distribution with probability density
105 //   pdf(x, y) = 4*a/Pi*exp(-a*(x^2+y^2))
106 //   pdf(r, theta) = r*pdf(x, y) = a/Pi*r*exp(-a*r^2)
107 template <typename T>
108 T sample_disk_gaussian(
109     const T s,
110     const T a);
111 template <typename T>
112 T sample_disk_gaussian_polar_pdf(
113     const T r,
114     const T a);
115 template <typename T>
116 T sample_disk_gaussian_area_pdf(
117     const T r,
118     const T a);
119 
120 
121 //
122 // Other sampling functions.
123 //
124 
125 // Map a uniform sample in [0,1)^2 to a direction in the cone with its apex at
126 // the origin and extending toward Y+.
127 template <typename T>
128 Vector<T, 3> sample_cone_uniform(const Vector<T, 2>& s, const T cos_theta_max);
129 template <typename T>
130 T sample_cone_uniform_pdf(const T cos_theta_max);
131 
132 // Map a uniform sample in [0,1) to a point on the unit circle with a uniform
133 // probability density p(x) = 1/(2*Pi).
134 template <typename T>
135 Vector<T, 2> sample_circle_uniform(const T s);
136 
137 // Map a uniform sample in [0,1)^2 to a point on the surface of a triangle
138 // with a uniform probability density p(x) = 1/A. Return the barycentric
139 // coordinates of the point inside the triangle.
140 template <typename T>
141 Vector<T, 3> sample_triangle_uniform(const Vector<T, 2>& s);
142 
143 // An alternate implementation of sample_triangle_uniform() due to Eric Heitz.
144 // See https://drive.google.com/file/d/1J-183vt4BrN9wmqItECIjjLIKwm29qSg/view
145 // for details.
146 template <typename T>
147 Vector<T, 3> sample_triangle_uniform_heitz(const Vector<T, 2>& s);
148 
149 // Build a regular N-sided polygon to use with sample_regular_polygon_uniform().
150 template <typename T>
151 void build_regular_polygon(
152     const size_t        vertex_count,
153     const T             tilt_angle,
154     Vector<T, 2>        vertices[]);
155 
156 // Map a uniform sample in [0,1)^3 to a point on the surface of a regular
157 // N-sided polygon with a uniform probability density p(x) = 1/A.
158 template <typename T>
159 Vector<T, 2> sample_regular_polygon_uniform(
160     const Vector<T, 3>& s,
161     const size_t        vertex_count,
162     const Vector<T, 2>  vertices[]);
163 
164 // Map a uniform sample in [0,1)^2 to a point on the surface of a regular
165 // N-sided polygon with a uniform probability density p(x) = 1/A.
166 template <typename T>
167 Vector<T, 2> sample_regular_polygon_uniform(
168     const Vector<T, 2>& s,
169     const size_t        vertex_count,
170     const Vector<T, 2>  vertices[]);
171 
172 // Map a uniform sample in [0,1)^2 to a point on the surface of a spherical
173 // triangle on the unit sphere.
174 template <typename T>
175 Vector<T, 3> sample_spherical_triangle_uniform(
176     const Vector<T, 3>& v0,
177     const Vector<T, 3>& v1,
178     const Vector<T, 3>& v2,
179     const Vector<T, 2>& eta);
180 
181 
182 //
183 // Exponential sampling functions.
184 //
185 // Reference:
186 //
187 //   Physically Based Rendering, first edition, page 641.
188 //
189 
190 // Map a uniform random sample in [0,1) to an exponential distribution
191 // of the form exp(-a*x).
192 template <typename T>
193 T sample_exponential_distribution(
194     const T s,
195     const T a);
196 template <typename T>
197 T exponential_distribution_pdf(
198     const T x,
199     const T a);
200 
201 // Map a uniform random sample in [0,1) to an exponential distribution on segment [l, r].
202 // of the form exp(-a*(x - l)) - exp(-a*(x - r)).
203 template <typename T>
204 T sample_exponential_distribution_on_segment(
205     const T s,
206     const T a,
207     const T l,
208     const T r);
209 template <typename T>
210 T exponential_distribution_on_segment_pdf(
211     const T x,
212     const T a,
213     const T l,
214     const T r);
215 
216 
217 //
218 // Equiangular sampling along a ray with respect to a point.
219 //
220 // Reference:
221 //
222 //   Importance Sampling Techniques for Path Tracing in Participating Media
223 //   Christopher Kulla and Marcos Fajardo
224 //   https://www.solidangle.com/research/egsr2012_volume.pdf
225 //
226 
227 template <typename T>
228 T sample_equiangular_distribution(
229     const T u,                      // uniform random sample in [0,1)
230     const T theta_a,                // start angle
231     const T theta_b,                // end angle
232     const T distance);              // distance from point to the ray
233 template <typename T>
234 T equiangular_distribution_pdf(
235     const T t,
236     const T theta_a,                // start angle
237     const T theta_b,                // end angle
238     const T distance);              // distance from point to the ray
239 
240 
241 //
242 // Reciprocal distribution:
243 // p(X) ~ 1 / X, where X belongs to [l, r).
244 //
245 
246 template <typename T>
247 T sample_rcp_distribution(
248     const T s,
249     const T l,
250     const T r);
251 template <typename T>
252 T rcp_distribution_pdf(
253     const T x,
254     const T l,
255     const T r);
256 
257 
258 //
259 // Implementation.
260 //
261 
262 template <typename T>
sample_sphere_uniform(const Vector<T,2> & s)263 inline Vector<T, 3> sample_sphere_uniform(const Vector<T, 2>& s)
264 {
265     assert(s[0] >= T(0.0) && s[0] < T(1.0));
266     assert(s[1] >= T(0.0) && s[1] < T(1.0));
267 
268     const T phi = TwoPi<T>() * s[0];
269     const T cos_theta = T(1.0) - T(2.0) * s[1];
270     const T sin_theta = std::sqrt(T(1.0) - cos_theta * cos_theta);
271 
272     return
273         Vector<T, 3>::make_unit_vector(
274             cos_theta,
275             sin_theta,
276             std::cos(phi),
277             std::sin(phi));
278 }
279 
280 template <typename T>
sample_hemisphere_uniform(const Vector<T,2> & s)281 inline Vector<T, 3> sample_hemisphere_uniform(const Vector<T, 2>& s)
282 {
283     assert(s[0] >= T(0.0) && s[0] < T(1.0));
284     assert(s[1] >= T(0.0) && s[1] < T(1.0));
285 
286     const T phi = TwoPi<T>() * s[0];
287     const T cos_theta = T(1.0) - s[1];
288     const T sin_theta = std::sqrt(T(1.0) - cos_theta * cos_theta);
289 
290     return
291         Vector<T, 3>::make_unit_vector(
292             cos_theta,
293             sin_theta,
294             std::cos(phi),
295             std::sin(phi));
296 }
297 
298 template <typename T>
sample_hemisphere_cosine(const Vector<T,2> & s)299 inline Vector<T, 3> sample_hemisphere_cosine(const Vector<T, 2>& s)
300 {
301     assert(s[0] >= T(0.0) && s[0] < T(1.0));
302     assert(s[1] >= T(0.0) && s[1] < T(1.0));
303 
304     const T phi = TwoPi<T>() * s[0];
305     const T cos_theta = std::sqrt(T(1.0) - s[1]);
306     const T sin_theta = std::sqrt(s[1]);
307 
308     return
309         Vector<T, 3>::make_unit_vector(
310             cos_theta,
311             sin_theta,
312             std::cos(phi),
313             std::sin(phi));
314 }
315 
316 template <typename T>
sample_hemisphere_cosine_power(const Vector<T,2> & s,const T n)317 inline Vector<T, 3> sample_hemisphere_cosine_power(const Vector<T, 2>& s, const T n)
318 {
319     assert(s[0] >= T(0.0) && s[0] < T(1.0));
320     assert(s[1] >= T(0.0) && s[1] < T(1.0));
321     assert(n >= T(0.0));
322 
323     const T phi = TwoPi<T>() * s[0];
324     const T cos_theta = std::pow(T(1.0) - s[1], T(1.0) / (n + T(1.0)));
325     const T sin_theta = std::sqrt(T(1.0) - cos_theta * cos_theta);
326 
327     return
328         Vector<T, 3>::make_unit_vector(
329             cos_theta,
330             sin_theta,
331             std::cos(phi),
332             std::sin(phi));
333 }
334 
335 template <typename T>
sample_hemisphere_cosine_power_pdf(const T cos_theta,const T n)336 inline T sample_hemisphere_cosine_power_pdf(const T cos_theta, const T n)
337 {
338     return (n + T(1.0)) * RcpTwoPi<T>() * std::pow(cos_theta, n);
339 }
340 
341 template <typename T>
sample_disk_uniform(const Vector<T,2> & s)342 inline Vector<T, 2> sample_disk_uniform(const Vector<T, 2>& s)
343 {
344     assert(s[0] >= T(0.0) && s[0] < T(1.0));
345     assert(s[1] >= T(0.0) && s[1] < T(1.0));
346 
347     const T a = T(2.0) * s[0] - T(1.0);
348     const T b = T(2.0) * s[1] - T(1.0);
349 
350     T phi, r;
351 
352     if (a * a > b * b)
353     {
354         r = a;
355         phi = PiOverFour<T>() * (b / a);
356     }
357     else if (b != T(0.0))
358     {
359         r = b;
360         phi = HalfPi<T>() - PiOverFour<T>() * (a / b);
361     }
362     else return Vector<T, 2>(0.0);
363 
364     return Vector<T, 2>(r * std::cos(phi), r * std::sin(phi));
365 }
366 
367 template <typename T>
sample_disk_uniform_alt(const Vector<T,2> & s)368 inline Vector<T, 2> sample_disk_uniform_alt(const Vector<T, 2>& s)
369 {
370     assert(s[0] >= T(0.0) && s[0] < T(1.0));
371     assert(s[1] >= T(0.0) && s[1] < T(1.0));
372 
373     const T phi = TwoPi<T>() * s[0];
374     const T r = std::sqrt(T(1.0) - s[1]);
375 
376     return r * Vector<T, 2>(std::cos(phi), std::sin(phi));
377 }
378 
379 template <typename T>
sample_disk_gaussian(const T s,const T a)380 inline T sample_disk_gaussian(
381     const T s,
382     const T a)
383 {
384     assert(s >= T(0.0));
385     assert(s < T(1.0));
386 
387     return std::sqrt(-std::log(T(1.0) - s) / a);
388 }
389 
390 template <typename T>
sample_disk_gaussian_polar_pdf(const T r,const T a)391 inline T sample_disk_gaussian_polar_pdf(
392     const T r,
393     const T a)
394 {
395     assert(r >= T(0.0));
396 
397     return a * RcpPi<T>() * r * std::exp(-a * r * r);
398 }
399 
400 template <typename T>
sample_disk_gaussian_area_pdf(const T r,const T a)401 inline T sample_disk_gaussian_area_pdf(
402     const T r,
403     const T a)
404 {
405     assert(r >= T(0.0));
406 
407     return a * FourOverPi<T>() * std::exp(-a * r * r);
408 }
409 
410 template <typename T>
sample_cone_uniform(const Vector<T,2> & s,const T cos_theta_max)411 inline Vector<T, 3> sample_cone_uniform(const Vector<T, 2>& s, const T cos_theta_max)
412 {
413     assert(s[0] >= T(0.0) && s[0] < T(1.0));
414     assert(s[1] >= T(0.0) && s[1] < T(1.0));
415 
416     const T phi = TwoPi<T>() * s[0];
417     const T cos_theta = lerp(T(1.0), cos_theta_max, s[1]);
418     const T sin_theta = std::sqrt(T(1.0) - cos_theta * cos_theta);
419 
420     return
421         Vector<T, 3>::make_unit_vector(
422             cos_theta,
423             sin_theta,
424             std::cos(phi),
425             std::sin(phi));
426 }
427 
428 template <typename T>
sample_cone_uniform_pdf(const T cos_theta_max)429 inline T sample_cone_uniform_pdf(const T cos_theta_max)
430 {
431     return T(1.0) / (TwoPi<T>() * (T(1.0) - cos_theta_max));
432 }
433 
434 template <typename T>
sample_circle_uniform(const T s)435 inline Vector<T, 2> sample_circle_uniform(const T s)
436 {
437     assert(s >= T(0.0) && s < T(1.0));
438 
439     const T phi = s * TwoPi<T>();
440 
441     return Vector<T, 2>(std::cos(phi), std::sin(phi));
442 }
443 
444 template <typename T>
sample_triangle_uniform(const Vector<T,2> & s)445 inline Vector<T, 3> sample_triangle_uniform(const Vector<T, 2>& s)
446 {
447     assert(s[0] >= T(0.0) && s[0] < T(1.0));
448     assert(s[1] >= T(0.0) && s[1] < T(1.0));
449 
450     const T sqrt_s0 = std::sqrt(s[0]);
451 
452     Vector<T, 3> b;
453     b[0] = T(1.0) - sqrt_s0;
454     b[1] = (T(1.0) - s[1]) * sqrt_s0;
455     b[2] = T(1.0) - b[0] - b[1];
456 
457     assert(feq(b[0] + b[1] + b[2], T(1.0)));
458 
459     return b;
460 }
461 
462 template <typename T>
sample_triangle_uniform_heitz(const Vector<T,2> & s)463 inline Vector<T, 3> sample_triangle_uniform_heitz(const Vector<T, 2>& s)
464 {
465     assert(s[0] >= T(0.0) && s[0] < T(1.0));
466     assert(s[1] >= T(0.0) && s[1] < T(1.0));
467 
468     T t0 = T(0.5) * s[0];
469     T t1 = T(0.5) * s[1];
470 
471     const T offset = t1 - t0;
472 
473     if (offset > T(0.0))
474         t1 += offset;
475     else t0 -= offset;
476 
477     Vector<T, 3> b;
478     b[0] = t0;
479     b[1] = t1;
480     b[2] = T(1.0) - b[0] - b[1];
481 
482     assert(feq(b[0] + b[1] + b[2], T(1.0)));
483 
484     return b;
485 }
486 
487 template <typename T>
build_regular_polygon(const size_t vertex_count,const T tilt_angle,Vector<T,2> vertices[])488 void build_regular_polygon(
489     const size_t        vertex_count,
490     const T             tilt_angle,
491     Vector<T, 2>        vertices[])
492 {
493     assert(vertex_count >= 3);
494 
495     for (size_t i = 0; i < vertex_count; ++i)
496     {
497         const T a = (i * TwoPi<T>()) / vertex_count + tilt_angle;
498         vertices[i] = Vector<T, 2>(std::cos(a), std::sin(a));
499     }
500 }
501 
502 template <typename T>
sample_regular_polygon_uniform(const Vector<T,3> & s,const size_t vertex_count,const Vector<T,2> vertices[])503 inline Vector<T, 2> sample_regular_polygon_uniform(
504     const Vector<T, 3>& s,
505     const size_t        vertex_count,
506     const Vector<T, 2>  vertices[])
507 {
508     assert(s[0] >= T(0.0) && s[0] < T(1.0));
509     assert(s[1] >= T(0.0) && s[1] < T(1.0));
510     assert(s[2] >= T(0.0) && s[2] < T(1.0));
511     assert(vertex_count >= 3);
512 
513     const size_t v0_index = truncate<size_t>(s[0] * vertex_count);
514 
515     size_t v1_index = v0_index + 1;
516     if (v1_index == vertex_count)
517         v1_index = 0;
518 
519     const Vector<T, 2>& v0 = vertices[v0_index];
520     const Vector<T, 2>& v1 = vertices[v1_index];
521 
522     T t0 = T(0.5) * s[1];
523     T t1 = T(0.5) * s[2];
524 
525     const T offset = t1 - t0;
526 
527     if (offset > T(0.0))
528         t1 += offset;
529     else t0 -= offset;
530 
531     return t0 * v0 + t1 * v1;
532 }
533 
534 template <typename T>
sample_regular_polygon_uniform(const Vector<T,2> & s,const size_t vertex_count,const Vector<T,2> vertices[])535 inline Vector<T, 2> sample_regular_polygon_uniform(
536     const Vector<T, 2>& s,
537     const size_t        vertex_count,
538     const Vector<T, 2>  vertices[])
539 {
540     assert(s[0] >= T(0.0) && s[0] < T(1.0));
541     assert(s[1] >= T(0.0) && s[1] < T(1.0));
542     assert(vertex_count >= 3);
543 
544     const size_t v0_index = truncate<size_t>(s[0] * vertex_count);
545     const T rescaled_s0 = s[0] * vertex_count - v0_index;
546     assert(rescaled_s0 >= T(0.0) && rescaled_s0 < T(1.0));
547 
548     size_t v1_index = v0_index + 1;
549     if (v1_index == vertex_count)
550         v1_index = 0;
551 
552     const Vector<T, 2>& v0 = vertices[v0_index];
553     const Vector<T, 2>& v1 = vertices[v1_index];
554 
555     T t0 = T(0.5) * rescaled_s0;
556     T t1 = T(0.5) * s[1];
557 
558     const T offset = t1 - t0;
559 
560     if (offset > T(0.0))
561         t1 += offset;
562     else t0 -= offset;
563 
564     return t0 * v0 + t1 * v1;
565 }
566 
567 template <typename T>
sample_spherical_triangle_uniform(const Vector<T,3> & A,const Vector<T,3> & B,const Vector<T,3> & C,const Vector<T,2> & eta)568 Vector<T, 3> sample_spherical_triangle_uniform(
569     const Vector<T, 3>& A,
570     const Vector<T, 3>& B,
571     const Vector<T, 3>& C,
572     const Vector<T, 2>& eta)
573 {
574     // Compute the arc lengths of the sides of the spherical triangle.
575     T a, b, c;
576     compute_spherical_triangle_edge_lengths(A, B, C, a, b, c);
577 
578     // Compute the interior angles of the spherical triangle.
579     T alpha, beta, gamma;
580     compute_spherical_triangle_interior_angles(a, b, c, alpha, beta, gamma);
581     const T cos_alpha = std::cos(alpha);
582     const T sin_alpha = std::sin(alpha);
583 
584     // Compute the area of the spherical triangle.
585     const T area = compute_spherical_triangle_area(alpha, beta, gamma);
586 
587     // Use one random variable to select the new area.
588     const T area_hat = eta[0] * area;
589 
590     // Save the sine and cosine of the angle phi.
591     const T s = std::sin(area_hat - alpha);
592     const T t = std::cos(area_hat - alpha);
593 
594     // Compute the pair (u, v) that determines beta_hat.
595     const T u = t - cos_alpha;
596     const T v = s + sin_alpha * std::cos(c);
597 
598     // Let q be the cosine of the new edge length b_hat.
599     const T q_num = (v * t - u * s) * cos_alpha - v;
600     const T q_den = (v * s + u * t) * sin_alpha;
601     const T q = clamp(q_num / q_den, T(-1.0), T(1.0));
602 
603     // Compute the third vertex of the sub-triangle.
604     const Vector<T, 3> C_hat = q * A + std::sqrt(T(1.0) - q * q) * normalize(C - dot(C, A) * A);
605     const T dot_C_hat_B = dot(C_hat, B);
606 
607     // Use the other random variable to select cos(theta).
608     const T z = T(1.0) - eta[1] * (T(1.0) - dot_C_hat_B);
609 
610     // Construct the corresponding point on the sphere.
611     return z * B + std::sqrt(T(1.0) - z * z) * normalize(C_hat - dot_C_hat_B * B);
612 }
613 
614 template <typename T>
sample_exponential_distribution(const T s,const T a)615 inline T sample_exponential_distribution(
616     const T s,
617     const T a)
618 {
619     assert(s >= T(0.0));
620     assert(s < T(1.0));
621 
622     return -std::log(T(1.0) - s) / a;
623 }
624 
625 template <typename T>
exponential_distribution_pdf(const T x,const T a)626 inline T exponential_distribution_pdf(
627     const T x,
628     const T a)
629 {
630     assert(x >= T(0.0));
631 
632     return a * std::exp(-a * x);
633 }
634 
635 template <typename T>
sample_exponential_distribution_on_segment(const T s,const T a,const T l,const T r)636 inline T sample_exponential_distribution_on_segment(
637     const T s,
638     const T a,
639     const T l,
640     const T r)
641 {
642     assert(s >= T(0.0));
643     assert(s < T(1.0));
644     assert(a > T(0.0));
645 
646     const T tail = T(1.0) - std::exp(-(r - l) * a);
647     return clamp(l - std::log(T(1.0) - s * tail) / a, l, r);
648 }
649 
650 template <typename T>
exponential_distribution_on_segment_pdf(const T x,const T a,const T l,const T r)651 inline T exponential_distribution_on_segment_pdf(
652     const T x,
653     const T a,
654     const T l,
655     const T r)
656 {
657     assert(x >= l);
658     assert(x <= r);
659 
660     const T left = std::exp(a * (x - l));
661     const T right = std::exp(a * (x - r));
662     return a / (left - right);
663 }
664 
665 template <typename T>
sample_equiangular_distribution(const T s,const T theta_a,const T theta_b,const T distance)666 inline T sample_equiangular_distribution(
667     const T s,
668     const T theta_a,
669     const T theta_b,
670     const T distance)
671 {
672     assert(s >= T(0.0));
673     assert(s < T(1.0));
674 
675     return distance * std::tan(lerp(theta_a, theta_b, s));
676 }
677 
678 template <typename T>
equiangular_distribution_pdf(const T t,const T theta_a,const T theta_b,const T distance)679 inline T equiangular_distribution_pdf(
680     const T t,
681     const T theta_a,
682     const T theta_b,
683     const T distance)
684 {
685     return distance / ((theta_b - theta_a) * (square(distance) + square(t)));
686 }
687 
688 template <typename T>
sample_rcp_distribution(const T s,const T l,const T r)689 inline T sample_rcp_distribution(
690     const T s,
691     const T l,
692     const T r)
693 {
694     assert(l > T(0.0));
695     assert(r > l);
696 
697     const T d = r / l;
698     return
699         d < T(1.01)
700             ? l + s * (r - l)
701             : clamp(l * std::pow(d, s), l, r);
702 }
703 
704 template <typename T>
rcp_distribution_pdf(const T x,const T l,const T r)705 inline T rcp_distribution_pdf(
706     const T x,
707     const T l,
708     const T r)
709 {
710     assert(l > T(0.0));
711     assert(x >= l);
712     assert(x < r);
713 
714     const T d = r / l;
715     return
716         d < T(1.01)
717             ? rcp(r - l)
718             : rcp(x * std::log(d));
719 }
720 
721 }   // namespace foundation
722