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