Fermat
bsdf.h
1 /*
2  * Fermat
3  *
4  * Copyright (c) 2016-2019, NVIDIA CORPORATION. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the NVIDIA CORPORATION nor the
14  * names of its contributors may be used to endorse or promote products
15  * derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #pragma once
30 
31 // ------------------------------------------------------------------------- //
32 //
33 // Declaration of the composite BSDF class.
34 //
35 // ------------------------------------------------------------------------- //
36 
37 #include <types.h>
38 #include <cugar/linalg/vector.h>
39 #include <cugar/bsdf/lambert.h>
40 #include <cugar/bsdf/lambert_trans.h>
41 #include <cugar/bsdf/ggx.h>
42 #include <cugar/bsdf/ggx_smith.h>
43 #include <cugar/bsdf/ltc.h>
44 #include <cugar/bsdf/refraction.h>
45 #include <renderer_view.h>
46 #include <mis_utils.h>
47 
48 #define DIFFUSE_ONLY 0
49 #define SPECULAR_ONLY 0
50 #define SUPPRESS_SPECULAR 0
51 #define SUPPRESS_DIFFUSE 0
52 
53 #define USE_EFFICIENT_SAMPLER_WITH_APPROXIMATE_PDFS 1
54  // When this is set to 1, a different, more efficient sampling method will be used, for which the pdf computation
55  // will no longer be exact - in the sense it will no longer match the actual pdfs usded for sampling - which would
56  // have to be expressed as an integral with no closed-form solution - yet, for the purpose of MIS, exact pdfs are
57  // not really needed.
58  // One potential problem with this efficient sampler is that it makes exact inversion impossible - in the sense
59  // that the probabilities assigned to the various components are stochastic variables and cannot be computed, and
60  // invertex, exactly.
61 
64 
80 
84 {
85  kRadianceTransport = 0,
86  kParticleTransport = 1
87 };
88 
89 //#define USE_LTC
90 #define USE_GGX_SMITH
91 
123 struct Bsdf
124 {
128  {
129  kDiffuseReflectionIndex = 0u,
130  kDiffuseTransmissionIndex = 1u,
131  kGlossyReflectionIndex = 2u,
132  kGlossyTransmissionIndex = 3u,
133  kClearcoatReflectionIndex = 4u,
134  kNumComponents = 5u,
135  };
136 
140  {
141  kAbsorption = 0u,
142 
143  kDiffuseReflection = 0x1u,
144  kDiffuseTransmission = 0x2u,
145  kGlossyReflection = 0x4u,
146  kGlossyTransmission = 0x8u,
147  kClearcoatReflection = 0x10u,
148 
149  kDiffuseMask = 0x3u,
150  kGlossyMask = 0xCu,
151 
152  kReflectionMask = 0x5u,
153  kTransmissionMask = 0xAu,
154  kAllComponents = 0xFFu
155  };
156 
159  #if defined(USE_LTC)
161  #elif defined(USE_GGX_SMITH)
162  typedef cugar::GGXSmithBsdf glossy_component;
163  #else
164  typedef cugar::GGXBsdf glossy_component;
165  #endif
166 
169  FERMAT_HOST_DEVICE
170  static uint32 component_count() { return 4; }
171 
174  FERMAT_HOST_DEVICE
175  static uint32 component_index(const ComponentType comp)
176  {
177  if (comp == kDiffuseReflection) return 0;
178  if (comp == kDiffuseTransmission) return 1;
179  if (comp == kGlossyReflection) return 2;
180  if (comp == kGlossyTransmission) return 3;
181 
182  return 4;
183  }
184 
187  FERMAT_HOST_DEVICE
188  static ComponentType component_mask(const ComponentIndex comp) { return ComponentType(1u << comp); }
189 
192  FERMAT_HOST_DEVICE
193  Bsdf() :
194  #if defined(USE_LTC)
195  m_diffuse(0.0f), m_diffuse_trans(0.0f), m_glossy(0.0f, NULL, NULL, NULL, 0u), m_glossy_trans(1.0f,true), m_fresnel(0.0f) {}
196  #else
197  m_diffuse(0.0f), m_diffuse_trans(0.0f), m_glossy(1.0f), m_glossy_trans(1.0f,true), m_fresnel(0.0f) {}
198  #endif
199 
202  FERMAT_HOST_DEVICE
203  Bsdf(const Bsdf& bsdf) :
204  m_diffuse(bsdf.m_diffuse),
205  m_diffuse_trans(bsdf.m_diffuse_trans),
206  m_glossy(bsdf.m_glossy),
207  m_glossy_trans(bsdf.m_glossy_trans),
208  m_fresnel(bsdf.m_fresnel),
209  m_reflectivity(bsdf.m_reflectivity),
210  m_ior(bsdf.m_ior),
211  m_opacity(bsdf.m_opacity),
212  m_clearcoat_ior(bsdf.m_clearcoat_ior),
213  m_glossy_reflectance(bsdf.m_glossy_reflectance),
214  m_transport(bsdf.m_transport) {}
215 
218  FERMAT_HOST_DEVICE
220  const TransportType transport,
221  const RenderingContextView renderer,
222  const MeshMaterial material,
223  const float mollification_factor = 1.0f,
224  const float mollification_bias = 0.0f,
225  const float min_roughness = 0.0f) :
226  m_diffuse(cugar::Vector3f(material.diffuse.x, material.diffuse.y, material.diffuse.z) / M_PIf),
227  m_diffuse_trans(cugar::Vector3f(material.diffuse_trans.x, material.diffuse_trans.y, material.diffuse_trans.z) / M_PIf),
228  #if defined(USE_LTC)
229  m_glossy(cugar::max(material.roughness * mollification_factor + mollification_bias, min_roughness), renderer.ltc_M, renderer.ltc_Minv, renderer.ltc_A, renderer.ltc_size),
230  #else
231  m_glossy(cugar::max(material.roughness * mollification_factor + mollification_bias, min_roughness)),
232  m_glossy_trans(material.roughness , true, material.index_of_refraction, 1.0f),
233  #endif
234  m_fresnel(cugar::Vector3f(material.specular.x, material.specular.y, material.specular.z) / M_PIf),
235  m_reflectivity(cugar::Vector3f(material.reflectivity.x, material.reflectivity.y, material.reflectivity.z)),
236  m_ior(material.index_of_refraction),
237  m_opacity(material.opacity),
238  m_glossy_reflectance(renderer.glossy_reflectance),
239  m_transport(transport)
240  {
241  const float R0 = cugar::min( cugar::max_comp( m_reflectivity ), 0.95f );
242  m_clearcoat_ior = (1 + sqrtf(R0)) / (1 - sqrtf(R0)); // calculate the clearcoat's IOR based on the normal incidence reflectivity
243  }
244 
247  FERMAT_HOST_DEVICE
249  const TransportType transport,
250  const RenderingContextView renderer,
251  const cugar::Vector3f diffuse,
252  const cugar::Vector3f specular,
253  const float roughness,
255  const float opacity = 1.0f,
256  const float ior = 1.0f) :
257  m_diffuse(diffuse / M_PIf),
258  m_diffuse_trans(diffuse_trans / M_PIf),
259  #if defined(USE_LTC)
260  m_glossy(roughness, renderer.ltc_M, renderer.ltc_Minv, renderer.ltc_A, renderer.ltc_size),
261  #else
262  m_glossy(roughness),
263  m_glossy_trans(roughness, true, ior, 1.0f),
264  #endif
265  m_fresnel(specular / M_PIf),
266  m_ior(ior),
267  m_opacity(opacity),
268  m_glossy_reflectance(renderer.glossy_reflectance),
269  m_transport(transport)
270  {
271  const float R0 = cugar::min( cugar::max_comp( m_reflectivity ), 0.95f );
272  m_clearcoat_ior = (1 + sqrtf(R0)) / (1 - sqrtf(R0)); // calculate the clearcoat's IOR based on the normal incidence reflectivity
273  }
274 
277  FERMAT_HOST_DEVICE
278  const diffuse_trans_component& diffuse_trans() const { return m_diffuse_trans; }
279 
282  FERMAT_HOST_DEVICE
283  const diffuse_component& diffuse() const { return m_diffuse; }
284 
287  FERMAT_HOST_DEVICE
288  const glossy_component& glossy() const { return m_glossy; }
289 
292  FERMAT_HOST_DEVICE
293  const glossy_component& glossy_trans() const { return m_glossy_trans; }
294 
297  FERMAT_HOST_DEVICE
298  float get_eta(const float NoV) const { return NoV > 0.0f ? 1.0f / m_ior : m_ior; }
299 
302  FERMAT_HOST_DEVICE
303  float get_inv_eta(const float NoV) const { return NoV > 0.0f ? m_ior : 1.0f / m_ior; }
304 
311  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
313  const cugar::DifferentialGeometry& geometry,
314  const cugar::Vector3f w_i,
315  const cugar::Vector3f w_o,
316  const ComponentType components = kAllComponents) const
317  {
318  // compute the true component weights together with the transmitted directions and coefficients
319  cugar::Vector3f Fc_1, Tc_1;
320  cugar::Vector3f Fc_2, Tc_2;
321  cugar::Vector3f w[4];
322  component_weights( geometry, w_i, w_o, Fc_1, Tc_1, Fc_2, Tc_2, w );
323 
324  const float factor = compression_factor( geometry, w_i, w_o );
325 
326  return
327  ((components & kDiffuseReflection) ? m_diffuse.f(geometry, w_i, w_o) * w[kDiffuseReflectionIndex] * factor : 0.0f) +
328  ((components & kDiffuseTransmission) ? m_diffuse_trans.f(geometry, w_i, w_o) * w[kDiffuseTransmissionIndex] * factor : 0.0f) +
329  ((components & kGlossyReflection) ? m_glossy.f(geometry, w_i, w_o) * w[kGlossyReflectionIndex] * factor : 0.0f) +
330  ((components & kGlossyTransmission) ? m_glossy_trans.f(geometry, w_i, w_o) * w[kGlossyTransmissionIndex] * factor : 0.0f);
331  }
332 
335  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
336  void f(
337  const cugar::DifferentialGeometry& geometry,
338  const cugar::Vector3f w_i,
339  const cugar::Vector3f w_o,
340  cugar::Vector3f* f) const
341  {
342  // compute the true component weights together with the transmitted directions and coefficients
343  cugar::Vector3f Fc_1, Tc_1;
344  cugar::Vector3f Fc_2, Tc_2;
345  cugar::Vector3f w[4];
346  component_weights( geometry, w_i, w_o, Fc_1, Tc_1, Fc_2, Tc_2, w );
347 
348  cugar::Vector3f f_d, f_g, f_dt, f_gt;
349 
350  f_d = m_diffuse.f(geometry, w_i, w_o);
351  f_g = m_diffuse_trans.f(geometry, w_i, w_o);
352  f_dt = m_glossy.f(geometry, w_i, w_o);
353  f_gt = m_glossy_trans.f(geometry, w_i, w_o);
354 
355  const float factor = compression_factor( geometry, w_i, w_o );
356 
357  f[kDiffuseReflectionIndex] = f_d * w[kDiffuseReflectionIndex] * factor;
358  f[kDiffuseTransmissionIndex] = f_dt * w[kDiffuseTransmissionIndex] * factor;
359  f[kGlossyReflectionIndex] = f_g * w[kGlossyReflectionIndex] * factor;
360  f[kGlossyTransmissionIndex] = f_gt * w[kGlossyTransmissionIndex] * factor;
361  }
362 
365  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
366  void f_and_p(
367  const cugar::DifferentialGeometry& geometry,
368  const cugar::Vector3f w_i,
369  const cugar::Vector3f w_o,
371  float* p,
372  const cugar::SphericalMeasure measure = cugar::kProjectedSolidAngle,
373  bool RR = true) const
374  {
375  // compute the true component weights together with the transmitted directions and coefficients
376  cugar::Vector3f Fc_1, Tc_1;
377  cugar::Vector3f Fc_2, Tc_2;
378  cugar::Vector3f w[4];
379  component_weights( geometry, w_i, w_o, Fc_1, Tc_1, Fc_2, Tc_2, w );
380 
381  // compute the coat reflection & transmission probabilities
382  float coat_reflection_prob = cugar::average(Fc_1);
383  float coat_transmission_prob = 1.0f - coat_reflection_prob;
384 
385  // evaluate the inner layers
386  cugar::Vector3f f_d, f_g, f_dt, f_gt;
387  float p_d, p_g, p_dt, p_gt;
388 
389  m_diffuse.f_and_p(geometry, w_i, w_o, f_d, p_d, measure);
390  m_diffuse_trans.f_and_p(geometry, w_i, w_o, f_dt, p_dt, measure);
391  m_glossy.f_and_p(geometry, w_i, w_o, f_g, p_g, measure);
392  m_glossy_trans.f_and_p(geometry, w_i, w_o, f_gt, p_gt, measure);
393 
394  // compute the sampling weights for the inner layers
395  float w_p[4];
396  sampling_weights(geometry, w_i, w_p);
397 
398  // modulate the sampling weights by the coat transmission probability
399  normalize_sampling_weights( w_p, coat_reflection_prob, coat_transmission_prob, RR );
400 
401  p[kDiffuseReflectionIndex] = p_d * w_p[kDiffuseReflectionIndex];
402  p[kDiffuseTransmissionIndex] = p_dt * w_p[kDiffuseTransmissionIndex];
403  p[kGlossyReflectionIndex] = p_g * w_p[kGlossyReflectionIndex];
404  p[kGlossyTransmissionIndex] = p_gt * w_p[kGlossyTransmissionIndex];
405 
406  const float factor = compression_factor( geometry, w_i, w_o );
407 
408  f[kDiffuseReflectionIndex] = f_d * w[kDiffuseReflectionIndex] * factor;
409  f[kDiffuseTransmissionIndex] = f_dt * w[kDiffuseTransmissionIndex] * factor;
410  f[kGlossyReflectionIndex] = f_g * w[kGlossyReflectionIndex] * factor;
411  f[kGlossyTransmissionIndex] = f_gt * w[kGlossyTransmissionIndex] * factor;
412  }
413 
416  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
417  void f_and_p(
418  const cugar::DifferentialGeometry& geometry,
419  const cugar::Vector3f w_i,
420  const cugar::Vector3f w_o,
422  float& p,
423  const cugar::SphericalMeasure measure = cugar::kProjectedSolidAngle,
424  const bool RR = true,
425  const ComponentType components = kAllComponents) const
426  {
427  // compute the true component weights together with the transmitted directions and coefficients
428  cugar::Vector3f Fc_1, Tc_1;
429  cugar::Vector3f Fc_2, Tc_2;
430  cugar::Vector3f w[4];
431  component_weights( geometry, w_i, w_o, Fc_1, Tc_1, Fc_2, Tc_2, w );
432 
433  // compute the coat reflection & transmission probabilities
434  float coat_reflection_prob = cugar::average(Fc_1);
435  float coat_transmission_prob = 1.0f - coat_reflection_prob;
436 
437  // evaluate the inner layers
438  cugar::Vector3f f_d(0.0f), f_g(0.0f), f_dt(0.0f), f_gt(0.0f);
439  float p_d(0.0f), p_g(0.0f), p_dt(0.0f), p_gt(0.0f);
440 
441  if (components & kDiffuseReflection) m_diffuse.f_and_p(geometry, w_i, w_o, f_d, p_d, measure);
442  if (components & kDiffuseTransmission) m_diffuse_trans.f_and_p(geometry, w_i, w_o, f_dt, p_dt, measure);
443  if (components & kGlossyReflection) m_glossy.f_and_p(geometry, w_i, w_o, f_g, p_g, measure);
444  if (components & kGlossyTransmission) m_glossy_trans.f_and_p(geometry, w_i, w_o, f_gt, p_gt, measure);
445 
446  // compute the sampling weights for the inner layers
447  float w_p[4];
448  sampling_weights(geometry, w_i, w_p);
449 
450  // modulate the sampling weights by the coat transmission probability
451  normalize_sampling_weights( w_p, coat_reflection_prob, coat_transmission_prob, RR, components );
452 
453  p = p_d * w_p[kDiffuseReflectionIndex] +
454  p_dt * w_p[kDiffuseTransmissionIndex] +
455  p_g * w_p[kGlossyReflectionIndex] +
456  p_gt * w_p[kGlossyTransmissionIndex];
457 
458  const float factor = compression_factor( geometry, w_i, w_o );
459 
460  f = f_d * w[kDiffuseReflectionIndex] * factor +
461  f_dt * w[kDiffuseTransmissionIndex] * factor +
462  f_g * w[kGlossyReflectionIndex] * factor +
463  f_gt * w[kGlossyTransmissionIndex] * factor;
464  }
465 
473  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
474  float p(
475  const cugar::DifferentialGeometry& geometry,
476  const cugar::Vector3f w_i,
477  const cugar::Vector3f w_o,
478  const cugar::SphericalMeasure measure = cugar::kProjectedSolidAngle,
479  const bool RR = true,
480  const ComponentType components = kAllComponents) const
481  {
482  // evaluate the clearcoat's transmission
483  cugar::Vector3f H_c;
484  cugar::Vector3f Fc_1;
485  cugar::Vector3f Tc_1;
486  float eta_c;
487  float cos_theta_i;
488 
489  if (!clearcoat_transmission(
490  geometry,
491  w_i,
492  H_c,
493  cos_theta_i,
494  eta_c,
495  Fc_1,
496  Tc_1 ))
497  {
498  // total internal reflection - no chance to enter the clearcoat layer
499  return 0.0f;
500  }
501 
502  // compute the coat reflection & transmission probabilities
503  float coat_reflection_prob = cugar::average(Fc_1);
504  float coat_transmission_prob = 1.0f - coat_reflection_prob;
505 
506  // compute the sampling weights for the inner layers
507  float w_p[4];
508  sampling_weights(geometry, w_i, w_p);
509 
510  // modulate the sampling weights by the coat transmission probability
511  normalize_sampling_weights( w_p, coat_reflection_prob, coat_transmission_prob, RR, components );
512 
513  float p_d(0.0f), p_g(0.0f), p_dt(0.0f), p_gt(0.0f);
514 
515  if (components & kDiffuseReflection) p_d = m_diffuse.p(geometry, w_i, w_o, measure);
516  if (components & kDiffuseTransmission) p_dt = m_diffuse_trans.p(geometry, w_i, w_o, measure);
517  if (components & kGlossyReflection) p_g = m_glossy.p(geometry, w_i, w_o, measure);
518  if (components & kGlossyTransmission) p_gt = m_glossy_trans.p(geometry, w_i, w_o, measure);
519 
520  return
521  p_d * w_p[kDiffuseReflectionIndex] +
522  p_dt * w_p[kDiffuseTransmissionIndex] +
523  p_g * w_p[kGlossyReflectionIndex] +
524  p_gt * w_p[kGlossyTransmissionIndex];
525  }
526 
529  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
531  const cugar::DifferentialGeometry& geometry,
532  const cugar::Vector3f V,
533  float& diffuse_refl_prob,
534  float& diffuse_trans_prob,
535  float& glossy_refl_prob,
536  float& glossy_trans_prob) const
537  {
538  const float NoV_signed = cugar::dot(geometry.normal_s, V);
539  const float NoV = fabsf( NoV_signed );
540 
541  cugar::Vector3f r_coeff;
542  cugar::Vector3f t_coeff;
543 
544  if (m_ior == 0) // suppress the glossy layer
545  {
546  r_coeff = cugar::Vector3f(0.0f);
547  t_coeff = cugar::Vector3f(1.0f);
548  }
549  else
550  {
551  r_coeff = glossy_reflectance( NoV_signed );
552  t_coeff = cugar::Vector3f(1.0f - cugar::max_comp(r_coeff));
553  }
554 
555  #if DIFFUSE_ONLY
556  // disable Fresnel mixing - allow diffuse only
557  r_coeff = cugar::Vector3f(0.0f);
558  t_coeff = cugar::Vector3f(1.0f);
559  #elif SPECULAR_ONLY
560  // disable Fresnel mixing - allow specular only
561  r_coeff = cugar::Vector3f(1.0f);
562  t_coeff = cugar::Vector3f(0.0f);
563  #endif
564  #if SUPPRESS_SPECULAR
565  r_coeff = cugar::Vector3f(0.0f);
566  #endif
567  #if SUPPRESS_DIFFUSE
568  t_coeff = cugar::Vector3f(0.0f);
569  #endif
570 
571  glossy_refl_prob = cugar::max_comp( r_coeff );
572  glossy_trans_prob = (1 - m_opacity) * cugar::max_comp( t_coeff );
573  diffuse_refl_prob = m_opacity * cugar::max_comp( t_coeff * m_diffuse.color ) * M_PIf;
574  diffuse_trans_prob = m_opacity * cugar::max_comp( t_coeff * m_diffuse_trans.color ) * M_PIf;
575  }
576 
579  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
581  const cugar::DifferentialGeometry& geometry,
582  const cugar::Vector3f V,
583  float* w) const
584  {
585  sampling_weights( geometry, V, w[kDiffuseReflectionIndex], w[kDiffuseTransmissionIndex], w[kGlossyReflectionIndex], w[kGlossyTransmissionIndex] );
586  }
587 
590  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
592  float* w_p,
593  float& coat_reflection_prob,
594  float& coat_transmission_prob,
595  const bool RR,
596  const ComponentType components = kAllComponents) const
597  {
598  // set to zero unwanted components
599  if ((components & kDiffuseReflection) == 0) w_p[kDiffuseReflectionIndex] = 0;
600  if ((components & kDiffuseTransmission) == 0) w_p[kDiffuseTransmission] = 0;
601  if ((components & kGlossyReflection) == 0) w_p[kGlossyReflection] = 0;
602  if ((components & kGlossyTransmission) == 0) w_p[kGlossyTransmission] = 0;
603  if ((components & kClearcoatReflection) == 0) { coat_reflection_prob = 0; coat_transmission_prob = 1; }
604 
605  // modulate the sampling weights by the coat transmission probability
606  w_p[kDiffuseReflectionIndex] *= coat_transmission_prob;
607  w_p[kDiffuseTransmissionIndex] *= coat_transmission_prob;
608  w_p[kGlossyReflectionIndex] *= coat_transmission_prob;
609  w_p[kGlossyTransmissionIndex] *= coat_transmission_prob;
610 
611  if (RR == false)
612  {
613  const float inv_sum = 1.0f / (
614  w_p[kDiffuseReflectionIndex] +
615  w_p[kDiffuseTransmissionIndex] +
616  w_p[kGlossyReflectionIndex] +
617  w_p[kGlossyTransmissionIndex] +
618  coat_reflection_prob);
619 
620  w_p[kDiffuseReflectionIndex] *= inv_sum;
621  w_p[kDiffuseTransmissionIndex] *= inv_sum;
622  w_p[kGlossyReflectionIndex] *= inv_sum;
623  w_p[kGlossyTransmissionIndex] *= inv_sum;
624  coat_reflection_prob *= inv_sum;
625  coat_transmission_prob *= inv_sum;
626  }
627  }
628 
631  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
633  const float VoH,
634  const float eta,
635  cugar::Vector3f& r_coeff,
636  cugar::Vector3f& t_coeff) const
637  {
638  if (eta == 0.0f) // NOTE: eta == m_ior == 0 signals the complete suppression of glossy reflections
639  {
640  r_coeff = cugar::Vector3f(0.0f);
641  t_coeff = cugar::Vector3f(1.0f);
642  }
643  else
644  {
645  r_coeff = cugar::fresnel_schlick(VoH, eta, m_fresnel);
646  t_coeff = cugar::Vector3f(1.0f - cugar::max_comp(r_coeff));
647  }
648 
649  #if DIFFUSE_ONLY
650  // disable Fresnel mixing - allow diffuse only
651  r_coeff = cugar::Vector3f(0.0f);
652  t_coeff = cugar::Vector3f(1.0f);
653  #elif SPECULAR_ONLY
654  // disable Fresnel mixing - allow specular only
655  r_coeff = cugar::Vector3f(1.0f);
656  t_coeff = cugar::Vector3f(0.0f);
657  #endif
658  #if SUPPRESS_SPECULAR
659  r_coeff = cugar::Vector3f(0.0f);
660  #endif
661  #if SUPPRESS_DIFFUSE
662  t_coeff = cugar::Vector3f(0.0f);
663  #endif
664  }
665 
668  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
670  const cugar::DifferentialGeometry& geometry,
671  const cugar::Vector3f V,
672  const cugar::Vector3f L,
673  cugar::Vector3f& r_coeff,
674  cugar::Vector3f& t_coeff) const
675  {
676  float eta = 0.0f;
677  float inv_eta = 0.0f;
678  float VoH = 0.0f;
679 
680  if (m_ior)
681  {
682  const cugar::Vector3f N = geometry.normal_s;
683 
684  eta = dot(N, V) > 0.0f ? 1.0f / m_ior : m_ior;
685  inv_eta = dot(N, V) > 0.0f ? m_ior : 1.0f / m_ior;
686 
687  // recover the microfacet H
688  const cugar::Vector3f H = microfacet(V, L, N, inv_eta);
689 
690  VoH = dot(V,H);
691  }
692  // NOTE: eta == m_ior == 0 signals the complete suppression of glossy reflections
693 
694  fresnel_weights( VoH, eta, r_coeff, t_coeff );
695  }
696 
700  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
702  const cugar::DifferentialGeometry& geometry,
703  const cugar::Vector3f V,
704  const cugar::Vector3f L,
705  cugar::Vector3f& diffuse_refl_coeff,
706  cugar::Vector3f& diffuse_trans_coeff,
707  cugar::Vector3f& glossy_refl_coeff,
708  cugar::Vector3f& glossy_trans_coeff) const
709  {
710  cugar::Vector3f w[4];
711  inner_component_weights( geometry, V, L, w );
712 
713  glossy_refl_coeff = w[kGlossyReflectionIndex];
714  glossy_trans_coeff = w[kGlossyTransmissionIndex];
715  diffuse_refl_coeff = w[kDiffuseReflectionIndex];
716  diffuse_trans_coeff = w[kDiffuseTransmissionIndex];
717  }
718 
721  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
723  const cugar::DifferentialGeometry& geometry,
724  const cugar::Vector3f V,
725  const cugar::Vector3f L,
726  cugar::Vector3f* w) const
727  {
728  cugar::Vector3f r_coeff, t_coeff;
729  fresnel_weights( geometry, V, L, r_coeff, t_coeff );
730 
731  // compute an additional weight for the diffuse (or matte) component, necessary for energy conservation,
732  // as explained in:
733  // "A Microfacet Based Coupled Specular-Matte BRDF Model with Importance Sampling",
734  // Csaba Kelemen and László Szirmay-Kalos
735  const float diffuse_weight =
736  (1.0f - glossy_reflectance( dot(geometry.normal_s,V) )) *
737  (1.0f - glossy_reflectance( dot(geometry.normal_s,L) ));
738 
739  w[kGlossyReflectionIndex] = r_coeff;
740  w[kGlossyTransmissionIndex] = t_coeff * (1 - m_opacity);
741  w[kDiffuseReflectionIndex] = t_coeff * m_opacity * diffuse_weight;
742  w[kDiffuseTransmissionIndex] = t_coeff * m_opacity * diffuse_weight;
743  }
744 
747  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
749  const cugar::DifferentialGeometry& geometry,
750  const cugar::Vector3f w_i,
751  const cugar::Vector3f w_o,
752  cugar::Vector3f& Fc_1,
753  cugar::Vector3f& Tc_1,
754  cugar::Vector3f& Fc_2,
755  cugar::Vector3f& Tc_2,
756  cugar::Vector3f* w) const
757  {
758  // calculate the transmitted incident vector seen from the inner layers
759  cugar::Vector3f H_c;
760  float eta_c;
761  float cos_theta_i;
762 
763  if (!clearcoat_transmission(
764  geometry,
765  w_i,
766  H_c,
767  cos_theta_i,
768  eta_c,
769  Fc_1,
770  Tc_1 ))
771  {
772  // total internal reflection - no chance to enter the clearcoat layer
773  w[kGlossyReflectionIndex] = 0.0f;
774  w[kGlossyTransmissionIndex] = 0.0f;
775  w[kDiffuseReflectionIndex] = 0.0f;
776  w[kDiffuseTransmissionIndex] = 0.0f;
777  return;
778  }
779 
780  // Here we blatantly ignore the second refraction to exit the clearcoat layer - as accounting for that explicitly
781  // would lead to severe energy loss due to the lack of multiple-scattering.
782  // A much better alternative we plan to adopt is the adding-doubling algorithm proposed by L.Belcour.
783  Fc_2 = 0.0f;
784  Tc_2 = 1.0f - Fc_2;
785 
786  inner_component_weights( geometry, w_i, w_o, w );
787 
788  w[kGlossyReflectionIndex] *= Tc_1 * Tc_2;
789  w[kGlossyTransmissionIndex] *= Tc_1 * Tc_2;
790  w[kDiffuseReflectionIndex] *= Tc_1 * Tc_2;
791  w[kDiffuseTransmissionIndex] *= Tc_1 * Tc_2;
792  }
793 
796  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
798  const cugar::DifferentialGeometry& geometry,
799  const cugar::Vector3f w_i,
800  const cugar::Vector3f w_o,
801  cugar::Vector3f* w) const
802  {
803  cugar::Vector3f Fc_1;
804  cugar::Vector3f Tc_1;
805  cugar::Vector3f Fc_2;
806  cugar::Vector3f Tc_2;
807 
808  component_weights( geometry, w_i, w_o, Fc_1, Tc_1, Fc_2, Tc_2, w );
809  }
810 
824  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
826  const cugar::DifferentialGeometry& geometry,
827  const float z[3],
828  const cugar::Vector3f in,
829  const ComponentType out_comp,
830  cugar::Vector3f& out,
831  float& out_p,
832  float& out_p_proj,
833  cugar::Vector3f& out_g,
834  const bool evaluate_full_bsdf = false) const
835  {
836  cugar::Vector3f g(0.0f);
837  float p(0.0f);
838  float p_proj(0.0f);
839 
840  if (out_comp == kDiffuseReflection)
841  {
842  // sample the diffuse component
843  const Bsdf::diffuse_component component = diffuse();
844 
845  component.sample(cugar::Vector3f(z[0], z[1], z[2]), geometry, in, out, g, p, p_proj);
846  }
847  else if (out_comp == kGlossyReflection)
848  {
849  // sample the glossy component
850  const Bsdf::glossy_component component = glossy();
851 
852  component.sample(cugar::Vector3f(z[0], z[1], z[2]), geometry, in, out, g, p, p_proj);
853  }
854  else if (out_comp == kDiffuseTransmission)
855  {
856  // sample the diffuse component
857  const Bsdf::diffuse_trans_component component = diffuse_trans();
858 
859  component.sample(cugar::Vector3f(z[0], z[1], z[2]), geometry, in, out, g, p, p_proj);
860  }
861  else if (out_comp == kGlossyTransmission)
862  {
863  // sample the glossy component
864  const Bsdf::glossy_component& component = glossy_trans();
865 
866  component.sample(cugar::Vector3f(z[0], z[1], z[2]), geometry, in, out, g, p, p_proj);
867  }
868 
869  if (out_comp != kAbsorption)
870  {
871  // re-evaluate the entire BSDF
872  if (evaluate_full_bsdf)
873  {
874  g = this->f(geometry, in, out, kAllComponents) / p_proj;
875  }
876  else
877  {
878  // evaluate the true weights, now that the output direction is known
879  cugar::Vector3f w[4];
880  component_weights(geometry, in, out, w);
881 
882  // re-weight the bsdf value
883  g *=
884  (out_comp & kGlossyReflection) ? w[kGlossyReflectionIndex] :
885  (out_comp & kGlossyTransmission) ? w[kGlossyTransmissionIndex] :
886  (out_comp & kDiffuseReflection) ? w[kDiffuseReflectionIndex] :
887  w[kDiffuseTransmissionIndex];
888  }
889 
890  const float factor = compression_factor( geometry, in, out );
891 
892  out_p = p;
893  out_p_proj = p_proj;
894  out_g = g * factor;
895  return true;
896  }
897  else
898  {
899  out = cugar::Vector3f(0.0f);
900  out_p = 0.0f;
901  out_p_proj = 0.0f;
902  out_g = cugar::Vector3f(0.0f);
903  return false;
904  }
905  }
906 
920  FERMAT_HOST_DEVICE FERMAT_FORCEINLINE
921  bool sample(
922  const cugar::DifferentialGeometry& geometry,
923  const float z[3],
924  const cugar::Vector3f in,
925  ComponentType& out_comp,
926  cugar::Vector3f& out,
927  float& out_p,
928  float& out_p_proj,
929  cugar::Vector3f& out_g,
930  bool RR = true,
931  bool evaluate_full_bsdf = false,
932  const ComponentType components = kAllComponents) const
933  {
934  cugar::Vector3f g(0.0f);
935  float p(0.0f);
936  float p_proj(0.0f);
937  float z_norm(0.0f);
938  float p_comp(0.0f);
939 
940  cugar::Vector3f w_i = in;
941  cugar::Vector3f w_o;
942 
943  //
944  // 1. compute the Fresnel term Fc due to the coating layer
945  // 2. use Fc to derive sampling probabilities of all layers (i.e. multiplying their pdf by (1-Fc))
946  // 2b. normalize all probabilities if RR is disabled
947  // 3. sample reflection from the coating layer with probability Fc
948  // 4. choose an inner layer with the above probabilities
949  // 5. refract the output from the selected inner layer through the coating layer
950  //
951 
952  // We assume the clearcoat is actually made of two layers, C_o and C_i: one with a relative index of refraction (eta)
953  // of eta with respect to the outer medium, and another adjacent one with a relative index of refraction
954  // of eta with respect to the inner medium.
955  // i.e. ior(C_o)/ior(O) = eta
956  // i.e. ior(C_i)/ior(I) = eta
957  // Now, this means that if the material's IOR specifies the ratio ior(I)/ior(O), we have:
958  //
959  // ior(C_i)/ior(C_o) = ior(I)/ior(O)
960  //
961  // We suppose the substrate GGX/diffuse layer sits at the surface between C_o and C_i, implying that
962  // the transmissive component of that layer sees exactly the same ratio eta = ior(I)/ior(O).
963  // In practice, this means that rays that go through all the layers will simply see a combined refraction
964  // of eta.
965  // In theory of course modeling the individual interactions would be significant for multiple scattering
966  // within the medium, yet that would be rather expensive and require stochastic evaluation, e.g. using
967  // the scheme proposed in:
968  // "Position-Free Monte Carlo Simulation for Arbitrary Layered BSDFs", Yu Guo, Milos Hasan, Shuang Zhao.
969  // A more convenient approach is the one taken by:
970  // "Efficient Rendering of Layered Materials using an Atomic Decomposition with Statistical Operators",
971  // Laurent Belcour - in ACM Transactions on Graphics, Association for Computing Machinery, 2018.
972  // While we plan to adopt the full machinery of the latter in the future, we currently just approximate it.
973 
974  cugar::Vector3f H_c;
975  cugar::Vector3f Fc_1;
976  cugar::Vector3f Tc_1;
977  float eta_c;
978  float cos_theta_i;
979 
980  if (!clearcoat_transmission(
981  geometry,
982  in,
983  H_c,
984  cos_theta_i,
985  eta_c,
986  Fc_1,
987  Tc_1 ))
988  {
989  // total internal reflection - no chance to enter the clearcoat layer
990  out = cugar::Vector3f(0.0f);
991  out_p = 0.0f;
992  out_p_proj = 0.0f;
993  out_g = cugar::Vector3f(0.0f);
994  return false;
995  }
996 
997  float coat_reflection_prob = cugar::average(Fc_1);
998  float coat_transmission_prob = 1.0f - coat_reflection_prob;
999 
1000  // evaluate the sampling weights for the inner layers
1001  float w_p[4];
1002  sampling_weights(geometry, in, w_p);
1003 
1004  #if USE_EFFICIENT_SAMPLER_WITH_APPROXIMATE_PDFS
1005  //
1006  // The following scheme is orders of magnitude more efficient at sampling the glossy/specular layer near normal incidence,
1007  // as the a-priori glossy sampling weights calculated using the *average* glossy reflectance tend to be way too low for high
1008  // values of cos(theta).
1009  //
1010 
1011  // sample the GGX microfacet distribution
1012  glossy_component::distribution_type glossy_distribution = m_glossy.distribution();
1013 
1014  // move to local coordinates
1015  const cugar::Vector3f V_local = geometry.to_local( w_i );
1016 
1017  const cugar::Vector3f H_local = glossy_distribution.sample( cugar::Vector3f(z[0], z[1], z[2]), V_local );
1018 
1019  // move to world coordinates
1020  const cugar::Vector3f H = geometry.from_local( H_local );
1021 
1022  // compute the Fresnel coefficients
1023  cugar::Vector3f r_coeff, t_coeff;
1024 
1025  // compute eta based on the surface side
1026  const float eta = V_local.z > 0.0f ? 1.0f / m_ior : m_ior;
1027 
1028  fresnel_weights( dot(V_local, H_local), eta, r_coeff, t_coeff );
1029 
1030  // assign component probabilities
1031  w_p[kGlossyReflectionIndex] = (w_p[kGlossyReflectionIndex] + cugar::max_comp( r_coeff ) ) * 0.5f;
1032  w_p[kGlossyTransmissionIndex] = (w_p[kGlossyTransmissionIndex] + (1 - m_opacity) * cugar::max_comp( t_coeff )) * 0.5f;
1033  w_p[kDiffuseReflectionIndex] = (w_p[kDiffuseReflectionIndex] + m_opacity * cugar::max_comp( t_coeff * m_diffuse.color ) * M_PIf) * 0.5f;
1034  w_p[kDiffuseTransmissionIndex] = (w_p[kDiffuseTransmissionIndex] + m_opacity * cugar::max_comp( t_coeff * m_diffuse_trans.color ) * M_PIf) * 0.5f;
1035  #endif
1036 
1037  // modulate the sampling weights by the cleacoat sampling probability
1038  normalize_sampling_weights( w_p, coat_reflection_prob, coat_transmission_prob, RR, components );
1039 
1040  // select a BSDF component with z[2]
1041  if (z[2] < w_p[kDiffuseReflectionIndex])
1042  {
1043  // sample the diffuse reflection component
1044  const Bsdf::diffuse_component component = diffuse();
1045 
1046  z_norm = z[2] / w_p[kDiffuseReflectionIndex];
1047  p_comp = w_p[kDiffuseReflectionIndex];
1048 
1049  component.sample(cugar::Vector3f(z[0], z[1], z_norm), geometry, w_i, w_o, g, p, p_proj);
1050 
1051  out_comp = kDiffuseReflection;
1052  }
1053  else if (z[2] < w_p[kDiffuseReflectionIndex] +
1054  w_p[kGlossyReflectionIndex])
1055  {
1056  // sample the glossy reflection component
1057  const Bsdf::glossy_component component = glossy();
1058 
1059  z_norm = (z[2] - w_p[kDiffuseReflectionIndex]) / w_p[kGlossyReflectionIndex];
1060  p_comp = w_p[kGlossyReflectionIndex];
1061 
1062  #if !USE_EFFICIENT_SAMPLER_WITH_APPROXIMATE_PDFS
1063  component.sample(cugar::Vector3f(z[0], z[1], z_norm), geometry, w_i, w_o, g, p, p_proj);
1064  #else
1065  // sample L given H
1066  component.sample( geometry, H, w_i, w_o, g, p, p_proj );
1067  #endif
1068  out_comp = kGlossyReflection;
1069  }
1070  else if (z[2] < w_p[kDiffuseReflectionIndex] +
1071  w_p[kGlossyReflectionIndex] +
1072  w_p[kDiffuseTransmissionIndex])
1073  {
1074  // sample the diffuse transmission component
1075  const Bsdf::diffuse_trans_component component = diffuse_trans();
1076 
1077  z_norm = (z[2] - w_p[kDiffuseReflectionIndex] - w_p[kGlossyReflectionIndex]) / w_p[kDiffuseTransmissionIndex];
1078  p_comp = w_p[kDiffuseTransmissionIndex];
1079 
1080  component.sample(cugar::Vector3f(z[0], z[1], z_norm), geometry, w_i, w_o, g, p, p_proj);
1081 
1082  out_comp = kDiffuseTransmission;
1083  }
1084  else if (z[2] < w_p[kDiffuseReflectionIndex] +
1085  w_p[kGlossyReflectionIndex] +
1086  w_p[kDiffuseTransmissionIndex] +
1087  w_p[kGlossyTransmissionIndex])
1088  {
1089  // sample the glossy transmission component
1090  const Bsdf::glossy_component& component = glossy_trans();
1091 
1092  z_norm = (z[2] - w_p[kDiffuseReflectionIndex] - w_p[kGlossyReflectionIndex] - w_p[kDiffuseTransmissionIndex]) / w_p[kGlossyTransmissionIndex];
1093  p_comp = w_p[kGlossyTransmissionIndex];
1094 
1095  #if !USE_EFFICIENT_SAMPLER_WITH_APPROXIMATE_PDFS
1096  component.sample(cugar::Vector3f(z[0], z[1], z_norm), geometry, w_i, w_o, g, p, p_proj);
1097  #else
1098  // sample L given H
1099  component.sample( geometry, H, w_i, w_o, g, p, p_proj );
1100  #endif
1101 
1102  out_comp = kGlossyTransmission;
1103  }
1104  else if (z[2] < w_p[kDiffuseReflectionIndex] +
1105  w_p[kGlossyReflectionIndex] +
1106  w_p[kDiffuseTransmissionIndex] +
1107  w_p[kGlossyTransmissionIndex] +
1108  coat_reflection_prob)
1109  {
1110  // sample the clearcoat reflection
1111  z_norm = (z[2] - w_p[kDiffuseReflectionIndex] - w_p[kGlossyReflectionIndex] - w_p[kDiffuseTransmissionIndex] - w_p[kGlossyTransmissionIndex]) / coat_reflection_prob;
1112  p_comp = coat_reflection_prob;
1113 
1114  // compute the reflection direction
1115  out = 2 * cos_theta_i * H_c - in;
1116  g = Fc_1 / p_comp; // NOTE: we pre-divide by p_comp here, unlike for other components
1117  p_proj = cugar::float_infinity();
1118  p = cugar::float_infinity();
1119 
1120  out_comp = kClearcoatReflection;
1121  }
1122  else
1123  {
1124  out_comp = kAbsorption;
1125  }
1126 
1127  if (out_comp != kAbsorption &&
1128  out_comp != kClearcoatReflection)
1129  {
1130  // Here we blatantly ignore the second refraction to exit the clearcoat layer - as accounting for that explicitly
1131  // would lead to severe energy loss due to the lack of multiple-scattering.
1132  // A much better alternative we plan to adopt is the adding-doubling algorithm proposed by L.Belcour.
1133  cugar::Vector3f Fc_2 = 0.0f;
1134  cugar::Vector3f Tc_2 = 1.0f - Fc_2;
1135 
1136  // modulate g by both clearcoat transmission terms
1137  g *= Tc_1 * Tc_2;
1138 
1139  // write out the output direction
1140  out = w_o;
1141  }
1142 
1143  // TODO: chose the channel based on our new BSDF factorization heuristic
1144  if (out_comp != kAbsorption)
1145  {
1146  // re-evaluate the entire BSDF and its sampling probabilities
1147  if (out_comp != kClearcoatReflection)
1148  {
1149  if (evaluate_full_bsdf)
1150  {
1151  float p_d = (components & kDiffuseReflection) ? diffuse().p(geometry, in, out, cugar::kProjectedSolidAngle) : 0.0f;
1152  float p_dt = (components & kDiffuseTransmission) ? diffuse_trans().p(geometry, in, out, cugar::kProjectedSolidAngle) : 0.0f;
1153  float p_g = (components & kGlossyReflection) ? glossy().p(geometry, in, out, cugar::kProjectedSolidAngle) : 0.0f;
1154  float p_gt = (components & kGlossyTransmission) ? glossy_trans().p(geometry, in, out, cugar::kProjectedSolidAngle) : 0.0f;
1155 
1156  p_proj =
1157  p_d * w_p[kDiffuseReflectionIndex] * coat_transmission_prob +
1158  p_dt * w_p[kDiffuseTransmissionIndex] * coat_transmission_prob +
1159  p_g * w_p[kGlossyReflectionIndex] * coat_transmission_prob +
1160  p_gt * w_p[kGlossyTransmissionIndex] * coat_transmission_prob;
1161 
1162  p = p_proj * fabsf( dot(out, geometry.normal_s) );
1163  g = this->f(geometry, in, out, components) / p_proj;
1164  }
1165  else
1166  {
1167  // evaluate the true weights, now that the output direction is known
1168  cugar::Vector3f w[4];
1169  inner_component_weights(geometry, in, out, w);
1170 
1171  // re-weight the bsdf value
1172  g *=
1173  (out_comp & kGlossyReflection) ? w[kGlossyReflectionIndex] :
1174  (out_comp & kGlossyTransmission) ? w[kGlossyTransmissionIndex] :
1175  (out_comp & kDiffuseReflection) ? w[kDiffuseReflectionIndex] :
1176  w[kDiffuseTransmissionIndex];
1177 
1178  g /= p_comp;
1179  p *= p_comp;
1180  p_proj *= p_comp;
1181  }
1182  }
1183 
1184  const float factor = compression_factor( geometry, in, out );
1185 
1186  out_p = p;
1187  out_p_proj = p_proj;
1188  out_g = g * factor;
1189  return true;
1190  }
1191  else
1192  {
1193  out = cugar::Vector3f(0.0f);
1194  out_p = 0.0f;
1195  out_p_proj = 0.0f;
1196  out_g = cugar::Vector3f(0.0f);
1197  return false;
1198  }
1199  }
1200 
1201  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
1202  bool clearcoat_transmission(
1203  const cugar::DifferentialGeometry& geometry,
1204  const cugar::Vector3f w_i,
1205  cugar::Vector3f& H, // the output clearcoat microfacet
1206  float& cos_theta_i, // the output cosine of the incident angle - dot(w_i,H)
1207  float& eta, // the output clearcoat relative IOR
1208  cugar::Vector3f& Fc_1, // the output Fresnel reflection coefficient - R12
1209  cugar::Vector3f& Tc_1) const // the output Fresnel transmission coefficient - T12
1210  {
1211  const float R0 = cugar::min( cugar::max_comp( m_reflectivity ), 0.95f );
1212  const float eta_c = 1.0f / m_clearcoat_ior;
1213 
1214  H = geometry.normal_s; // for now, assume the clearcoat to be perfectly specular
1215  cos_theta_i = dot(w_i, H);
1216 
1217  // compute the refracted direction - which will be the input to the internal layers
1218  cugar::Vector3f w_t;
1219  float Fc_1_s;
1220  if (!cugar::refract(w_i, H, cos_theta_i, eta_c, &w_t, &Fc_1_s))
1221  {
1222  // total internal reflection - no chance to enter the clearcoat layer
1223  Fc_1 = 1.0f;
1224  Tc_1 = 0.0f;
1225  return false;
1226  }
1227 
1228  // use the true Fresnel coefficient to interpolate between m_reflectivity at normal incidence and pure white at grazing angles
1229  Fc_1 = cugar::lerp( m_reflectivity, cugar::Vector3f(1.0f), cugar::max(Fc_1_s - R0, 0.0f) / (1 - R0) );
1230  Tc_1 = 1.0f - Fc_1;
1231  return true;
1232  }
1233 
1236  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
1238  const cugar::DifferentialGeometry& geometry,
1239  const cugar::Vector3f w_i,
1240  const cugar::Vector3f w_o) const
1241  {
1242  if (m_transport == kRadianceTransport && m_ior)
1243  {
1244  // apply the radiance compression factor of (eta_t / eta_i)^2
1245  const float NoV = dot(w_i, geometry.normal_s);
1246  const float NoL = dot(w_o, geometry.normal_s);
1247  if (NoV * NoL < 0.0f)
1248  return cugar::sqr( NoV > 0.0f ? m_ior : 1.0f / m_ior );
1249  }
1250  return 1.0f;
1251  }
1252 
1253  FERMAT_FORCEINLINE FERMAT_HOST_DEVICE
1254  float glossy_reflectance(const float cos_theta) const
1255  {
1256  const uint32 S = 32;
1257 
1258  const float eta = cos_theta > 0.0f ? 1.0f / m_ior : m_ior;
1259 
1260  const uint32 cos_theta_i = cugar::min( S-1u, uint32( fabsf(cos_theta) * (S-1) ) );
1261  const uint32 base_spec_i = cugar::min( S-1u, uint32( cugar::max_comp( m_fresnel ) * (S-1) ) );
1262  const uint32 eta_i = cugar::min( S-1u, uint32( (eta / 2.0f) * (S-1) ) );
1263  const uint32 roughness_i = cugar::min( S-1u, uint32( m_glossy.roughness * (S-1) ) );
1264 
1265  const uint32 cell_i = (eta_i*S*S*S + base_spec_i*S*S + roughness_i*S + cos_theta_i);
1266 
1267  return m_glossy_reflectance[cell_i];
1268  }
1269 
1270  diffuse_component m_diffuse;
1271  diffuse_trans_component m_diffuse_trans;
1272  glossy_component m_glossy;
1273  glossy_component m_glossy_trans;
1274  cugar::Vector3f m_fresnel;
1275  cugar::Vector3f m_reflectivity; // normal-incidence reflectivity of the clear-coat layer
1276  float m_ior;
1277  float m_opacity;
1278  float m_clearcoat_ior;
1279  const float* m_glossy_reflectance;
1280  TransportType m_transport;
1281 };
1282 
1283 
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert_trans.h:103
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void sample(const Vector3f u, const DifferentialGeometry &geometry, const Vector3f V, Vector3f &L, Vector3f &g, float &p, float &p_proj) const
Definition: lambert_trans.h:118
Defines the GGX BSDF.
SphericalMeasure
Definition: differential_geometry.h:50
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L) const
Definition: ggx_smith.h:346
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f sample(const Vector3f u, const Vector3f V, float *p=NULL) const
Definition: ggx_smith.h:114
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void sample(const DifferentialGeometry &geometry, const Vector3f H, const Vector3f V, Vector3f &L, Vector3f &g, float &p, float &p_proj) const
Definition: ggx_smith.h:529
static FERMAT_HOST_DEVICE uint32 component_count()
Definition: bsdf.h:170
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE GGXSmithMicrofacetDistribution distribution() const
Definition: ggx_smith.h:236
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE VectorType fresnel_schlick(float cos_theta_i, const float eta, const VectorType fresnel_base)
Definition: refraction.h:105
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: ggx_smith.h:484
FERMAT_HOST_DEVICE float get_eta(const float NoV) const
Definition: bsdf.h:298
Definition: ggx.h:132
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE float compression_factor(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o) const
Definition: bsdf.h:1237
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void fresnel_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, const cugar::Vector3f L, cugar::Vector3f &r_coeff, cugar::Vector3f &t_coeff) const
Definition: bsdf.h:669
Definition: lambert_trans.h:51
FERMAT_HOST_DEVICE const glossy_component & glossy_trans() const
Definition: bsdf.h:293
static FERMAT_HOST_DEVICE ComponentType component_mask(const ComponentIndex comp)
Definition: bsdf.h:188
ComponentType
Definition: bsdf.h:139
Defines the Lambert BSDF.
FERMAT_HOST_DEVICE Bsdf(const TransportType transport, const RenderingContextView renderer, const MeshMaterial material, const float mollification_factor=1.0f, const float mollification_bias=0.0f, const float min_roughness=0.0f)
Definition: bsdf.h:219
Definition: MeshView.h:55
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void normalize_sampling_weights(float *w_p, float &coat_reflection_prob, float &coat_transmission_prob, const bool RR, const ComponentType components=kAllComponents) const
Definition: bsdf.h:591
TransportType
Definition: bsdf.h:83
FERMAT_HOST_DEVICE const diffuse_component & diffuse() const
Definition: bsdf.h:283
CUGAR_HOST_DEVICE Vector3f to_local(const Vector3f v) const
Definition: differential_geometry.h:70
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void sample(const Vector3f u, const DifferentialGeometry &geometry, const Vector3f V, Vector3f &L, Vector3f &g, float &p, float &p_proj) const
Definition: lambert.h:131
CUGAR_HOST_DEVICE Vector3f from_local(const Vector3f v) const
Definition: differential_geometry.h:82
FERMAT_HOST_DEVICE const diffuse_trans_component & diffuse_trans() const
Definition: bsdf.h:278
Definition: ggx_smith.h:54
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void component_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, cugar::Vector3f *w) const
Definition: bsdf.h:797
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE bool sample(const cugar::DifferentialGeometry &geometry, const float z[3], const cugar::Vector3f in, ComponentType &out_comp, cugar::Vector3f &out, float &out_p, float &out_p_proj, cugar::Vector3f &out_g, bool RR=true, bool evaluate_full_bsdf=false, const ComponentType components=kAllComponents) const
Definition: bsdf.h:921
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L) const
Definition: lambert_trans.h:60
FERMAT_HOST_DEVICE Bsdf()
Definition: bsdf.h:193
ComponentIndex
Definition: bsdf.h:127
FERMAT_HOST_DEVICE Bsdf(const Bsdf &bsdf)
Definition: bsdf.h:203
Definition: lambert.h:64
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE bool refract(const Vector3f w_i, const Vector3f N, const float cos_theta_i, const float eta, Vector3f *out, float *F)
Definition: refraction.h:157
float CUGAR_HOST_DEVICE sqr(const float x)
Definition: numbers.h:610
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void f_and_p(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, cugar::Vector3f &f, float &p, const cugar::SphericalMeasure measure=cugar::kProjectedSolidAngle, const bool RR=true, const ComponentType components=kAllComponents) const
Definition: bsdf.h:417
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f microfacet(const Vector3f V, const Vector3f L, const Vector3f N, const float inv_eta)
Definition: ggx_common.h:50
Definition: differential_geometry.h:59
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void f(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, cugar::Vector3f *f) const
Definition: bsdf.h:336
Definition: ggx_smith.h:204
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void inner_component_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, const cugar::Vector3f L, cugar::Vector3f &diffuse_refl_coeff, cugar::Vector3f &diffuse_trans_coeff, cugar::Vector3f &glossy_refl_coeff, cugar::Vector3f &glossy_trans_coeff) const
Definition: bsdf.h:701
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void f_and_p(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, cugar::Vector3f *f, float *p, const cugar::SphericalMeasure measure=cugar::kProjectedSolidAngle, bool RR=true) const
Definition: bsdf.h:366
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void fresnel_weights(const float VoH, const float eta, cugar::Vector3f &r_coeff, cugar::Vector3f &t_coeff) const
Definition: bsdf.h:632
Define a vector_view POD type and plain_view() for std::vector.
Definition: diff.h:38
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void sampling_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, float *w) const
Definition: bsdf.h:580
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void f_and_p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, Vector3f &f, float &p, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert_trans.h:86
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE Vector3f f(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L) const
Definition: lambert.h:73
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void sampling_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, float &diffuse_refl_prob, float &diffuse_trans_prob, float &glossy_refl_prob, float &glossy_trans_prob) const
Definition: bsdf.h:530
Defines the LTC BSDF.
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void component_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, cugar::Vector3f &Fc_1, cugar::Vector3f &Tc_1, cugar::Vector3f &Fc_2, cugar::Vector3f &Tc_2, cugar::Vector3f *w) const
Definition: bsdf.h:748
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void f_and_p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, Vector3f &f, float &p, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert.h:99
static FERMAT_HOST_DEVICE uint32 component_index(const ComponentType comp)
Definition: bsdf.h:175
Definition: ltc.h:79
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE uint8 comp(const uchar2 a, const char c)
Definition: numbers.h:218
Definition: bsdf.h:123
FERMAT_HOST_DEVICE Bsdf(const TransportType transport, const RenderingContextView renderer, const cugar::Vector3f diffuse, const cugar::Vector3f specular, const float roughness, const cugar::Vector3f diffuse_trans, const float opacity=1.0f, const float ior=1.0f)
Definition: bsdf.h:248
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE cugar::Vector3f f(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, const ComponentType components=kAllComponents) const
Definition: bsdf.h:312
Definition: renderer_view.h:80
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE void f_and_p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, Vector3f &f, float &p, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: ggx_smith.h:428
FERMAT_HOST_DEVICE FERMAT_FORCEINLINE bool sample_component(const cugar::DifferentialGeometry &geometry, const float z[3], const cugar::Vector3f in, const ComponentType out_comp, cugar::Vector3f &out, float &out_p, float &out_p_proj, cugar::Vector3f &out_g, const bool evaluate_full_bsdf=false) const
Definition: bsdf.h:825
CUGAR_FORCEINLINE CUGAR_HOST_DEVICE float p(const DifferentialGeometry &geometry, const Vector3f V, const Vector3f L, const SphericalMeasure measure=kProjectedSolidAngle) const
Definition: lambert.h:116
FERMAT_HOST_DEVICE const glossy_component & glossy() const
Definition: bsdf.h:288
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE void inner_component_weights(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f V, const cugar::Vector3f L, cugar::Vector3f *w) const
Definition: bsdf.h:722
FERMAT_HOST_DEVICE float get_inv_eta(const float NoV) const
Definition: bsdf.h:303
FERMAT_FORCEINLINE FERMAT_HOST_DEVICE float p(const cugar::DifferentialGeometry &geometry, const cugar::Vector3f w_i, const cugar::Vector3f w_o, const cugar::SphericalMeasure measure=cugar::kProjectedSolidAngle, const bool RR=true, const ComponentType components=kAllComponents) const
Definition: bsdf.h:474