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