Blender V5.0
sample/mapping.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
2 * SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 * Adapted code from Open Shading Language. */
7
8#pragma once
9
10#include "util/math.h"
11#include "util/projection.h"
12
14
15/* Distribute 2D uniform random samples on [0, 1] over unit disk [-1, 1], with concentric mapping
16 * to better preserve stratification for some RNG sequences. */
18{
19 float phi;
20 float r;
21 const float a = 2.0f * rand.x - 1.0f;
22 const float b = 2.0f * rand.y - 1.0f;
23
24 if (a == 0.0f && b == 0.0f) {
25 return zero_float2();
26 }
27
28 if (a * a > b * b) {
29 r = a;
30 phi = M_PI_4_F * (b / a);
31 }
32 else {
33 r = b;
34 phi = M_PI_2_F - M_PI_4_F * (a / b);
35 }
36
37 return polar_to_cartesian(r, phi);
38}
39
40/* return an orthogonal tangent and bitangent given a normal and tangent that
41 * may not be exactly orthogonal */
43 const float3 T,
46{
47 *b = normalize(cross(N, T));
48 *a = cross(*b, N);
49}
50
52 const float3 T,
55{
56 *b = safe_normalize(cross(N, T));
57 if (len_squared(*b) < 0.99f) {
58 /* Normalization failed, so fall back to basic orthonormals. */
60 }
61 else {
62 *a = cross(*b, N);
63 }
64}
65
66/* sample direction with cosine weighted distributed in hemisphere */
68 const float2 rand_in,
70 ccl_private float *pdf)
71{
72 const float2 rand = sample_uniform_disk(rand_in);
73 const float costheta = safe_sqrtf(1.0f - len_squared(rand));
74
75 float3 T;
76 float3 B;
78 *wo = rand.x * T + rand.y * B + costheta * N;
79 *pdf = costheta * M_1_PI_F;
80}
81
83{
84 const float cos_theta = dot(N, D);
85 return cos_theta > 0 ? cos_theta * M_1_PI_F : 0.0f;
86}
87
88/* sample direction uniformly distributed in hemisphere */
90 const float2 rand,
92 ccl_private float *pdf)
93{
95 const float z = 1.0f - len_squared(xy);
96
97 xy *= safe_sqrtf(z + 1.0f);
98
99 float3 T;
100 float3 B;
101 make_orthonormals(N, &T, &B);
102
103 *wo = xy.x * T + xy.y * B + z * N;
104 *pdf = M_1_2PI_F;
105}
106
107ccl_device_inline float pdf_uniform_cone(const float3 N, const float3 D, const float angle)
108{
109 const float z = precise_angle(N, D);
110 if (z < angle) {
111 return M_1_2PI_F / one_minus_cos(angle);
112 }
113 return 0.0f;
114}
115
116/* Uniformly sample a direction in a cone of given angle around `N`. Use concentric mapping to
117 * better preserve stratification. Return the angle between `N` and the sampled direction as
118 * `cos_theta`.
119 * Pass `1 - cos(angle)` as argument instead of `angle` to alleviate precision issues at small
120 * angles (see sphere light for reference). */
122 const float one_minus_cos_angle,
123 const float2 rand,
124 ccl_private float *cos_theta,
125 ccl_private float *pdf)
126{
127 if (one_minus_cos_angle > 0) {
128 /* Remap radius to get a uniform distribution w.r.t. solid angle on the cone.
129 * The logic to derive this mapping is as follows:
130 *
131 * Sampling a cone is comparable to sampling the hemisphere, we just restrict theta. Therefore,
132 * the same trick of first sampling the unit disk and the projecting the result up towards the
133 * hemisphere by calculating the appropriate z coordinate still works.
134 *
135 * However, by itself this results in cosine-weighted hemisphere sampling, so we need some kind
136 * of remapping. Cosine-weighted hemisphere and uniform cone sampling have the same conditional
137 * PDF for phi (both are constant), so we only need to think about theta, which corresponds
138 * directly to the radius.
139 *
140 * To find this mapping, we consider the simplest sampling strategies for cosine-weighted
141 * hemispheres and uniform cones. In both, phi is chosen as `2pi * random()`. For the former,
142 * `r_disk(rand) = sqrt(rand)`. This is just naive disk sampling, since the projection to the
143 * hemisphere doesn't change the radius.
144 * For the latter, `r_cone(rand) = sin_from_cos(mix(cos_angle, 1, rand))`.
145 *
146 * So, to remap, we just invert r_disk `(-> rand(r_disk) = r_disk^2)` and insert it into
147 * r_cone: `r_cone(r_disk) = r_cone(rand(r_disk)) = sin_from_cos(mix(cos_angle, 1, r_disk^2))`.
148 * In practice, we need to replace `rand` with `1 - rand` to preserve the stratification,
149 * but since it's uniform, that's fine. */
151 const float r2 = len_squared(xy);
152
153 /* Equivalent to `mix(cos_angle, 1.0f, 1.0f - r2)`. */
154 *cos_theta = 1.0f - r2 * one_minus_cos_angle;
155
156 /* Remap disk radius to cone radius, equivalent to `xy *= sin_theta / sqrt(r2)`. */
157 xy *= safe_sqrtf(one_minus_cos_angle * (2.0f - one_minus_cos_angle * r2));
158
159 *pdf = M_1_2PI_F / one_minus_cos_angle;
160
161 float3 T;
162 float3 B;
163 make_orthonormals(N, &T, &B);
164 return xy.x * T + xy.y * B + *cos_theta * N;
165 }
166
167 *cos_theta = 1.0f;
168 *pdf = 1.0f;
169
170 return N;
171}
172
173/* sample uniform point on the surface of a sphere */
175{
176 const float z = 1.0f - 2.0f * rand.x;
177 const float r = sin_from_cos(z);
178 const float phi = M_2PI_F * rand.y;
179
180 return make_float3(polar_to_cartesian(r, phi), z);
181}
182
183/* sample point in unit polygon with given number of corners and rotation */
184ccl_device float2 regular_polygon_sample(const float corners, float rotation, const float2 rand)
185{
186 float u = rand.x;
187 float v = rand.y;
188
189 /* sample corner number and reuse u */
190 const float corner = floorf(u * corners);
191 u = u * corners - corner;
192
193 /* uniform sampled triangle weights */
194 u = sqrtf(u);
195 v = v * u;
196 u = 1.0f - u;
197
198 /* point in triangle */
199 const float angle = M_PI_F / corners;
200 const float2 p = make_float2((u + v) * cosf(angle), (u - v) * sinf(angle));
201
202 /* rotate */
203 rotation += corner * 2.0f * angle;
204
205 const float cr = cosf(rotation);
206 const float sr = sinf(rotation);
207
208 return make_float2(cr * p.x - sr * p.y, sr * p.x + cr * p.y);
209}
210
211/* Generate random variable x following geometric distribution p(x) = r * (1 - r)^x, 0 <= p <= 1.
212 * Also compute the probability mass function pmf.
213 * The sampled order is truncated at `cut_off`. */
215 const float r,
216 ccl_private float &pmf,
217 const int cut_off = INT_MAX)
218{
219 const int n = min(int(floorf(logf(rand) / logf(1.0f - r))), cut_off);
220 pmf = (n == cut_off) ? powf(1.0f - r, n) : r * powf(1.0f - r, n);
221 return n;
222}
223
224/* Generate random variable x following exponential distribution p(x) = lambda * exp(-lambda * x),
225 * where lambda > 0 is the rate parameter. */
226ccl_device_inline float sample_exponential_distribution(const float rand, const float lambda)
227{
228 return -logf(1.0f - rand) / lambda;
229}
230
231/* Generate random variable x following bounded exponential distribution
232 * p(x) = lambda * exp(-lambda * x) / (exp(-lambda * t.min) - exp(-lambda * t.max)),
233 * where lambda > 0 is the rate parameter.
234 * The generated sample lies in (t.min, t.max). */
236 const float lambda,
237 const Interval<float> t)
238{
239 const float attenuation = 1.0f - expf(lambda * (t.min - t.max));
240 return clamp(t.min - logf(1.0f - rand * attenuation) / lambda, t.min, t.max);
241}
242
244 const Spectrum lambda,
245 const Interval<float> t)
246{
247 const Spectrum attenuation = exp(-lambda * t.min) - exp(-lambda * t.max);
248 return safe_divide(lambda * exp(-lambda * clamp(x, t.min, t.max)), attenuation);
249}
250
#define D
MINLINE float safe_sqrtf(float a)
MINLINE float safe_divide(float a, float b)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
ATTR_WARN_UNUSED_RESULT const BMVert * v
ccl_device_inline float cos_theta(const float3 w)
SIMD_FORCE_INLINE const btScalar & z() const
Return the z value.
Definition btQuadWord.h:117
dot(value.rgb, luminance_coefficients)") DEFINE_VALUE("REDUCE(lhs
ccl_device_inline float2 polar_to_cartesian(const float r, const float phi)
#define M_PI_2_F
#define M_PI_4_F
#define ccl_private
#define ccl_device_inline
#define M_1_PI_F
#define M_1_2PI_F
#define logf(x)
#define expf(x)
#define powf(x, y)
#define CCL_NAMESPACE_END
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
#define exp
VecBase< float, D > normalize(VecOp< float, D >) RET
VecBase< float, 3 > cross(VecOp< float, 3 >, VecOp< float, 3 >) RET
constexpr T clamp(T, U, U) RET
ccl_device_inline float sin_from_cos(const float c)
Definition math_base.h:609
ccl_device_inline float one_minus_cos(const float angle)
Definition math_base.h:625
CCL_NAMESPACE_BEGIN ccl_device_inline float2 zero_float2()
Definition math_float2.h:13
ccl_device_inline float len_squared(const float2 a)
ccl_device_inline float2 safe_normalize(const float2 a)
ccl_device_inline float precise_angle(const float3 a, const float3 b)
ccl_device_inline void make_orthonormals(const float3 N, ccl_private float3 *a, ccl_private float3 *b)
#define N
#define T
#define B
#define floorf
#define sqrtf
#define ccl_device
#define M_2PI_F
#define sinf
#define make_float2
#define M_PI_F
#define cosf
ccl_device_inline float pdf_uniform_cone(const float3 N, const float3 D, const float angle)
ccl_device_inline float sample_exponential_distribution(const float rand, const float lambda)
ccl_device_inline void sample_uniform_hemisphere(const float3 N, const float2 rand, ccl_private float3 *wo, ccl_private float *pdf)
CCL_NAMESPACE_BEGIN ccl_device float2 sample_uniform_disk(const float2 rand)
ccl_device_inline void sample_cos_hemisphere(const float3 N, const float2 rand_in, ccl_private float3 *wo, ccl_private float *pdf)
ccl_device float3 sample_uniform_sphere(const float2 rand)
ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, ccl_private float3 *a, ccl_private float3 *b)
ccl_device_inline int sample_geometric_distribution(const float rand, const float r, ccl_private float &pmf, const int cut_off=INT_MAX)
ccl_device_inline float3 sample_uniform_cone(const float3 N, const float one_minus_cos_angle, const float2 rand, ccl_private float *cos_theta, ccl_private float *pdf)
ccl_device_inline float pdf_cos_hemisphere(const float3 N, const float3 D)
ccl_device_inline Spectrum pdf_exponential_distribution(const float x, const Spectrum lambda, const Interval< float > t)
ccl_device void make_orthonormals_safe_tangent(const float3 N, const float3 T, ccl_private float3 *a, ccl_private float3 *b)
ccl_device float2 regular_polygon_sample(const float corners, float rotation, const float2 rand)
#define min(a, b)
Definition sort.cc:36
float x
float y
float3 Spectrum
int xy[2]
Definition wm_draw.cc:178