Blender V5.0
bssrdf.h
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#pragma once
6
7#include "kernel/types.h"
8
11
13
14struct Bssrdf {
16
20
21 /* Parameters for refractive entry bounce. */
22 float ior;
23 float alpha;
24};
25
26static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!");
27
28/* Random Walk BSSRDF */
29
30ccl_device float bssrdf_dipole_compute_Rd(const float alpha_prime, const float fourthirdA)
31{
32 const float s = sqrtf(3.0f * (1.0f - alpha_prime));
33 return 0.5f * alpha_prime * (1.0f + expf(-fourthirdA * s)) * expf(-s);
34}
35
36ccl_device float bssrdf_dipole_compute_alpha_prime(const float rd, const float fourthirdA)
37{
38 /* Little Newton solver. */
39 if (rd < 1e-4f) {
40 return 0.0f;
41 }
42 if (rd >= 0.995f) {
43 return 0.999999f;
44 }
45
46 float x0 = 0.0f;
47 float x1 = 1.0f;
48 float xmid;
49 float fmid;
50
51 constexpr const int max_num_iterations = 12;
52 for (int i = 0; i < max_num_iterations; ++i) {
53 xmid = 0.5f * (x0 + x1);
54 fmid = bssrdf_dipole_compute_Rd(xmid, fourthirdA);
55 if (fmid < rd) {
56 x0 = xmid;
57 }
58 else {
59 x1 = xmid;
60 }
61 }
62
63 return xmid;
64}
65
67{
69 /* Scale mean free path length so it gives similar looking result to older
70 * Cubic, Gaussian and Burley models. */
71 bssrdf->radius *= 0.25f * M_1_PI_F;
72 }
73 else {
74 /* Adjust radius based on IOR and albedo. */
75 const float inv_eta = 1.0f / bssrdf->ior;
76 const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * bssrdf->ior;
77 const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) /
78 (1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */
79
80 Spectrum alpha_prime;
83 GET_SPECTRUM_CHANNEL(bssrdf->albedo, i), fourthirdA);
84 }
85
86 bssrdf->radius *= sqrt(3.0f * (one_spectrum() - alpha_prime));
87 }
88}
89
90/* Christensen-Burley BSSRDF.
91 *
92 * Approximate Reflectance Profiles from
93 * http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
94 */
95
96/* This is a bit arbitrary, just need big enough radius so it matches
97 * the mean free length, but still not too big so sampling is still
98 * effective. */
99#define BURLEY_TRUNCATE 16.0f
100#define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE)
101
103{
104 /* Diffuse surface transmission, equation (6). */
105 return 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f);
106}
107
108/* Scale mean free path length so it gives similar looking result
109 * to Cubic and Gaussian models. */
114
116{
117 /* Mean free path length. */
119 /* Surface albedo. */
120 const Spectrum A = bssrdf->albedo;
121 Spectrum s;
124 }
125
126 bssrdf->radius = l / s;
127}
128
129ccl_device float bssrdf_burley_eval(const float d, const float r)
130{
131 const float Rm = BURLEY_TRUNCATE * d;
132
133 if (r >= Rm) {
134 return 0.0f;
135 }
136
137 /* Burley reflectance profile, equation (3).
138 *
139 * NOTES:
140 * - Surface albedo is already included into `sc->weight`, no need to
141 * multiply by this term here.
142 * - This is normalized diffuse model, so the equation is multiplied
143 * by `2*pi`, which also matches `cdf()`.
144 */
145 const float exp_r_3_d = expf(-r / (3.0f * d));
146 const float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d;
147 return (exp_r_d + exp_r_3_d) / (4.0f * d);
148}
149
150ccl_device float bssrdf_burley_pdf(const float d, const float r)
151{
152 if (r == 0.0f) {
153 return 0.0f;
154 }
155
156 return bssrdf_burley_eval(d, r) * (1.0f / BURLEY_TRUNCATE_CDF);
157}
158
159/* Find the radius for desired CDF value.
160 * Returns scaled radius, meaning the result is to be scaled up by d.
161 * Since there's no closed form solution we do Newton-Raphson method to find it.
162 */
164{
165 const float tolerance = 1e-6f;
166 const int max_iteration_count = 10;
167 /* Do initial guess based on manual curve fitting, this allows us to reduce
168 * number of iterations to maximum 4 across the [0..1] range. We keep maximum
169 * number of iteration higher just to be sure we didn't miss root in some
170 * corner case.
171 */
172 float r;
173 if (xi <= 0.9f) {
174 r = expf(xi * xi * 2.4f) - 1.0f;
175 }
176 else {
177 /* TODO(sergey): Some nicer curve fit is possible here. */
178 r = 15.0f;
179 }
180 /* Solve against scaled radius. */
181 for (int i = 0; i < max_iteration_count; i++) {
182 const float exp_r_3 = expf(-r / 3.0f);
183 const float exp_r = exp_r_3 * exp_r_3 * exp_r_3;
184 const float f = 1.0f - 0.25f * exp_r - 0.75f * exp_r_3 - xi;
185 const float f_ = 0.25f * exp_r + 0.25f * exp_r_3;
186
187 if (fabsf(f) < tolerance || f_ == 0.0f) {
188 break;
189 }
190
191 r = r - f / f_;
192 r = fmaxf(r, 0.0f);
193 }
194 return r;
195}
196
198 const float xi,
199 ccl_private float *r,
200 ccl_private float *h)
201{
202 const float Rm = BURLEY_TRUNCATE * d;
203 const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d;
204
205 *r = r_;
206
207 /* h^2 + r^2 = Rm^2 */
208 *h = safe_sqrtf(Rm * Rm - r_ * r_);
209}
210
212{
213 float channels = 0;
215 if (GET_SPECTRUM_CHANNEL(radius, i) > 0.0f) {
216 channels += 1.0f;
217 }
218 }
219 return channels;
220}
221
223 float xi,
224 ccl_private float *r,
225 ccl_private float *h)
226{
227 const float num_channels = bssrdf_num_channels(radius);
228 float sampled_radius;
229
230 /* Sample color channel and reuse random number. Only a subset of channels
231 * may be used if their radius was too small to handle as BSSRDF. */
232 xi *= num_channels;
233 sampled_radius = 0.0f;
234
235 float sum = 0.0f;
237 const float channel_radius = GET_SPECTRUM_CHANNEL(radius, i);
238 if (channel_radius > 0.0f) {
239 const float next_sum = sum + 1.0f;
240 if (xi < next_sum) {
241 xi -= sum;
242 sampled_radius = channel_radius;
243 break;
244 }
245 sum = next_sum;
246 }
247 }
248
249 /* Sample BSSRDF. */
250 bssrdf_burley_sample(sampled_radius, xi, r, h);
251}
252
261
262ccl_device_forceinline float bssrdf_pdf(const Spectrum radius, const float r)
263{
264 const Spectrum pdf = bssrdf_eval(radius, r);
265 return reduce_add(pdf) / bssrdf_num_channels(radius);
266}
267
268/* Setup */
269
271{
272 const float sample_weight = fabsf(average(weight));
273 if (sample_weight < CLOSURE_WEIGHT_CUTOFF) {
274 return nullptr;
275 }
276
278 sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight);
279
280 if (bssrdf == nullptr) {
281 return nullptr;
282 }
283
284 bssrdf->sample_weight = sample_weight;
285 return bssrdf;
286}
287
290 const int path_flag,
291 ClosureType type)
292{
293 /* Clamps protecting against bad/extreme and non physical values. */
294 bssrdf->anisotropy = clamp(bssrdf->anisotropy, 0.0f, 0.9f);
295 bssrdf->ior = clamp(bssrdf->ior, 1.01f, 3.8f);
296
297 int flag = 0;
298
300 /* CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID uses a fixed roughness. */
301 bssrdf->alpha = 1.0f;
302 }
303
304 /* Verify if the radii are large enough to sample without precision issues. */
305 int bssrdf_channels = SPECTRUM_CHANNELS;
306 Spectrum diffuse_weight = zero_spectrum();
307
308 if (type == CLOSURE_BSSRDF_BURLEY_ID && (path_flag & PATH_RAY_DIFFUSE_ANCESTOR)) {
309 /* Fall back to diffuse after a diffuse ancestor for Christensen-Burley. Can't see it that
310 * well and it adds considerable noise due to probabilities of continuing the path getting
311 * lower and lower. The disk sampling must probe in directions where most of the time
312 * nothing will be hit. */
313 bssrdf_channels = 0;
314 diffuse_weight = bssrdf->weight;
315 }
316 else {
319 GET_SPECTRUM_CHANNEL(diffuse_weight, i) = GET_SPECTRUM_CHANNEL(bssrdf->weight, i);
320 GET_SPECTRUM_CHANNEL(bssrdf->weight, i) = 0.0f;
321 GET_SPECTRUM_CHANNEL(bssrdf->radius, i) = 0.0f;
322 bssrdf_channels--;
323 }
324 }
325 }
326
327 if (bssrdf_channels < SPECTRUM_CHANNELS) {
328 /* Add diffuse BSDF if any radius too small. */
330 sd, sizeof(DiffuseBsdf), diffuse_weight);
331
332 if (bsdf) {
333 bsdf->N = bssrdf->N;
334 flag |= bsdf_diffuse_setup(bsdf);
335 }
336 }
337
338 /* Setup BSSRDF if radius is large enough. */
339 if (bssrdf_channels > 0) {
340 bssrdf->type = type;
341 bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf_channels;
342
344
345 flag |= SD_BSSRDF;
346 }
347 else {
348 bssrdf->type = CLOSURE_NONE_ID;
349 bssrdf->sample_weight = 0.0f;
350 }
351
352 return flag;
353}
354
MINLINE float safe_sqrtf(float a)
CCL_NAMESPACE_BEGIN ccl_device ccl_private ShaderClosure * closure_alloc(ccl_private ShaderData *sd, const uint size, ClosureType type, Spectrum weight)
Definition alloc.h:11
ccl_device_inline ccl_private ShaderClosure * bsdf_alloc(ccl_private ShaderData *sd, const int size, Spectrum weight)
Definition alloc.h:55
ATTR_WARN_UNUSED_RESULT const BMLoop * l
#define A
ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf)
ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, const ClosureType type)
Definition bssrdf.h:66
ccl_device_inline Spectrum bssrdf_burley_compatible_mfp(Spectrum r)
Definition bssrdf.h:110
ccl_device void bssrdf_sample(const Spectrum radius, float xi, ccl_private float *r, ccl_private float *h)
Definition bssrdf.h:222
ccl_device_forceinline float bssrdf_burley_root_find(const float xi)
Definition bssrdf.h:163
ccl_device float bssrdf_dipole_compute_Rd(const float alpha_prime, const float fourthirdA)
Definition bssrdf.h:30
ccl_device_forceinline float bssrdf_pdf(const Spectrum radius, const float r)
Definition bssrdf.h:262
ccl_device_inline ccl_private Bssrdf * bssrdf_alloc(ccl_private ShaderData *sd, Spectrum weight)
Definition bssrdf.h:270
ccl_device float bssrdf_burley_eval(const float d, const float r)
Definition bssrdf.h:129
ccl_device float bssrdf_num_channels(const Spectrum radius)
Definition bssrdf.h:211
#define BURLEY_TRUNCATE
Definition bssrdf.h:99
ccl_device void bssrdf_burley_sample(const float d, const float xi, ccl_private float *r, ccl_private float *h)
Definition bssrdf.h:197
#define BURLEY_TRUNCATE_CDF
Definition bssrdf.h:100
ccl_device void bssrdf_burley_setup(ccl_private Bssrdf *bssrdf)
Definition bssrdf.h:115
ccl_device float bssrdf_dipole_compute_alpha_prime(const float rd, const float fourthirdA)
Definition bssrdf.h:36
ccl_device_inline float bssrdf_burley_fitting(const float A)
Definition bssrdf.h:102
ccl_device float bssrdf_burley_pdf(const float d, const float r)
Definition bssrdf.h:150
ccl_device int bssrdf_setup(ccl_private ShaderData *sd, ccl_private Bssrdf *bssrdf, const int path_flag, ClosureType type)
Definition bssrdf.h:288
ccl_device_forceinline Spectrum bssrdf_eval(const Spectrum radius, const float r)
Definition bssrdf.h:253
static T sum(const btAlignedObjectArray< T > &items)
#define ccl_device_forceinline
#define one_spectrum
#define SPECTRUM_CHANNELS
#define zero_spectrum
#define ccl_private
#define ccl_device_inline
#define M_1_PI_F
#define FOREACH_SPECTRUM_CHANNEL(counter)
#define GET_SPECTRUM_CHANNEL(v, i)
#define BSSRDF_MIN_RADIUS
#define expf(x)
#define CCL_NAMESPACE_END
#define CLOSURE_WEIGHT_CUTOFF
#define sqrt
constexpr T clamp(T, U, U) RET
ClosureType
@ CLOSURE_BSSRDF_BURLEY_ID
@ CLOSURE_NONE_ID
@ CLOSURE_BSSRDF_RANDOM_WALK_ID
@ CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID
@ SD_BSSRDF
@ PATH_RAY_DIFFUSE_ANCESTOR
ccl_device_inline dual1 reduce_add(const dual< T > a)
Definition math_dual.h:49
float average(point a)
Definition node_math.h:144
#define fabsf
#define sqrtf
#define ccl_device
#define fmaxf
closure color bssrdf(string method, normal N, vector radius, color albedo) BUILTIN
Spectrum radius
Definition bssrdf.h:17
float anisotropy
Definition bssrdf.h:19
float alpha
Definition bssrdf.h:23
Spectrum albedo
Definition bssrdf.h:18
float ior
Definition bssrdf.h:22
SHADER_CLOSURE_BASE
Definition bssrdf.h:15
i
Definition text_draw.cc:230
float3 Spectrum
uint8_t flag
Definition wm_window.cc:145