Blender V4.3
bsdf_util.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
11
12/* Compute fresnel reflectance for perpendicular (aka S-) and parallel (aka P-) polarized light.
13 * If requested by the caller, r_phi is set to the phase shift on reflection.
14 * Also returns the dot product of the refracted ray and the normal as `cos_theta_t`, as it is
15 * used when computing the direction of the refracted ray. */
17 float eta,
18 ccl_private float *r_cos_theta_t,
19 ccl_private float2 *r_phi)
20{
21 kernel_assert(!isnan_safe(cos_theta_i));
22
23 /* Using Snell's law, calculate the squared cosine of the angle between the surface normal and
24 * the transmitted ray. */
25 const float eta_cos_theta_t_sq = sqr(eta) - (1.0f - sqr(cos_theta_i));
26 if (eta_cos_theta_t_sq <= 0) {
27 /* Total internal reflection. */
28 if (r_phi) {
29 /* The following code would compute the proper phase shift on TIR.
30 * However, for the current user of this computation (the iridescence code),
31 * this doesn't actually affect the result, so don't bother with the computation for now.
32 *
33 * `const float fac = sqrtf(1.0f - sqr(cosThetaI) - sqr(eta));`
34 * `r_phi->x = -2.0f * atanf(fac / cosThetaI);`
35 * `r_phi->y = -2.0f * atanf(fac / (cosThetaI * sqr(eta)));`
36 */
37 *r_phi = zero_float2();
38 }
39 return one_float2();
40 }
41
42 cos_theta_i = fabsf(cos_theta_i);
43 /* Relative to the surface normal. */
44 const float cos_theta_t = -safe_sqrtf(eta_cos_theta_t_sq) / eta;
45
46 if (r_cos_theta_t) {
47 *r_cos_theta_t = cos_theta_t;
48 }
49
50 /* Amplitudes of reflected waves. */
51 const float r_s = (cos_theta_i + eta * cos_theta_t) / (cos_theta_i - eta * cos_theta_t);
52 const float r_p = (cos_theta_t + eta * cos_theta_i) / (eta * cos_theta_i - cos_theta_t);
53
54 if (r_phi) {
55 *r_phi = make_float2(r_s < 0.0f, r_p < 0.0f) * M_PI_F;
56 }
57
58 /* Return squared amplitude to get the fraction of reflected energy. */
59 return make_float2(sqr(r_s), sqr(r_p));
60}
61
62/* Compute fresnel reflectance for unpolarized light. */
64 float eta,
65 ccl_private float *r_cos_theta_t)
66{
67 return average(fresnel_dielectric_polarized(cos_theta_i, eta, r_cos_theta_t, nullptr));
68}
69
70/* Refract the incident ray, given the cosine of the refraction angle and the relative refractive
71 * index of the incoming medium w.r.t. the outgoing medium. */
73 const float3 normal,
74 const float cos_theta_t,
75 const float inv_eta)
76{
77 return (inv_eta * dot(normal, incident) + cos_theta_t) * normal - inv_eta * incident;
78}
79
80ccl_device float fresnel_dielectric_cos(float cosi, float eta)
81{
82 // compute fresnel reflectance without explicitly computing
83 // the refracted direction
84 float c = fabsf(cosi);
85 float g = eta * eta - 1 + c * c;
86 if (g > 0) {
87 g = sqrtf(g);
88 float A = (g - c) / (g + c);
89 float B = (c * (g + c) - 1) / (c * (g - c) + 1);
90 return 0.5f * A * A * (1 + B * B);
91 }
92 return 1.0f; // TIR(no refracted component)
93}
94
95ccl_device Spectrum fresnel_conductor(float cosi, const Spectrum eta, const Spectrum k)
96{
97 Spectrum cosi2 = make_spectrum(cosi * cosi);
98 Spectrum one = make_spectrum(1.0f);
99 Spectrum tmp_f = eta * eta + k * k;
100 Spectrum tmp = tmp_f * cosi2;
101 Spectrum Rparl2 = (tmp - (2.0f * eta * cosi) + one) / (tmp + (2.0f * eta * cosi) + one);
102 Spectrum Rperp2 = (tmp_f - (2.0f * eta * cosi) + cosi2) / (tmp_f + (2.0f * eta * cosi) + cosi2);
103 return (Rparl2 + Rperp2) * 0.5f;
104}
105
106ccl_device float ior_from_F0(float f0)
107{
108 const float sqrt_f0 = sqrtf(clamp(f0, 0.0f, 0.99f));
109 return (1.0f + sqrt_f0) / (1.0f - sqrt_f0);
110}
111
112ccl_device float F0_from_ior(float ior)
113{
114 return sqr((ior - 1.0f) / (ior + 1.0f));
115}
116
118{
119 float m = clamp(1.0f - u, 0.0f, 1.0f);
120 float m2 = m * m;
121 return m2 * m2 * m; // pow(m, 5)
122}
123
124/* Calculate the fresnel color, which is a blend between white and the F0 color */
126 float3 H,
127 float ior,
128 Spectrum F0)
129{
130 /* Compute the real Fresnel term and remap it from real_F0..1 to F0..1.
131 * The reason why we use this remapping instead of directly doing the
132 * Schlick approximation mix(F0, 1.0, (1.0-cosLH)^5) is that for cases
133 * with similar IORs (e.g. ice in water), the relative IOR can be close
134 * enough to 1.0 that the Schlick approximation becomes inaccurate. */
135 float real_F = fresnel_dielectric_cos(dot(L, H), ior);
136 float real_F0 = fresnel_dielectric_cos(1.0f, ior);
137
138 return mix(F0, one_spectrum(), inverse_lerp(real_F0, 1.0f, real_F));
139}
140
141/* If the shading normal results in specular reflection in the lower hemisphere, raise the shading
142 * normal towards the geometry normal so that the specular reflection is just above the surface.
143 * Only used for glossy materials. */
145{
146 const float3 R = 2 * dot(N, I) * N - I;
147
148 const float Iz = dot(I, Ng);
149 kernel_assert(Iz >= 0);
150
151 /* Reflection rays may always be at least as shallow as the incoming ray. */
152 const float threshold = min(0.9f * Iz, 0.01f);
153 if (dot(Ng, R) >= threshold) {
154 return N;
155 }
156
157 /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane.
158 * The X axis is found by normalizing the component of N that's orthogonal to Ng.
159 * The Y axis isn't actually needed.
160 */
161 const float3 X = safe_normalize_fallback(N - dot(N, Ng) * Ng, N);
162
163 /* Calculate N.z and N.x in the local coordinate system.
164 *
165 * The goal of this computation is to find a N' that is rotated towards Ng just enough
166 * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t.
167 *
168 * According to the standard reflection equation,
169 * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t.
170 *
171 * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get
172 * 2*dot(N', I)*N'.z - I.z = t.
173 *
174 * The rotation is simple to express in the coordinate system we formed -
175 * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane,
176 * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z .
177 *
178 * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2).
179 *
180 * With these simplifications, we get the equation
181 * 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t,
182 * or
183 * 2*sqrt(1 - N'.z^2)*I.x*N'.z = t + I.z * (1 - 2*N'.z^2),
184 * after rearranging terms.
185 * Raise both sides to the power of two and substitute terms with
186 * a = I.x^2 + I.z^2,
187 * b = 2*(a + Iz*t),
188 * c = (Iz + t)^2,
189 * we obtain
190 * 4*a*N'.z^4 - 2*b*N'.z^2 + c = 0.
191 *
192 * The only unknown here is N'.z, so we can solve for that.
193 *
194 * The equation has four solutions in general, two can immediately be discarded because they're
195 * negative so N' would lie in the lower hemisphere; one solves
196 * 2*sqrt(1 - N'.z^2)*I.x*N'.z = -(t + I.z * (1 - 2*N'.z^2))
197 * instead of the original equation (before squaring both sides).
198 * Therefore only one root is valid.
199 */
200
201 const float Ix = dot(I, X);
202
203 const float a = sqr(Ix) + sqr(Iz);
204 const float b = 2.0f * (a + Iz * threshold);
205 const float c = sqr(threshold + Iz);
206
207 /* In order that the root formula solves 2*sqrt(1 - N'.z^2)*I.x*N'.z = t + I.z - 2*I.z*N'.z^2,
208 * Ix and (t + I.z * (1 - 2*N'.z^2)) must have the same sign (the rest terms are non-negative by
209 * definition). */
210 const float Nz2 = (Ix < 0) ? 0.25f * (b + safe_sqrtf(sqr(b) - 4.0f * a * c)) / a :
211 0.25f * (b - safe_sqrtf(sqr(b) - 4.0f * a * c)) / a;
212
213 const float Nx = safe_sqrtf(1.0f - Nz2);
214 const float Nz = safe_sqrtf(Nz2);
215
216 return Nx * X + Nz * Ng;
217}
218
219/* Do not call #ensure_valid_specular_reflection if the primitive type is curve or if the geometry
220 * normal and the shading normal is the same. */
222{
223 if ((sd->flag & SD_USE_BUMP_MAP_CORRECTION) == 0) {
224 return N;
225 }
226 if ((sd->type & PRIMITIVE_CURVE) || isequal(sd->Ng, N)) {
227 return N;
228 }
229 return ensure_valid_specular_reflection(sd->Ng, sd->wi, N);
230}
231
232/* Principled Hair albedo and absorption coefficients. */
234 const float azimuthal_roughness)
235{
236 const float x = azimuthal_roughness;
237 return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f;
238}
239
241bsdf_principled_hair_sigma_from_reflectance(const Spectrum color, const float azimuthal_roughness)
242{
243 const Spectrum sigma = log(color) /
245 return sigma * sigma;
246}
247
249 const float pheomelanin)
250{
251 const float3 eumelanin_color = make_float3(0.506f, 0.841f, 1.653f);
252 const float3 pheomelanin_color = make_float3(0.343f, 0.733f, 1.924f);
253
254 return eumelanin * rgb_to_spectrum(eumelanin_color) +
255 pheomelanin * rgb_to_spectrum(pheomelanin_color);
256}
257
258/* Computes the weight for base closure(s) which are layered under another closure.
259 * layer_albedo is an estimate of the top layer's reflectivity, while weight is the closure weight
260 * of the entire base+top combination. */
262 const Spectrum weight)
263{
264 return weight * saturatef(1.0f - reduce_max(safe_divide_color(layer_albedo, weight)));
265}
266
267/* ******** Thin-film iridescence implementation ********
268 *
269 * Based on "A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence"
270 * by Laurent Belcour and Pascal Barla.
271 * https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html.
272 */
273
285{
286 float phase = M_2PI_F * OPD * 1e-9f;
287 float3 val = make_float3(5.4856e-13f, 4.4201e-13f, 5.2481e-13f);
288 float3 pos = make_float3(1.6810e+06f, 1.7953e+06f, 2.2084e+06f);
289 float3 var = make_float3(4.3278e+09f, 9.3046e+09f, 6.6121e+09f);
290
291 float3 xyz = val * sqrt(M_2PI_F * var) * cos(pos * phase + shift) * exp(-sqr(phase) * var);
292 xyz.x += 1.64408e-8f * cosf(2.2399e+06f * phase + shift) * expf(-4.5282e+09f * sqr(phase));
293 return xyz / 1.0685e-7f;
294}
295
297iridescence_airy_summation(float T121, float R12, float R23, float OPD, float phi)
298{
299 if (R23 == 1.0f) {
300 /* Shortcut for TIR on the bottom interface. */
301 return one_float3();
302 }
303
304 float R123 = R12 * R23;
305 float r123 = sqrtf(R123);
306 float Rs = sqr(T121) * R23 / (1.0f - R123);
307
308 /* Perform summation over path order differences (equation 10). */
309 float3 R = make_float3(R12 + Rs); /* C0 */
310 float Cm = (Rs - T121);
311 /* Truncate after m=3, higher differences have barely any impact. */
312 for (int m = 1; m < 4; m++) {
313 Cm *= r123;
314 R += Cm * 2.0f * iridescence_lookup_sensitivity(m * OPD, m * phi);
315 }
316 return R;
317}
318
320 float eta1,
321 float eta2,
322 float eta3,
323 float cos_theta_1,
324 float thickness,
325 ccl_private float *r_cos_theta_3)
326{
327 /* For films below 30nm, the wave-optic-based Airy summation approach no longer applies,
328 * so blend towards the case without coating. */
329 if (thickness < 30.0f) {
330 eta2 = mix(eta1, eta2, smoothstep(0.0f, 30.0f, thickness));
331 }
332
333 float cos_theta_2;
334 float2 phi12, phi23;
335
336 /* Compute reflection at the top interface (ambient to film). */
337 float2 R12 = fresnel_dielectric_polarized(cos_theta_1, eta2 / eta1, &cos_theta_2, &phi12);
338 if (isequal(R12, one_float2())) {
339 /* TIR at the top interface. */
340 return one_spectrum();
341 }
342
343 /* Compute optical path difference inside the thin film. */
344 float OPD = -2.0f * eta2 * thickness * cos_theta_2;
345
346 /* Compute reflection at the bottom interface (film to medium). */
347 float2 R23 = fresnel_dielectric_polarized(-cos_theta_2, eta3 / eta2, r_cos_theta_3, &phi23);
348 if (isequal(R23, one_float2())) {
349 /* TIR at the bottom interface.
350 * All the Airy summation math still simplifies to 1.0 in this case. */
351 return one_spectrum();
352 }
353
354 /* Compute helper parameters. */
355 float2 T121 = one_float2() - R12;
356 float2 phi = make_float2(M_PI_F, M_PI_F) - phi12 + phi23;
357
358 /* Perform Airy summation and average the polarizations. */
359 float3 R = mix(iridescence_airy_summation(T121.x, R12.x, R23.x, OPD, phi.x),
360 iridescence_airy_summation(T121.y, R12.y, R23.y, OPD, phi.y),
361 0.5f);
362
363 /* Color space conversion here is tricky.
364 * In theory, the correct thing would be to compute the spectral color matching functions
365 * for the RGB channels, take their Fourier transform in wavelength parametrization, and
366 * then use that in iridescence_lookup_sensitivity().
367 * To avoid this complexity, the code here instead uses the reference implementation's
368 * Gaussian fit of the CIE XYZ curves. However, this means that at this point, R is in
369 * XYZ values, not RGB.
370 * Additionally, since I is a reflectivity, not a luminance, the spectral color matching
371 * functions should be multiplied by the reference illuminant. Since the fit is based on
372 * the "raw" CIE XYZ curves, the reference illuminant implicitly is a constant spectrum,
373 * meaning Illuminant E.
374 * Therefore, we can't just use the regular XYZ->RGB conversion here, we need to include
375 * a chromatic adaption from E to whatever the white point of the working color space is.
376 * The proper way to do this would be a Von Kries-style transform, but to keep it simple,
377 * we just multiply by the white point here.
378 *
379 * NOTE: The reference implementation sidesteps all this by just hard-coding a XYZ->CIE RGB
380 * matrix. Since CIE RGB uses E as its white point, this sidesteps the chromatic adaption
381 * topic, but the primary colors don't match (unless you happen to actually work in CIE RGB.)
382 */
383 R *= float4_to_float3(kernel_data.film.white_xyz);
384 return saturate(xyz_to_rgb(kg, R));
385}
386
sqrt(x)+1/max(0
MINLINE float safe_sqrtf(float a)
#define saturate(a)
#define X
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 color
#define A
ccl_device_forceinline Spectrum interpolate_fresnel_color(float3 L, float3 H, float ior, Spectrum F0)
Definition bsdf_util.h:125
ccl_device float fresnel_dielectric_cos(float cosi, float eta)
Definition bsdf_util.h:80
ccl_device float F0_from_ior(float ior)
Definition bsdf_util.h:112
ccl_device_inline Spectrum bsdf_principled_hair_sigma_from_reflectance(const Spectrum color, const float azimuthal_roughness)
Definition bsdf_util.h:241
ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale(const float azimuthal_roughness)
Definition bsdf_util.h:233
ccl_device Spectrum fresnel_conductor(float cosi, const Spectrum eta, const Spectrum k)
Definition bsdf_util.h:95
ccl_device Spectrum fresnel_iridescence(KernelGlobals kg, float eta1, float eta2, float eta3, float cos_theta_1, float thickness, ccl_private float *r_cos_theta_3)
Definition bsdf_util.h:319
ccl_device_forceinline float fresnel_dielectric(float cos_theta_i, float eta, ccl_private float *r_cos_theta_t)
Definition bsdf_util.h:63
ccl_device float3 maybe_ensure_valid_specular_reflection(ccl_private ShaderData *sd, float3 N)
Definition bsdf_util.h:221
ccl_device_inline Spectrum bsdf_principled_hair_sigma_from_concentration(const float eumelanin, const float pheomelanin)
Definition bsdf_util.h:248
ccl_device_inline float3 refract_angle(const float3 incident, const float3 normal, const float cos_theta_t, const float inv_eta)
Definition bsdf_util.h:72
CCL_NAMESPACE_BEGIN ccl_device float2 fresnel_dielectric_polarized(float cos_theta_i, float eta, ccl_private float *r_cos_theta_t, ccl_private float2 *r_phi)
Definition bsdf_util.h:16
ccl_device_inline Spectrum closure_layering_weight(const Spectrum layer_albedo, const Spectrum weight)
Definition bsdf_util.h:261
ccl_device float ior_from_F0(float f0)
Definition bsdf_util.h:106
ccl_device_inline Spectrum iridescence_lookup_sensitivity(float OPD, float shift)
Definition bsdf_util.h:284
ccl_device_inline float3 iridescence_airy_summation(float T121, float R12, float R23, float OPD, float phi)
Definition bsdf_util.h:297
ccl_device float3 ensure_valid_specular_reflection(float3 Ng, float3 I, float3 N)
Definition bsdf_util.h:144
ccl_device float schlick_fresnel(float u)
Definition bsdf_util.h:117
local_group_size(16, 16) .push_constant(Type b
additional_info("compositor_sum_squared_difference_float_shared") .push_constant(Type output_img float dot(value.rgb, luminance_coefficients)") .define("LOAD(value)"
#define kernel_assert(cond)
#define kernel_data
const KernelGlobalsCPU *ccl_restrict KernelGlobals
#define ccl_device_forceinline
#define cosf(x)
#define ccl_device
#define expf(x)
#define ccl_private
#define ccl_device_inline
#define CCL_NAMESPACE_END
#define saturatef(x)
ccl_device_forceinline float3 make_float3(const float x, const float y, const float z)
ccl_device_forceinline float2 make_float2(const float x, const float y)
#define fabsf(x)
#define sqrtf(x)
#define mix(a, b, c)
Definition hash.h:36
@ SD_USE_BUMP_MAP_CORRECTION
@ PRIMITIVE_CURVE
ShaderData
ccl_device_inline Spectrum rgb_to_spectrum(float3 rgb)
MINLINE float smoothstep(float edge0, float edge1, float x)
ccl_device_inline float2 one_float2()
Definition math_float2.h:19
CCL_NAMESPACE_BEGIN ccl_device_inline float2 zero_float2()
Definition math_float2.h:14
ccl_device_inline float average(const float2 a)
ccl_device_inline float reduce_max(const float2 a)
ccl_device_inline bool isequal(const float2 a, const float2 b)
ccl_device_inline float3 safe_normalize_fallback(const float3 a, const float3 fallback)
ccl_device_inline float3 one_float3()
Definition math_float3.h:24
ccl_device_inline float3 exp(float3 v)
ccl_device_inline float3 cos(float3 v)
ccl_device_inline float3 log(float3 v)
#define N
#define B
#define R
#define L
#define H(x, y, z)
#define M_PI_F
Definition mikk_util.hh:15
color xyz_to_rgb(float x, float y, float z)
Definition node_color.h:73
#define I
#define M_2PI_F
Definition sky_float3.h:23
#define min(a, b)
Definition sort.c:32
float x
float y
float x
Definition sky_float3.h:27
#define one_spectrum
#define make_spectrum(f)
SPECTRUM_DATA_TYPE Spectrum
ccl_device_inline float inverse_lerp(float a, float b, float x)
Definition util/math.h:550
ccl_device_inline float sqr(float a)
Definition util/math.h:782
ccl_device_inline bool isnan_safe(float f)
Definition util/math.h:359
ccl_device_inline float3 float4_to_float3(const float4 a)
Definition util/math.h:535
ccl_device_inline Spectrum safe_divide_color(Spectrum a, Spectrum b)
Definition util/math.h:632
ccl_device_inline int clamp(int a, int mn, int mx)
Definition util/math.h:379