Blender  V3.3
bsdf_microfacet_multi_impl.h
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2011-2022 Blender Foundation */
3 
4 /* Evaluate the BSDF from wi to wo.
5  * Evaluation is split into the analytical single-scattering BSDF and the multi-scattering BSDF,
6  * which is evaluated stochastically through a random walk. At each bounce (except for the first
7  * one), the amount of reflection from here towards wo is evaluated before bouncing again.
8  *
9  * Because of the random walk, the evaluation is not deterministic, but its expected value is equal
10  * to the correct BSDF, which is enough for Monte-Carlo rendering. The PDF also can't be determined
11  * analytically, so the single-scattering PDF plus a diffuse term to account for the
12  * multi-scattered energy is used. In combination with MIS, that is enough to produce an unbiased
13  * result, although the balance heuristic isn't necessarily optimal anymore.
14  */
16  float3 wo,
17  const bool wo_outside,
18  const float3 color,
19  const float alpha_x,
20  const float alpha_y,
21  ccl_private uint *lcg_state,
22  const float eta,
23  bool use_fresnel,
24  const float3 cspec0)
25 {
26  /* Evaluating for a shallower incoming direction produces less noise, and the properties of the
27  * BSDF guarantee reciprocity. */
28  bool swapped = false;
29 #ifdef MF_MULTI_GLASS
30  if (wi.z * wo.z < 0.0f) {
31  /* Glass transmission is a special case and requires the directions to change hemisphere. */
32  if (-wo.z < wi.z) {
33  swapped = true;
34  float3 tmp = -wo;
35  wo = -wi;
36  wi = tmp;
37  }
38  }
39  else
40 #endif
41  if (wo.z < wi.z) {
42  swapped = true;
43  float3 tmp = wo;
44  wo = wi;
45  wi = tmp;
46  }
47 
48  if (wi.z < 1e-5f || (wo.z < 1e-5f && wo_outside) || (wo.z > -1e-5f && !wo_outside))
49  return make_float3(0.0f, 0.0f, 0.0f);
50 
51  const float2 alpha = make_float2(alpha_x, alpha_y);
52 
53  float lambda_r = mf_lambda(-wi, alpha);
54  float shadowing_lambda = mf_lambda(wo_outside ? wo : -wo, alpha);
55 
56  /* Analytically compute single scattering for lower noise. */
57  float3 eval;
58  float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
59  const float3 wh = normalize(wi + wo);
60 #ifdef MF_MULTI_GLASS
61  eval = mf_eval_phase_glass(-wi, lambda_r, wo, wo_outside, alpha, eta);
62  if (wo_outside)
63  eval *= -lambda_r / (shadowing_lambda - lambda_r);
64  else
65  eval *= -lambda_r * beta(-lambda_r, shadowing_lambda + 1.0f);
66 #else /* MF_MULTI_GLOSSY */
67  const float G2 = 1.0f / (1.0f - (lambda_r + 1.0f) + shadowing_lambda);
68  float val = G2 * 0.25f / wi.z;
69  if (alpha.x == alpha.y)
70  val *= D_ggx(wh, alpha.x);
71  else
72  val *= D_ggx_aniso(wh, alpha);
73  eval = make_float3(val, val, val);
74 #endif
75 
76  float F0 = fresnel_dielectric_cos(1.0f, eta);
77  if (use_fresnel) {
78  throughput = interpolate_fresnel_color(wi, wh, eta, F0, cspec0);
79 
80  eval *= throughput;
81  }
82 
83  float3 wr = -wi;
84  float hr = 1.0f;
85  float C1_r = 1.0f;
86  float G1_r = 0.0f;
87  bool outside = true;
88 
89  for (int order = 0; order < 10; order++) {
90  /* Sample microfacet height. */
91  float height_rand = lcg_step_float(lcg_state);
92  if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand))
93  break;
94  /* Sample microfacet normal. */
95  float vndf_rand_y = lcg_step_float(lcg_state);
96  float vndf_rand_x = lcg_step_float(lcg_state);
97  float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y);
98 
99 #ifdef MF_MULTI_GLASS
100  if (order == 0 && use_fresnel) {
101  /* Evaluate amount of scattering towards wo on this microfacet. */
102  float3 phase;
103  if (outside)
104  phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta);
105  else
106  phase = mf_eval_phase_glass(wr, lambda_r, -wo, !wo_outside, alpha, 1.0f / eta);
107 
108  eval = throughput * phase *
109  mf_G1(wo_outside ? wo : -wo,
110  mf_C1((outside == wo_outside) ? hr : -hr),
111  shadowing_lambda);
112  }
113 #endif
114  if (order > 0) {
115  /* Evaluate amount of scattering towards wo on this microfacet. */
116  float3 phase;
117 #ifdef MF_MULTI_GLASS
118  if (outside)
119  phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta);
120  else
121  phase = mf_eval_phase_glass(wr, lambda_r, -wo, !wo_outside, alpha, 1.0f / eta);
122 #else /* MF_MULTI_GLOSSY */
123  phase = mf_eval_phase_glossy(wr, lambda_r, wo, alpha) * throughput;
124 #endif
125  eval += throughput * phase *
126  mf_G1(wo_outside ? wo : -wo,
127  mf_C1((outside == wo_outside) ? hr : -hr),
128  shadowing_lambda);
129  }
130  if (order + 1 < 10) {
131  /* Bounce from the microfacet. */
132 #ifdef MF_MULTI_GLASS
133  bool next_outside;
134  float3 wi_prev = -wr;
135  float phase_rand = lcg_step_float(lcg_state);
136  wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside);
137  if (!next_outside) {
138  outside = !outside;
139  wr = -wr;
140  hr = -hr;
141  }
142 
143  if (use_fresnel && !next_outside) {
144  throughput *= color;
145  }
146  else if (use_fresnel && order > 0) {
147  throughput *= interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0);
148  }
149 #else /* MF_MULTI_GLOSSY */
150  if (use_fresnel && order > 0) {
151  throughput *= interpolate_fresnel_color(-wr, wm, eta, F0, cspec0);
152  }
153  wr = mf_sample_phase_glossy(-wr, &throughput, wm);
154 #endif
155 
156  lambda_r = mf_lambda(wr, alpha);
157 
158  if (!use_fresnel)
159  throughput *= color;
160 
161  C1_r = mf_C1(hr);
162  G1_r = mf_G1(wr, C1_r, lambda_r);
163  }
164  }
165 
166  if (swapped)
167  eval *= fabsf(wi.z / wo.z);
168  return eval;
169 }
170 
171 /* Perform a random walk on the microsurface starting from wi, returning the direction in which the
172  * walk escaped the surface in wo. The function returns the throughput between wi and wo. Without
173  * reflection losses due to coloring or fresnel absorption in conductors, the sampling is optimal.
174  */
176  ccl_private float3 *wo,
177  const float3 color,
178  const float alpha_x,
179  const float alpha_y,
180  ccl_private uint *lcg_state,
181  const float eta,
182  bool use_fresnel,
183  const float3 cspec0)
184 {
185  const float2 alpha = make_float2(alpha_x, alpha_y);
186 
187  float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
188  float3 wr = -wi;
189  float lambda_r = mf_lambda(wr, alpha);
190  float hr = 1.0f;
191  float C1_r = 1.0f;
192  float G1_r = 0.0f;
193  bool outside = true;
194 
195  float F0 = fresnel_dielectric_cos(1.0f, eta);
196 
197  int order;
198  for (order = 0; order < 10; order++) {
199  /* Sample microfacet height. */
200  float height_rand = lcg_step_float(lcg_state);
201  if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) {
202  /* The random walk has left the surface. */
203  *wo = outside ? wr : -wr;
204  return throughput;
205  }
206  /* Sample microfacet normal. */
207  float vndf_rand_y = lcg_step_float(lcg_state);
208  float vndf_rand_x = lcg_step_float(lcg_state);
209  float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y);
210 
211  /* First-bounce color is already accounted for in mix weight. */
212  if (!use_fresnel && order > 0)
213  throughput *= color;
214 
215  /* Bounce from the microfacet. */
216 #ifdef MF_MULTI_GLASS
217  bool next_outside;
218  float3 wi_prev = -wr;
219  float phase_rand = lcg_step_float(lcg_state);
220  wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside);
221  if (!next_outside) {
222  hr = -hr;
223  wr = -wr;
224  outside = !outside;
225  }
226 
227  if (use_fresnel) {
228  if (!next_outside) {
229  throughput *= color;
230  }
231  else {
232  float3 t_color = interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0);
233 
234  if (order == 0)
235  throughput = t_color;
236  else
237  throughput *= t_color;
238  }
239  }
240 #else /* MF_MULTI_GLOSSY */
241  if (use_fresnel) {
242  float3 t_color = interpolate_fresnel_color(-wr, wm, eta, F0, cspec0);
243 
244  if (order == 0)
245  throughput = t_color;
246  else
247  throughput *= t_color;
248  }
249  wr = mf_sample_phase_glossy(-wr, &throughput, wm);
250 #endif
251 
252  /* Update random walk parameters. */
253  lambda_r = mf_lambda(wr, alpha);
254  G1_r = mf_G1(wr, C1_r, lambda_r);
255  }
256  *wo = make_float3(0.0f, 0.0f, 1.0f);
257  return make_float3(0.0f, 0.0f, 0.0f);
258 }
259 
260 #undef MF_MULTI_GLASS
261 #undef MF_MULTI_GLOSSY
262 #undef MF_PHASE_FUNCTION
unsigned int uint
Definition: BLI_sys_types.h:67
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint order
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 Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input 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 a color
ccl_device_forceinline float3 mf_eval_phase_glossy(const float3 w, const float lambda, const float3 wo, const float2 alpha)
ccl_device_forceinline float D_ggx_aniso(const float3 wm, const float2 alpha)
ccl_device_forceinline float mf_C1(const float h)
ccl_device_forceinline float3 mf_sample_phase_glass(const float3 wi, const float eta, const float3 wm, const float randV, ccl_private bool *outside)
ccl_device_forceinline bool mf_sample_height(const float3 w, ccl_private float *h, ccl_private float *C1, ccl_private float *G1, ccl_private float *lambda, const float U)
ccl_device_forceinline float mf_lambda(const float3 w, const float2 alpha)
ccl_device_forceinline float3 mf_eval_phase_glass(const float3 w, const float lambda, const float3 wo, const bool wo_outside, const float2 alpha, const float eta)
#define MF_FUNCTION_FULL_NAME(prefix)
CCL_NAMESPACE_BEGIN ccl_device_forceinline float D_ggx(float3 wm, float alpha)
ccl_device_forceinline float3 mf_sample_vndf(const float3 wi, const float2 alpha, const float randx, const float randy)
ccl_device_forceinline float mf_G1(const float3 w, const float C1, const float lambda)
ccl_device_forceinline float3 mf_sample_phase_glossy(const float3 wi, ccl_private float3 *weight, const float3 wm)
ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME() mf_sample(float3 wi, ccl_private float3 *wo, const float3 color, const float alpha_x, const float alpha_y, ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0)
ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME() mf_eval(float3 wi, float3 wo, const bool wo_outside, const float3 color, const float alpha_x, const float alpha_y, ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0)
ccl_device float fresnel_dielectric_cos(float cosi, float eta)
Definition: bsdf_util.h:79
ccl_device_forceinline float3 interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0)
Definition: bsdf_util.h:114
#define ccl_device_forceinline
Definition: cuda/compat.h:35
#define ccl_private
Definition: cuda/compat.h:48
ccl_device float lcg_step_float(T rng)
Definition: lcg.h:19
#define make_float2(x, y)
Definition: metal/compat.h:203
#define fabsf(x)
Definition: metal/compat.h:219
#define make_float3(x, y, z)
Definition: metal/compat.h:204
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
float x
Definition: types_float2.h:15
float y
Definition: types_float2.h:15
ccl_device_inline float beta(float x, float y)
Definition: util/math.h:775