0028218: Visualization, Path Tracing - Redesign path tracing materials to support...
authordbp <dbp@opencascade.org>
Fri, 18 Nov 2016 14:53:10 +0000 (17:53 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 3 Mar 2017 13:11:21 +0000 (16:11 +0300)
Existing OCCT path tracing engine used very simple additive material (BSDF) model, so it was possible to reproduce
behavior only of very basic materials such as metal, glass, or plastic. However, some important in CAD industry
materials like car paint or ceramic could not be modeled well. In this patch, OCCT BSDF was significantly improved
by replacing additive model with two-layered scattering model. Therefore, we have base diffuse, glossy, or transmissive
layer, covered by one glossy/specular coat. The layers themselves have no thickness; they can simply reflect light or
transmits it to the layer under it. Balancing different combinations of layer properties can produce a wide range of
different effects. At the same time, disabling the first (coat) layer allows to keep full compatibility with previously
supported scattering model. All new parameters are available via 'vbsdf' command.

Location of new sample for few material examples:
samples\tcl\pathtrace_materials.tcl

Fix shader compilation issue.

Fix test case sample_ball_alpha.

Shaders_PathtraceBase_fs.pxx - regenerate resource from origin

15 files changed:
.gitattributes
samples/tcl/pathtrace_cube.tcl
samples/tcl/pathtrace_materials.tcl [new file with mode: 0644]
src/Graphic3d/Graphic3d_BSDF.cxx
src/Graphic3d/Graphic3d_BSDF.hxx
src/Graphic3d/Graphic3d_MaterialAspect.cxx
src/OpenGl/OpenGl_SceneGeometry.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/PathtraceBase.fs
src/Shaders/RaytraceBase.fs
src/Shaders/Shaders_PathtraceBase_fs.pxx
src/Shaders/Shaders_RaytraceBase_fs.pxx
src/ViewerTest/ViewerTest.cxx
tests/v3d/raytrace/sample_ball_alpha
tests/v3d/raytrace/sample_materials [new file with mode: 0644]

index db61c81..0faa8ba 100644 (file)
@@ -37,6 +37,9 @@
 *.vrml      eol=lf
 *.md        eol=lf
 *.natvis    eol=lf
+*.fs        eol=lf
+*.vs        eol=lf
+*.glsl      eol=lf
 FILES       eol=lf
 PACKAGES    eol=lf
 EXTERNLIB   eol=lf
index 264ed77..6cd321e 100644 (file)
@@ -51,8 +51,8 @@ psphere s 0.2
 vdisplay     -noupdate s
 vlocation    -noupdate s -setLocation 0.21 0.3 0.2
 vsetmaterial -noupdate s glass
-vbsdf s -absorpcolor 0.8 0.8 1.0
-vbsdf s -absorpcoeff 6
+vbsdf s -absorpColor 0.8 0.8 1.0
+vbsdf s -absorpCoeff 6
 
 # setup first inner box
 box c 0.3 0.3 0.2
@@ -68,15 +68,15 @@ vdisplay     -noupdate g
 vlocation    -noupdate g -setLocation 0.7 0.25 0.2
 vlocation    -noupdate g -rotate 0 0 0 0 0 1 10
 vsetmaterial -noupdate g glass
-vbsdf g -absorpcolor 0.8 1.0 0.8
-vbsdf g -absorpcoeff 6
+vbsdf g -absorpColor 0.8 1.0 0.8
+vbsdf g -absorpCoeff 6
 
 # setup second inner sphere
 psphere r 0.1
 vdisplay -noupdate r
 vsetmaterial -noupdate r plastic
-vbsdf r -kd 0.5 0.9 0.3 -ks 0.0 -kr 0.3 -n
-vbsdf r -fresnel Constant 1.0
+vbsdf r -kd 0.5 0.9 0.3 -ks 0.3 -baseRoughness 0.0 -n
+vbsdf r -baseFresnel Constant 1.0
 vlocation r -setLocation 0.5 0.65 0.1
 
 puts "Trying path tracing mode..."
diff --git a/samples/tcl/pathtrace_materials.tcl b/samples/tcl/pathtrace_materials.tcl
new file mode 100644 (file)
index 0000000..8f7d10c
--- /dev/null
@@ -0,0 +1,226 @@
+# Script demonstrating Global illumination materials
+# using path tracing rendering engine in 3D view
+
+#Category: Visualization
+#Title: Path tracing - Materials
+
+set aBallPath [locate_data_file occ/Ball.brep]
+
+pload MODELING VISUALIZATION
+
+# Setup 3D viewer
+vclear
+vinit name=View1 w=512 h=512
+vglinfo
+vvbo 0
+vsetdispmode 1
+
+# Restore exported shapes
+restore $aBallPath Ball0
+restore $aBallPath Ball1
+restore $aBallPath Ball2
+restore $aBallPath Ball3
+restore $aBallPath Ball4
+restore $aBallPath Ball5
+restore $aBallPath Ball6
+restore $aBallPath Ball7
+restore $aBallPath Ball8
+
+# Create chessboard-style floor
+box tile 10 10 0.1
+eval compound [lrepeat 144 tile] tiles
+explode tiles
+for {set i 0} {$i < 12} {incr i} {
+  for {set j 1} {$j <= 12} {incr j} {
+    ttranslate tiles_[expr 12 * $i + $j] [expr $i * 10 - 90] [expr $j * 10 - 70] -0.15
+    vdisplay -noupdate tiles_[expr 12 * $i + $j]
+
+    vsetmaterial -noupdate tiles_[expr 12 * $i + $j] plaster
+
+    if {($i + $j) % 2 == 0} {
+      vbsdf tiles_[expr 12 * $i + $j] -kd 0.85
+    } else {
+      vbsdf tiles_[expr 12 * $i + $j] -kd 0.45
+    }
+  }
+}
+
+# Setup object 'Ball1'
+vdisplay Ball1
+vsetmaterial Ball1 Brass
+vbsdf Ball1 -Kc 0 0 0
+vbsdf Ball1 -Kd 0.272798 0.746262 0.104794
+vbsdf Ball1 -Ks 0.253738 0.253738 0.253738
+vbsdf Ball1 -Kt 0 0 0
+vbsdf Ball1 -baseRoughness 0.045
+vbsdf Ball1 -coatRoughness 0
+vbsdf Ball1 -Le 0 0 0
+vbsdf Ball1 -absorpColor 0 0 0
+vbsdf Ball1 -absorpCoeff 0
+vbsdf Ball1 -coatFresnel Constant 0
+vbsdf Ball1 -baseFresnel Schlick 0.58 0.42 0.2
+vlocation Ball1 -rotation 0 0 0 1
+vlocation Ball1 -location 10 0 0
+
+# Setup object 'Ball2'
+vdisplay Ball2
+vsetmaterial Ball2 Brass
+vbsdf Ball2 -Kc 0 0 0
+vbsdf Ball2 -Kd 0.8 0.8 0.8
+vbsdf Ball2 -Ks 0 0 0
+vbsdf Ball2 -Kt 0 0 0
+vbsdf Ball2 -baseRoughness 0
+vbsdf Ball2 -coatRoughness 0
+vbsdf Ball2 -Le 2.02 0.171915 0.171915
+vbsdf Ball2 -absorpColor 0 0 0
+vbsdf Ball2 -absorpCoeff 0
+vbsdf Ball2 -coatFresnel Constant 0
+vbsdf Ball2 -baseFresnel Constant 1
+vlocation Ball2 -rotation 0 0 0 1
+vlocation Ball2 -location 10 40 0
+
+# Setup object 'Ball3'
+vdisplay Ball3
+vsetmaterial Ball3 Glass
+vbsdf Ball3 -Kc 1 1 1
+vbsdf Ball3 -Kd 0 0 0
+vbsdf Ball3 -Ks 0 0 0
+vbsdf Ball3 -Kt 1 1 1
+vbsdf Ball3 -baseRoughness 0
+vbsdf Ball3 -coatRoughness 0
+vbsdf Ball3 -Le 0 0 0
+vbsdf Ball3 -absorpColor 0.75 0.95 0.9
+vbsdf Ball3 -absorpCoeff 0.05
+vbsdf Ball3 -coatFresnel Dielectric 1.62
+vbsdf Ball3 -baseFresnel Constant 1
+vlocation Ball3 -rotation 0 0 0 1
+vlocation Ball3 -location -30 -40 0
+
+# Setup object 'Ball4'
+vdisplay Ball4
+vsetmaterial Ball4 Brass
+vbsdf Ball4 -Kc 0 0 0
+vbsdf Ball4 -Kd 0 0 0
+vbsdf Ball4 -Ks 0.985 0.985 0.985
+vbsdf Ball4 -Kt 0 0 0
+vbsdf Ball4 -baseRoughness 0
+vbsdf Ball4 -coatRoughness 0
+vbsdf Ball4 -Le 0 0 0
+vbsdf Ball4 -absorpColor 0 0 0
+vbsdf Ball4 -absorpCoeff 0
+vbsdf Ball4 -coatFresnel Constant 0
+vbsdf Ball4 -baseFresnel Schlick 0.58 0.42 0.2
+vlocation Ball4 -rotation 0 0 0 1
+vlocation Ball4 -location -70 -40 0
+
+# Setup object 'Ball5'
+vdisplay Ball5
+vsetmaterial Ball5 Glass
+vbsdf Ball5 -Kc 1 1 1
+vbsdf Ball5 -Kd 0 0 0
+vbsdf Ball5 -Ks 0 0 0
+vbsdf Ball5 -Kt 1 1 1
+vbsdf Ball5 -baseRoughness 0
+vbsdf Ball5 -coatRoughness 0
+vbsdf Ball5 -Le 0 0 0
+vbsdf Ball5 -absorpColor 0 0.288061 0.825532
+vbsdf Ball5 -absorpCoeff 0.3
+vbsdf Ball5 -coatFresnel Dielectric 1.62
+vbsdf Ball5 -baseFresnel Constant 1
+vlocation Ball5 -rotation 0 0 0 1
+vlocation Ball5 -location -30 0 0
+
+# Setup object 'Ball6'
+vdisplay Ball6
+vsetmaterial Ball6 Brass
+vbsdf Ball6 -Kc 1 1 1
+vbsdf Ball6 -Kd 0 0.716033 0.884507
+vbsdf Ball6 -Ks 0.115493 0.115493 0.115493
+vbsdf Ball6 -Kt 0 0 0
+vbsdf Ball6 -baseRoughness 0.045
+vbsdf Ball6 -coatRoughness 0
+vbsdf Ball6 -Le 0 0 0
+vbsdf Ball6 -absorpColor 0 0 0
+vbsdf Ball6 -absorpCoeff 0
+vbsdf Ball6 -coatFresnel Dielectric 1.5
+vbsdf Ball6 -baseFresnel Schlick 0.58 0.42 0.2
+vlocation Ball6 -rotation 0 0 0 1
+vlocation Ball6 -location -30 40 0
+
+# Setup object 'Ball7'
+vdisplay Ball7
+vsetmaterial Ball7 Brass
+vbsdf Ball7 -Kc 1 1 1
+vbsdf Ball7 -Kd 1e-06 9.9999e-07 9.9999e-07
+vbsdf Ball7 -Ks 0.0479573 0.804998 0
+vbsdf Ball7 -Kt 0 0 0
+vbsdf Ball7 -baseRoughness 0.447
+vbsdf Ball7 -coatRoughness 0
+vbsdf Ball7 -Le 0 0 0
+vbsdf Ball7 -absorpColor 0 0 0
+vbsdf Ball7 -absorpCoeff 0
+vbsdf Ball7 -coatFresnel Dielectric 1.5
+vbsdf Ball7 -baseFresnel Schlick 0.58 0.42 0.2
+vlocation Ball7 -rotation 0 0 0 1
+vlocation Ball7 -location -70 0 0
+
+# Setup object 'Ball8'
+vdisplay Ball8
+vsetmaterial Ball8 Aluminium
+vbsdf Ball8 -Kc 0 0 0
+vbsdf Ball8 -Kd 0 0 0
+vbsdf Ball8 -Ks 0.985 0.985 0.985
+vbsdf Ball8 -Kt 0 0 0
+vbsdf Ball8 -baseRoughness 0.026
+vbsdf Ball8 -coatRoughness 0
+vbsdf Ball8 -Le 0 0 0
+vbsdf Ball8 -absorpColor 0 0 0
+vbsdf Ball8 -absorpCoeff 0
+vbsdf Ball8 -coatFresnel Constant 0
+vbsdf Ball8 -baseFresnel Schlick 0.913183 0.921494 0.924524
+vlocation Ball8 -rotation 0 0 0 1
+vlocation Ball8 -location -70 40 0
+
+# Setup object 'Ball0'
+vdisplay Ball0
+vsetmaterial Ball0 Glass
+vbsdf Ball0 -Kc 0 0 0
+vbsdf Ball0 -Kd 0.723404 0.166229 0.166229
+vbsdf Ball0 -Ks 0 0 0
+vbsdf Ball0 -Kt 0 0 0
+vbsdf Ball0 -baseRoughness 0
+vbsdf Ball0 -coatRoughness 0
+vbsdf Ball0 -Le 0 0 0
+vbsdf Ball0 -absorpColor 0 0 0
+vbsdf Ball0 -absorpCoeff 0
+vbsdf Ball0 -coatFresnel Constant 0
+vbsdf Ball0 -baseFresnel Constant 1
+vlocation Ball0 -rotation 0 0 0 1
+vlocation Ball0 -location 10 -40 0
+
+# Restore view parameters
+vcamera -perspective -fovy 25
+vcamera -distance 238.089
+vviewparams -proj 0.679219 -0.00724546 0.7339
+vviewparams -up -0.733931 -0.00311795 0.679217
+vviewparams -at -22.3025 0.0986351 3.30327
+vviewparams -eye 139.412 -1.62643 178.037
+vviewparams -size 170.508
+
+# Restore light source parameters
+vlight clear
+vlight add directional direction -0.303949 -0.434084 -0.848048 smoothness 0.3 intensity 12
+
+# Load environment map
+vtextureenv on 1
+
+puts "Trying path tracing mode..."
+vrenderparams -ray -gi -rayDepth 10
+
+# Start progressive refinement mode
+#vprogressive
+
+puts "Make several path tracing iterations to refine the picture, please wait..."
+vfps 512
+puts "Done. To improve the image further, or after view manipulations, give command:"
+puts "vfps \[nb_iteratons\]" 
index 5784008..4421e9c 100644 (file)
@@ -27,7 +27,7 @@ Graphic3d_Vec4 Graphic3d_Fresnel::Serialize() const
 
   if (myFresnelType != Graphic3d_FM_SCHLICK)
   {
-    aData.x() = -static_cast<Standard_ShortReal> (myFresnelType);
+    aData.x() = -static_cast<float> (myFresnelType);
   }
 
   return aData;
@@ -37,8 +37,8 @@ Graphic3d_Vec4 Graphic3d_Fresnel::Serialize() const
 // function : fresnelNormal
 // purpose  :
 // =======================================================================
-inline Standard_ShortReal fresnelNormal (Standard_ShortReal theN,
-                                         Standard_ShortReal theK)
+inline float fresnelNormal (float theN,
+                            float theK)
 {
   return ((theN - 1.f) * (theN - 1.f) + theK * theK) /
          ((theN + 1.f) * (theN + 1.f) + theK * theK);
@@ -51,10 +51,37 @@ inline Standard_ShortReal fresnelNormal (Standard_ShortReal theN,
 Graphic3d_Fresnel Graphic3d_Fresnel::CreateConductor (const Graphic3d_Vec3& theRefractionIndex,
                                                       const Graphic3d_Vec3& theAbsorptionIndex)
 {
-  return Graphic3d_Fresnel (Graphic3d_FM_SCHLICK,
-    Graphic3d_Vec3 (fresnelNormal (theRefractionIndex.x(), theAbsorptionIndex.x()),
-                    fresnelNormal (theRefractionIndex.y(), theAbsorptionIndex.y()),
-                    fresnelNormal (theRefractionIndex.z(), theAbsorptionIndex.z())));
+  const Graphic3d_Vec3 aFresnel (fresnelNormal (theRefractionIndex.x(), theAbsorptionIndex.x()),
+                                 fresnelNormal (theRefractionIndex.y(), theAbsorptionIndex.y()),
+                                 fresnelNormal (theRefractionIndex.z(), theAbsorptionIndex.z()));
+
+  return Graphic3d_Fresnel (Graphic3d_FM_SCHLICK, aFresnel);
+}
+
+// =======================================================================
+// function : Graphic3d_BSDF
+// purpose  :
+// =======================================================================
+Graphic3d_BSDF::Graphic3d_BSDF()
+{
+  FresnelCoat = Graphic3d_Fresnel::CreateConstant (0.f);
+  FresnelBase = Graphic3d_Fresnel::CreateConstant (1.f);
+}
+
+// =======================================================================
+// function : operator==
+// purpose  :
+// =======================================================================
+bool Graphic3d_BSDF::operator== (const Graphic3d_BSDF& theOther) const
+{
+  return Kc          == theOther.Kc
+      && Kd          == theOther.Kd
+      && Kt          == theOther.Kt
+      && Ks          == theOther.Ks
+      && Le          == theOther.Le
+      && Absorption  == theOther.Absorption
+      && FresnelCoat == theOther.FresnelCoat
+      && FresnelBase == theOther.FresnelBase;
 }
 
 // =======================================================================
@@ -63,16 +90,21 @@ Graphic3d_Fresnel Graphic3d_Fresnel::CreateConductor (const Graphic3d_Vec3& theR
 // =======================================================================
 void Graphic3d_BSDF::Normalize()
 {
-  Standard_ShortReal aMax = std::max (Kd.x() + Ks.x() + Kr.x() + Kt.x(),
-                            std::max (Kd.y() + Ks.y() + Kr.y() + Kt.y(),
-                                      Kd.z() + Ks.z() + Kr.z() + Kt.z()));
+  float aMax = 0.f;
+
+  for (int aChannelID = 0; aChannelID < 3; ++aChannelID)
+  {
+    aMax = std::max (aMax, Kd[aChannelID] + Ks[aChannelID] + Kt[aChannelID]);
+  }
 
   if (aMax > 1.f)
   {
-    Kd /= aMax;
-    Ks /= aMax;
-    Kr /= aMax;
-    Ks /= aMax;
+    for (int aChannelID = 0; aChannelID < 3; ++aChannelID)
+    {
+      Kd[aChannelID] /= aMax;
+      Ks[aChannelID] /= aMax;
+      Kt[aChannelID] /= aMax;
+    }
   }
 }
 
@@ -93,26 +125,15 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateDiffuse (const Graphic3d_Vec3& theWeight)
 // function : CreateMetallic
 // purpose  :
 // =======================================================================
-Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3&    theWeight,
-                                               const Graphic3d_Fresnel& theFresnel,
-                                               const Standard_ShortReal theRoughness)
+Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3& theWeight, const Graphic3d_Fresnel& theFresnel, const float theRoughness)
 {
   Graphic3d_BSDF aBSDF;
 
-  aBSDF.Roughness = theRoughness;
+  aBSDF.FresnelBase = theFresnel;
 
   // Selecting between specular and glossy
   // BRDF depending on the given roughness
-  if (aBSDF.Roughness > 0.f)
-  {
-    aBSDF.Ks = theWeight;
-  }
-  else
-  {
-    aBSDF.Kr = theWeight;
-  }
-
-  aBSDF.Fresnel = theFresnel;
+  aBSDF.Ks = Graphic3d_Vec4 (theWeight, theRoughness);
 
   return aBSDF;
 }
@@ -121,18 +142,25 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateMetallic (const Graphic3d_Vec3&    theWeigh
 // function : CreateTransparent
 // purpose  :
 // =======================================================================
-Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3&    theWeight,
-                                                  const Graphic3d_Vec3&    theAbsorptionColor,
-                                                  const Standard_ShortReal theAbsorptionCoeff)
+Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3& theWeight,
+                                                  const Graphic3d_Vec3& theAbsorptionColor,
+                                                  const float           theAbsorptionCoeff)
 {
   Graphic3d_BSDF aBSDF;
 
+  // Create Fresnel parameters for the coat layer;
+  // set it to 0 value to simulate ideal refractor
+  aBSDF.FresnelCoat = Graphic3d_Fresnel::CreateConstant (0.f);
+
   aBSDF.Kt = theWeight;
 
-  aBSDF.AbsorptionColor = theAbsorptionColor;
-  aBSDF.AbsorptionCoeff = theAbsorptionCoeff;
+  // Link reflection and transmission coefficients
+  aBSDF.Kc.r() = aBSDF.Kt.r();
+  aBSDF.Kc.g() = aBSDF.Kt.g();
+  aBSDF.Kc.b() = aBSDF.Kt.b();
 
-  aBSDF.Fresnel = Graphic3d_Fresnel::CreateConstant (0.f);
+  aBSDF.Absorption = Graphic3d_Vec4 (theAbsorptionColor,
+                                     theAbsorptionCoeff);
 
   return aBSDF;
 }
@@ -141,19 +169,24 @@ Graphic3d_BSDF Graphic3d_BSDF::CreateTransparent (const Graphic3d_Vec3&    theWe
 // function : CreateGlass
 // purpose  :
 // =======================================================================
-Graphic3d_BSDF Graphic3d_BSDF::CreateGlass (const Graphic3d_Vec3&    theWeight,
-                                            const Graphic3d_Vec3&    theAbsorptionColor,
-                                            const Standard_ShortReal theAbsorptionCoeff,
-                                            const Standard_ShortReal theRefractionIndex)
+Graphic3d_BSDF Graphic3d_BSDF::CreateGlass (const Graphic3d_Vec3& theWeight,
+                                            const Graphic3d_Vec3& theAbsorptionColor,
+                                            const float           theAbsorptionCoeff,
+                                            const float           theRefractionIndex)
 {
   Graphic3d_BSDF aBSDF;
 
+  // Create Fresnel parameters for the coat layer
+  aBSDF.FresnelCoat = Graphic3d_Fresnel::CreateDielectric (theRefractionIndex);
+
   aBSDF.Kt = theWeight;
 
-  aBSDF.AbsorptionColor = theAbsorptionColor;
-  aBSDF.AbsorptionCoeff = theAbsorptionCoeff;
+  aBSDF.Kc.r() = aBSDF.Kt.r();
+  aBSDF.Kc.g() = aBSDF.Kt.g();
+  aBSDF.Kc.b() = aBSDF.Kt.b();
 
-  aBSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (theRefractionIndex);
+  aBSDF.Absorption = Graphic3d_Vec4 (theAbsorptionColor,
+                                     theAbsorptionCoeff);
 
   return aBSDF;
 }
\ No newline at end of file
index 74e812f..7919089 100644 (file)
@@ -34,8 +34,7 @@ class Graphic3d_Fresnel
 public:
 
   //! Creates uninitialized Fresnel factor.
-  Graphic3d_Fresnel()
-  : myFresnelType (Graphic3d_FM_CONSTANT)
+  Graphic3d_Fresnel() : myFresnelType (Graphic3d_FM_CONSTANT)
   {
     // ideal specular reflector
     myFresnelData = Graphic3d_Vec3 (0.f, 1.f, 0.f);
@@ -111,36 +110,39 @@ private:
 //! for physically-based rendering (in path tracing engine). BSDF is represented as
 //! weighted mixture of basic BRDFs/BTDFs (Bidirectional Reflectance (Transmittance)
 //! Distribution Functions).
+//!
+//! NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive
+//! layer, covered by one glossy/specular coat. In the current model, the layers themselves
+//! have no thickness; they can simply reflect light or transmits it to the layer under it.
+//! We use actual BRDF model only for direct reflection by the coat layer. For transmission
+//! through this layer, we approximate it as a flat specular surface.
 class Graphic3d_BSDF
 {
 public:
 
-  //! Weight of the Lambertian BRDF.
+  //! Weight of coat specular/glossy BRDF.
+  Graphic3d_Vec4 Kc;
+
+  //! Weight of base diffuse BRDF.
   Graphic3d_Vec3 Kd;
 
-  //! Weight of the reflection BRDF.
-  Graphic3d_Vec3 Kr;
+  //! Weight of base specular/glossy BRDF.
+  Graphic3d_Vec4 Ks;
 
-  //! Weight of the transmission BTDF.
+  //! Weight of base specular/glossy BTDF.
   Graphic3d_Vec3 Kt;
 
-  //! Weight of the Blinn's glossy BRDF.
-  Graphic3d_Vec3 Ks;
-
-  //! Self-emitted radiance.
+  //! Radiance emitted by the surface.
   Graphic3d_Vec3 Le;
 
-  //! Parameters of Fresnel reflectance.
-  Graphic3d_Fresnel Fresnel;
-
-  //! Roughness (exponent) of Blinn's BRDF.
-  Standard_ShortReal Roughness;
+  //! Volume scattering color/density.
+  Graphic3d_Vec4 Absorption;
 
-  //! Absorption color of transparent media.
-  Graphic3d_Vec3 AbsorptionColor;
+  //! Parameters of Fresnel reflectance of coat layer.
+  Graphic3d_Fresnel FresnelCoat;
 
-  //! Absorption intensity of transparent media.
-  Standard_ShortReal AbsorptionCoeff;
+  //! Parameters of Fresnel reflectance of base layer.
+  Graphic3d_Fresnel FresnelBase;
 
 public:
 
@@ -170,39 +172,13 @@ public:
 public:
 
   //! Creates uninitialized BSDF.
-  Graphic3d_BSDF() : Roughness (1.f), AbsorptionCoeff (0.f)
-  {
-    Fresnel = Graphic3d_Fresnel::CreateConstant (1.f);
-  }
+  Standard_EXPORT Graphic3d_BSDF();
 
   //! Normalizes BSDF components.
   Standard_EXPORT void Normalize();
 
-  //! Performs mixing of two BSDFs.
-  Graphic3d_BSDF& operator+ (const Graphic3d_BSDF& theOther)
-  {
-    Kd += theOther.Kd;
-    Kr += theOther.Kr;
-    Kt += theOther.Kt;
-    Ks += theOther.Ks;
-    Le += theOther.Le;
-
-    return *this;
-  }
-
   //! Performs comparison of two BSDFs.
-  bool operator== (const Graphic3d_BSDF& theOther) const
-  {
-    return Kd              == theOther.Kd
-        && Kr              == theOther.Kr
-        && Kt              == theOther.Kt
-        && Ks              == theOther.Ks
-        && Le              == theOther.Le
-        && Fresnel         == theOther.Fresnel
-        && Roughness       == theOther.Roughness
-        && AbsorptionCoeff == theOther.AbsorptionCoeff
-        && AbsorptionColor == theOther.AbsorptionColor;
-  }
+  Standard_EXPORT bool operator== (const Graphic3d_BSDF& theOther) const;
 
 };
 
index 2c4edbe..3b3f6db 100644 (file)
@@ -104,10 +104,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       ColorCoef[Graphic3d_TOR_DIFFUSE]  = 0.24f;
       ColorCoef[Graphic3d_TOR_SPECULAR] = 0.06f;
 
-      BSDF.Kd = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE];
-      BSDF.Ks = Graphic3d_Vec3 (0.00784314f, 0.00784314f, 0.00784314f);
+      BSDF.Kd = static_cast<Graphic3d_Vec3> (Colors[Graphic3d_TOR_DIFFUSE]);
+      BSDF.Ks = Graphic3d_Vec4 (0.00784314f, 0.00784314f, 0.00784314f, 0.25f);
       BSDF.Normalize();
-      BSDF.Roughness = 0.25f;
       break;
     case Graphic3d_NOM_SHINY_PLASTIC:
       Shininess                         = 1.00f;
@@ -115,10 +114,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       ColorCoef[Graphic3d_TOR_DIFFUSE]  = 0.50f;
       ColorCoef[Graphic3d_TOR_SPECULAR] = 1.00f;
 
-      BSDF.Kd = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE];
-      BSDF.Ks = Graphic3d_Vec3 (0.145f, 0.145f, 0.145f);
+      BSDF.Kd = static_cast<Graphic3d_Vec3> (Colors[Graphic3d_TOR_DIFFUSE]);
+      BSDF.Ks = Graphic3d_Vec4 (0.145f, 0.145f, 0.145f, 0.17f);
       BSDF.Normalize();
-      BSDF.Roughness = 0.17f;
       break;
     case Graphic3d_NOM_SATIN:
       Shininess                         = 0.09375f;
@@ -127,8 +125,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       ColorCoef[Graphic3d_TOR_SPECULAR] = 0.44f;
 
       BSDF.Kd = Graphic3d_Vec3 (0.2f);
-      BSDF.Ks = Graphic3d_Vec3 (0.6f);
-      BSDF.Roughness = 0.6f;
+      BSDF.Ks = Graphic3d_Vec4 (0.6f);
 
       break;
     case Graphic3d_NOM_NEON_GNC:
@@ -141,9 +138,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       ReflActivity[Graphic3d_TOR_EMISSION] = Standard_True;
 
       BSDF.Kd = Graphic3d_Vec3 (0.0f);
-      BSDF.Kr = Graphic3d_Vec3 (0.5f);
-      BSDF.Le = (Graphic3d_Vec3 )Colors[Graphic3d_TOR_DIFFUSE];
-      BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f);
+      BSDF.Ks = Graphic3d_Vec4 (0.5f, 0.5f, 0.5f, 0.f);
+      BSDF.Le = static_cast<Graphic3d_Vec3> (Colors[Graphic3d_TOR_DIFFUSE]);
+      BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f);
       break;
     case Graphic3d_NOM_METALIZED:
       Shininess                         = 0.13f;
@@ -289,7 +286,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.98f, 1.0f, 0.60f));
 
       BSDF.Kd = Graphic3d_Vec3 (0.243137f, 0.243137f, 0.243137f);
-      BSDF.Ks = Graphic3d_Vec3 (0.00392157f, 0.00392157f, 0.00392157f);
+      BSDF.Ks = Graphic3d_Vec4 (0.00392157f, 0.00392157f, 0.00392157f, 0.5f);
 
       break;
     // Ascending Compatibility of physical materials. Takes the same definition as in the next constructor. New materials
@@ -341,9 +338,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_EMISSION] = Quantity_Color (Graphic3d_Vec3 (0.0f, 1.0f, 0.46f));
 
       BSDF.Kd = Graphic3d_Vec3 (0.0f);
-      BSDF.Kr = Graphic3d_Vec3 (0.5f);
+      BSDF.Ks = Graphic3d_Vec4 (0.5f, 0.5f, 0.5f, 0.f);
       BSDF.Le = Graphic3d_Vec3 (0.0f, 1.0f, 0.46f);
-      BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f);
+      BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f);
       break;
     case Graphic3d_NOM_OBSIDIAN:
       MaterialType = Graphic3d_MATERIAL_PHYSIC;
@@ -358,8 +355,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.333f, 0.329f, 0.346f));
 
       BSDF.Kd = Graphic3d_Vec3 (0.023f, 0.f, 0.023f);
-      BSDF.Ks = Graphic3d_Vec3 (0.0156863f, 0.0156863f, 0.0156863f);
-      BSDF.Roughness = 0.1f;
+      BSDF.Ks = Graphic3d_Vec4 (0.0156863f, 0.0156863f, 0.0156863f, 0.1f);
       break;
     case Graphic3d_NOM_JADE:
       MaterialType = Graphic3d_MATERIAL_PHYSIC;
@@ -373,10 +369,9 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_DIFFUSE]  = Quantity_Color (Graphic3d_Vec3 (0.540f, 0.890f, 0.630f));
       Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.316f, 0.316f, 0.316f));
 
-      BSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (1.5f);
+      BSDF.FresnelBase = Graphic3d_Fresnel::CreateDielectric (1.5f);
       BSDF.Kd = Graphic3d_Vec3 (0.208658f, 0.415686f, 0.218401f);
-      BSDF.Ks = Graphic3d_Vec3 (0.611765f, 0.611765f, 0.611765f);
-      BSDF.Roughness = 0.06f;
+      BSDF.Ks = Graphic3d_Vec4 (0.611765f, 0.611765f, 0.611765f, 0.06f);
       break;
     case Graphic3d_NOM_CHARCOAL:
       MaterialType = Graphic3d_MATERIAL_PHYSIC;
@@ -391,8 +386,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
       Colors[Graphic3d_TOR_SPECULAR] = Quantity_Color (Graphic3d_Vec3 (0.000f, 0.000f, 0.000f));
 
       BSDF.Kd = Graphic3d_Vec3 (0.02f, 0.02f, 0.02f);
-      BSDF.Ks = Graphic3d_Vec3 (0.1f, 0.1f, 0.1f);
-      BSDF.Roughness = 0.3f;
+      BSDF.Ks = Graphic3d_Vec4 (0.1f, 0.1f, 0.1f, 0.3f);
       break;
     case Graphic3d_NOM_WATER:
       MaterialType = Graphic3d_MATERIAL_PHYSIC;
@@ -460,7 +454,7 @@ RawMaterial::RawMaterial (Graphic3d_NameOfMaterial theName, const char* theStrin
 
       BSDF.Kd = Graphic3d_Vec3 (0.1f);
       BSDF.Kt = Graphic3d_Vec3 (0.9f);
-      BSDF.Fresnel = Graphic3d_Fresnel::CreateConstant (0.0f);
+      BSDF.FresnelBase = Graphic3d_Fresnel::CreateConstant (0.0f);
       TransparencyCoef = 0.80f;
 
       Colors[Graphic3d_TOR_AMBIENT]  = Quantity_Color (Graphic3d_Vec3 (0.550f, 0.550f, 0.550f));
index 17ff432..427ed30 100755 (executable)
@@ -74,25 +74,28 @@ public:
   //! Physically-based material properties (used in path tracing engine).
   struct Physical
   {
-    //! Weight of the diffuse BRDF.
+    //! Weight of coat specular/glossy BRDF.
+    BVH_Vec4f Kc;
+
+    //! Weight of base diffuse BRDF.
     BVH_Vec4f Kd;
 
-    //! Weight of the reflection BRDF.
-    BVH_Vec4f Kr;
+    //! Weight of base specular/glossy BRDF.
+    BVH_Vec4f Ks;
 
-    //! Weight of the transmission BTDF.
+    //! Weight of base specular/glossy BTDF.
     BVH_Vec4f Kt;
 
-    //! Weight of the Blinn's glossy BRDF.
-    BVH_Vec4f Ks;
-
-    //! Self-emitted radiance.
+    //! Radiance emitted by the surface.
     BVH_Vec4f Le;
 
-    //! Fresnel coefficients.
-    BVH_Vec4f Fresnel;
+    //! Fresnel coefficients of coat layer.
+    BVH_Vec4f FresnelCoat;
+
+    //! Fresnel coefficients of base layer.
+    BVH_Vec4f FresnelBase;
 
-    //! Absorption color for the transmission BSDF.
+    //! Absorption color/intensity.
     BVH_Vec4f Absorption;
 
   } BSDF;
index c078c54..793d4ac 100644 (file)
@@ -407,16 +407,16 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_AspectFace*
   // Serialize physically-based material properties
   const Graphic3d_BSDF& aBSDF = aSrcMat.BSDF();
 
-  theMaterial.BSDF.Le = BVH_Vec4f (aBSDF.Le,               0.f);
-  theMaterial.BSDF.Kd = BVH_Vec4f (aBSDF.Kd, -1.f /* no tex */);
-  theMaterial.BSDF.Kr = BVH_Vec4f (aBSDF.Kr,               0.f);
-  theMaterial.BSDF.Kt = BVH_Vec4f (aBSDF.Kt,               0.f);
-  theMaterial.BSDF.Ks = BVH_Vec4f (aBSDF.Ks,   aBSDF.Roughness);
+  theMaterial.BSDF.Kc = aBSDF.Kc;
+  theMaterial.BSDF.Ks = aBSDF.Ks;
+  theMaterial.BSDF.Kd = BVH_Vec4f (aBSDF.Kd, -1.f); // no texture
+  theMaterial.BSDF.Kt = BVH_Vec4f (aBSDF.Kt,  0.f);
+  theMaterial.BSDF.Le = BVH_Vec4f (aBSDF.Le,  0.f);
 
-  theMaterial.BSDF.Fresnel = aBSDF.Fresnel.Serialize();
+  theMaterial.BSDF.Absorption = aBSDF.Absorption;
 
-  theMaterial.BSDF.Absorption = BVH_Vec4f (aBSDF.AbsorptionColor,
-                                           aBSDF.AbsorptionCoeff);
+  theMaterial.BSDF.FresnelCoat = aBSDF.FresnelCoat.Serialize ();
+  theMaterial.BSDF.FresnelBase = aBSDF.FresnelBase.Serialize ();
 
   // Handle material textures
   if (theAspect->Aspect()->ToMapTexture())
@@ -2291,7 +2291,7 @@ Standard_Boolean OpenGl_View::uploadRaytraceData (const Handle(OpenGl_Context)&
   if (myRaytraceGeometry.Materials.size() != 0)
   {
     aResult &= myRaytraceMaterialTexture->Init (theGlContext, 4,
-      GLsizei (myRaytraceGeometry.Materials.size() * 18), myRaytraceGeometry.Materials.front().Packed());
+      GLsizei (myRaytraceGeometry.Materials.size() * 19), myRaytraceGeometry.Materials.front().Packed());
 
     if (!aResult)
     {
index 7088826..8dfe1af 100644 (file)
@@ -29,25 +29,25 @@ struct SLocalSpace
 };
 
 //! Describes material properties (BSDF).
-struct SMaterial
+struct SBSDF
 {
-  //! Weight of the Lambertian BRDF.
+  //! Weight of coat specular/glossy BRDF.
+  vec4 Kc;
+
+  //! Weight of base diffuse BRDF.
   vec4 Kd;
 
-  //! Weight of the reflection BRDF.
-  vec3 Kr;
+  //! Weight of base specular/glossy BRDF.
+  vec4 Ks;
 
-  //! Weight of the transmission BTDF.
+  //! Weight of base specular/glossy BTDF.
   vec3 Kt;
 
-  //! Weight of the Blinn BRDF (and roughness).
-  vec4 Ks;
-
-  //! Fresnel coefficients.
-  vec3 Fresnel;
+  //! Fresnel coefficients of coat layer.
+  vec3 FresnelCoat;
 
-  //! Absorption color and intensity of the media.
-  vec4 Absorption;
+  //! Fresnel coefficients of base layer.
+  vec3 FresnelBase;
 };
 
 ///////////////////////////////////////////////////////////////////////////////////////
@@ -148,19 +148,19 @@ float fresnelDielectric (in float theCosI,
 //=======================================================================
 float fresnelDielectric (in float theCosI, in float theIndex)
 {
+  float aFresnel = 1.f;
+
   float anEtaI = theCosI > 0.f ? 1.f : theIndex;
   float anEtaT = theCosI > 0.f ? theIndex : 1.f;
 
-  float aSinT = (anEtaI / anEtaT) * sqrt (1.f - theCosI * theCosI);
+  float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI);
 
-  if (aSinT >= 1.f)
+  if (aSinT2 < 1.f)
   {
-    return 1.f;
+    aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT);
   }
 
-  float aCosT = sqrt (1.f - aSinT * aSinT);
-
-  return fresnelDielectric (abs (theCosI), aCosT, anEtaI, anEtaT);
+  return aFresnel;
 }
 
 //=======================================================================
@@ -197,22 +197,26 @@ float fresnelConductor (in float theCosI, in float theEta, in float theK)
 //=======================================================================
 vec3 fresnelMedia (in float theCosI, in vec3 theFresnel)
 {
+  vec3 aFresnel;
+
   if (theFresnel.x > FRESNEL_SCHLICK)
   {
-    return fresnelSchlick (abs (theCosI), theFresnel);
+    aFresnel = fresnelSchlick (abs (theCosI), theFresnel);
   }
-
-  if (theFresnel.x > FRESNEL_CONSTANT)
+  else if (theFresnel.x > FRESNEL_CONSTANT)
   {
-    return vec3 (theFresnel.z);
+    aFresnel = vec3 (theFresnel.z);
   }
-
-  if (theFresnel.x > FRESNEL_CONDUCTOR)
+  else if (theFresnel.x > FRESNEL_CONDUCTOR)
+  {
+    aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));
+  }
+  else
   {
-    return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));
+    aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y));
   }
 
-  return vec3 (fresnelDielectric (theCosI, theFresnel.y));
+  return aFresnel;
 }
 
 //=======================================================================
@@ -249,36 +253,34 @@ float EvalLambertianReflection (in vec3 theWi, in vec3 theWo)
   return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI);
 }
 
+#define FLT_EPSILON 1.0e-5f
+
 //=======================================================================
 // function : SmithG1
 // purpose  :
 //=======================================================================
 float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness)
 {
-  if (dot (theDirection, theM) * theDirection.z <= 0.f)
-  {
-    return 0.f;
-  }
+  float aResult = 0.f;
 
-  float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;
-
-  if (aTanThetaM == 0.0f)
+  if (dot (theDirection, theM) * theDirection.z > 0.f)
   {
-    return 1.f;
-  }
+    float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;
 
-  float aVal = 1.f / (theRoughness * aTanThetaM);
+    if (aTanThetaM == 0.f)
+    {
+      aResult = 1.f;
+    }
+    else
+    {
+      float aVal = 1.f / (theRoughness * aTanThetaM);
 
-  if (aVal >= 1.6f)
-  {
-    return 1.f;
+      // Use rational approximation to shadowing-masking function (from Mitsuba)
+      aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal);
+    }
   }
 
-  // Use fast and accurate rational approximation to the
-  // shadowing-masking function (from Mitsuba renderer)
-  float aSqr = aVal * aVal;
-
-  return (3.535f * aVal + 2.181f * aSqr) / (1.f + 2.276f * aVal + 2.577f * aSqr);
+  return min (aResult, 1.f);
 }
 
 //=======================================================================
@@ -306,18 +308,31 @@ vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in f
 }
 
 //=======================================================================
-// function : EvalMaterial
+// function : EvalBsdfLayered
 // purpose  : Evaluates BSDF for specified material, with cos(N, PSI)
 //=======================================================================
-vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo)
+vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo)
 {
 #ifdef TWO_SIDED_BXDF
   theWi.z *= sign (theWi.z);
   theWo.z *= sign (theWo.z);
 #endif
 
-  return theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo) +
-    theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.Fresnel, theBSDF.Ks.w);
+  vec3 aBxDF = theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo);
+
+  if (theBSDF.Ks.w > FLT_EPSILON)
+  {
+    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w);
+  }
+
+  aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat);
+
+  if (theBSDF.Kc.w > FLT_EPSILON)
+  {
+    aBxDF += theBSDF.Kc.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelCoat, theBSDF.Kc.w);
+  }
+
+  return aBxDF;
 }
 
 //=======================================================================
@@ -349,7 +364,7 @@ vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float theP
 }
 
 //=======================================================================
-// function : SampleBlinnReflection
+// function : SampleGlossyBlinnReflection
 // purpose  : Samples Blinn BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)
 //            The BRDF is a product of three main terms, D, G, and F,
 //            which is then divided by two cosine terms. Here we perform
@@ -358,7 +373,7 @@ vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float theP
 //            terms would be complex, and it is the D term that accounts
 //            for most of the variation.
 //=======================================================================
-vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)
+vec3 SampleGlossyBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)
 {
   float aKsi1 = RandFloat();
   float aKsi2 = RandFloat();
@@ -396,7 +411,7 @@ vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, i
   }
 
   // Jacobian of half-direction mapping
-  thePDF /= 4.f * dot (theWi, aM);
+  thePDF /= 4.f * aCosDelta;
 
   // compute shadow-masking coefficient
   float aG = SmithG1 (theWo, aM, theRoughness) *
@@ -411,139 +426,152 @@ vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, i
 }
 
 //=======================================================================
-// function : SampleSpecularReflection
-// purpose  : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)
+// function : BsdfPdfLayered
+// purpose  : Calculates BSDF of sampling input knowing output
 //=======================================================================
-vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)
+float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)
 {
-  // Sample input direction
-  theWi = vec3 (-theWo.x,
-                -theWo.y,
-                 theWo.z);
-
-#ifdef TWO_SIDED_BXDF
-  return fresnelMedia (theWo.z, theFresnel);
-#else
-  return fresnelMedia (theWo.z, theFresnel) * step (0.f, theWo.z);
-#endif
-}
+  float aPDF = 0.f; // PDF of sampling input direction
 
-//=======================================================================
-// function : SampleSpecularTransmission
-// purpose  : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI)
-//=======================================================================
-vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)
-{
-  vec3 aFactor = fresnelMedia (theWo.z, theFresnel);
+  // We choose whether the light is reflected or transmitted
+  // by the coating layer according to the Fresnel equations
+  vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);
 
-  float aReflection = convolve (aFactor, theWeight);
+  // Coat BRDF is scaled by its Fresnel reflectance term. For
+  // reasons of simplicity we scale base BxDFs only by coat's
+  // Fresnel transmittance term
+  vec3 aCoatT = UNIT - aCoatF;
 
-  // sample specular BRDF/BTDF
-  if (RandFloat() <= aReflection)
-  {
-    theWi = vec3 (-theWo.x,
-                  -theWo.y,
-                   theWo.z);
+  float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);
+  float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);
+  float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);
+  float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);
 
-    theWeight = aFactor * (1.f / aReflection);
-  }
-  else
+  if (theWi.z * theWo.z > 0.f)
   {
-    theInside = !theInside;
-
-    transmitted (theFresnel.y, theWo, theWi);
-
-    theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection));
-  }
-
-  return theWeight;
-}
-
-#define FLT_EPSILON 1.0e-5F
-
-//=======================================================================
-// function : BsdfPdf
-// purpose  : Calculates BSDF of sampling input knowing output
-//=======================================================================
-float BsdfPdf (in SMaterial theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)
-{
-  float aPd = convolve (theBSDF.Kd.rgb, theWeight);
-  float aPs = convolve (theBSDF.Ks.rgb, theWeight);
-  float aPr = convolve (theBSDF.Kr.rgb, theWeight);
-  float aPt = convolve (theBSDF.Kt.rgb, theWeight);
+    vec3 aH = normalize (theWi + theWo);
 
-  float aReflection = aPd + aPs + aPr + aPt;
+    aPDF = aPd * abs (theWi.z / M_PI);
 
-  float aPDF = 0.f; // PDF of sampling input direction
+    if (theBSDF.Kc.w > FLT_EPSILON)
+    {
+      float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent
 
-  if (theWi.z * theWo.z > 0.f)
-  {
-    vec3 aH = normalize (theWi + theWo);
+      aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);
+    }
 
-    // roughness value --> Blinn exponent
-    float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);
+    if (theBSDF.Ks.w > FLT_EPSILON)
+    {
+      float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent
 
-    aPDF = aPd * abs (theWi.z / M_PI) +
-      aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / (4.f * dot (theWi, aH));
+      aPDF += aPs * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);
+    }
   }
 
-  return aPDF / aReflection;
+  return aPDF / (aPc + aPd + aPs + aPt);
 }
 
 //! Tool macro to handle sampling of particular BxDF
-#define PICK_BXDF(p, k) aPDF = p / aReflection; theWeight *= k / aPDF;
+#define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF;
 
 //=======================================================================
-// function : SampleBsdf
+// function : SampleBsdfLayered
 // purpose  : Samples specified composite material (BSDF)
 //=======================================================================
-float SampleBsdf (in SMaterial theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)
+float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)
 {
-  // compute probability of each reflection type (BxDF)
-  float aPd = convolve (theBSDF.Kd.rgb, theWeight);
-  float aPs = convolve (theBSDF.Ks.rgb, theWeight);
-  float aPr = convolve (theBSDF.Kr.rgb, theWeight);
-  float aPt = convolve (theBSDF.Kt.rgb, theWeight);
-
-  float aReflection = aPd + aPs + aPr + aPt;
-
-  // choose BxDF component to sample
-  float aKsi = aReflection * RandFloat();
+  // NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive
+  // layer, covered by one glossy/specular coat. In the current model, the layers themselves
+  // have no thickness; they can simply reflect light or transmits it to the layer under it.
+  // We use actual BRDF model only for direct reflection by the coat layer. For transmission
+  // through this layer, we approximate it as a flat specular surface.
+
+  float aPDF = 0.f; // PDF of sampled direction
+
+  // We choose whether the light is reflected or transmitted
+  // by the coating layer according to the Fresnel equations
+  vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);
+
+  // Coat BRDF is scaled by its Fresnel term. According to
+  // Wilkie-Weidlich layered BSDF model, transmission term
+  // for light passing through the coat at direction I and
+  // leaving it in O is T = ( 1 - F (O) ) x ( 1 - F (I) ).
+  // For reasons of simplicity, we discard the second term
+  // and scale base BxDFs only by the first term.
+  vec3 aCoatT = UNIT - aCoatF;
+
+  float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);
+  float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);
+  float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);
+  float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);
+
+  // Calculate total reflection probability
+  float aTotalR = (aPc + aPd) + (aPs + aPt);
+
+  // Generate random variable to select BxDF
+  float aKsi = aTotalR * RandFloat();
+
+  if (aKsi < aPc) // REFLECTION FROM COAT
+  {
+    PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb)
 
-  // BxDF's PDF of sampled direction
-  float aPDF = 0.f;
+    if (theBSDF.Kc.w < FLT_EPSILON)
+    {
+      theWeight *= aCoatF;
 
-  if (aKsi < aPd) // diffuse reflection
-  {
-    PICK_BXDF (aPd, theBSDF.Kd.rgb);
+      theWi = vec3 (-theWo.x,
+                    -theWo.y,
+                     theWo.z);
+    }
+    else
+    {
+      theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF);
+    }
 
-    theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);
+    aPDF = mix (aPDF, MAXFLOAT, theBSDF.Kc.w < FLT_EPSILON);
   }
-  else if (aKsi < aPd + aPs) // glossy reflection
+  else if (aKsi < aTotalR) // REFLECTION FROM BASE
   {
-    PICK_BXDF (aPs, theBSDF.Ks.rgb);
+    theWeight *= aCoatT;
 
-    theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);
-  }
-  else if (aKsi < aPd + aPs + aPr) // specular reflection
-  {
-    PICK_BXDF (aPr, theBSDF.Kr.rgb);
+    if (aKsi < aPc + aPd) // diffuse BRDF
+    {
+      PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb)
+
+      theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);
+    }
+    else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF
+    {
+      PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb)
 
-    aPDF = MAXFLOAT;
+      if (theBSDF.Ks.w < FLT_EPSILON)
+      {
+        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);
 
-    theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);
-  }
-  else if (aKsi < aReflection) // specular transmission
-  {
-    PICK_BXDF (aPt, theBSDF.Kt.rgb);
+        theWi = vec3 (-theWo.x,
+                      -theWo.y,
+                       theWo.z);
+      }
+      else
+      {
+        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);
+      }
 
-    aPDF = MAXFLOAT;
+      aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);
+    }
+    else // specular transmission
+    {
+      PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb)
 
-    theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);
+      // refracted direction should exist if we are here
+      transmitted (theBSDF.FresnelCoat.y, theWo, theWi);
+
+      theInside = !theInside; aPDF = MAXFLOAT;
+    }
   }
 
   // path termination for extra small weights
-  theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aReflection));
+  theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR));
 
   return aPDF;
 }
@@ -689,15 +717,16 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o
 #define MIN_THROUGHPUT   vec3 (1.0e-3f)
 #define MIN_CONTRIBUTION vec3 (1.0e-2f)
 
-#define MATERIAL_KD(index)      (18 * index + 11)
-#define MATERIAL_KR(index)      (18 * index + 12)
-#define MATERIAL_KT(index)      (18 * index + 13)
-#define MATERIAL_KS(index)      (18 * index + 14)
-#define MATERIAL_LE(index)      (18 * index + 15)
-#define MATERIAL_FRESNEL(index) (18 * index + 16)
-#define MATERIAL_ABSORPT(index) (18 * index + 17)
+#define MATERIAL_KC(index)           (19 * index + 11)
+#define MATERIAL_KD(index)           (19 * index + 12)
+#define MATERIAL_KS(index)           (19 * index + 13)
+#define MATERIAL_KT(index)           (19 * index + 14)
+#define MATERIAL_LE(index)           (19 * index + 15)
+#define MATERIAL_FRESNEL_COAT(index) (19 * index + 16)
+#define MATERIAL_FRESNEL_BASE(index) (19 * index + 17)
+#define MATERIAL_ABSORPT_BASE(index) (19 * index + 18)
 
-//! Enables experimental russian roulette sampling path termination.
+//! Enables experimental Russian roulette sampling path termination.
 //! In most cases, it provides faster image convergence with minimal
 //! bias, so it is enabled by default.
 #define RUSSIAN_ROULETTE
@@ -713,6 +742,18 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o
 #endif
 
 //=======================================================================
+// function : IsNotZero
+// purpose  : Checks whether BSDF reflects direct light
+//=======================================================================
+bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput)
+{
+  vec3 aGlossy = theBSDF.Kc.rgb * step (FLT_EPSILON, theBSDF.Kc.w) +
+                 theBSDF.Ks.rgb * step (FLT_EPSILON, theBSDF.Ks.w);
+
+  return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON;
+}
+
+//=======================================================================
 // function : PathTrace
 // purpose  : Calculates radiance along the given ray
 //=======================================================================
@@ -766,17 +807,25 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
       aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f;
     }
 
-    // fetch material (BSDF)
-    SMaterial aMaterial = SMaterial (
-      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KD      (aTriIndex.w))),
-      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KR      (aTriIndex.w))),
-      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KT      (aTriIndex.w))),
-      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KS      (aTriIndex.w))),
-      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL (aTriIndex.w))),
-      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT (aTriIndex.w))));
+    SBSDF aBSDF;
+
+    // fetch BxDF weights
+    aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriIndex.w));
+    aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w));
+    aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w));
+    aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w)).rgb;
+
+    // compute smooth normal (in parallel with fetch)
+    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
+
+    aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),
+                               dot (aInvTransf1, aNormal),
+                               dot (aInvTransf2, aNormal)));
+
+    SLocalSpace aSpace = buildLocalSpace (aNormal);
 
 #ifdef USE_TEXTURES
-    if (aMaterial.Kd.w >= 0.f)
+    if (aBSDF.Kd.w >= 0.f)
     {
       vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);
 
@@ -789,32 +838,23 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
                            dot (aTrsfRow2, aTexCoord));
 
       vec4 aTexColor = textureLod (
-        sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f);
+        sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.f);
 
-      aMaterial.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)
+      aBSDF.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)
 
       if (aTexColor.w != 1.0f)
       {
         // mix transparency BTDF with texture alpha-channel
-        aMaterial.Kt = (UNIT - aTexColor.www) + aTexColor.w * aMaterial.Kt;
+        aBSDF.Kt = (UNIT - aTexColor.www) + aTexColor.w * aBSDF.Kt;
       }
     }
 #endif
 
-    // compute smooth normal
-    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
-
-    aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),
-                               dot (aInvTransf1, aNormal),
-                               dot (aInvTransf2, aNormal)));
-
-    SLocalSpace aSpace = buildLocalSpace (aNormal);
+    // fetch Fresnel reflectance for both layers
+    aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriIndex.w)).xyz;
+    aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriIndex.w)).xyz;
 
-    // account for self-emission (not stored in the material)
-    aRadiance += aThroughput * texelFetch (
-      uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;
-
-    if (uLightCount > 0 && convolve (aMaterial.Kd.rgb + aMaterial.Ks.rgb, aThroughput) > 0.f)
+    if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))
     {
       aExpPDF = 1.f / uLightCount;
 
@@ -833,14 +873,14 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
       aLight.xyz = SampleLight (aLight.xyz, aDistance,
         aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF);
 
-      aImpPDF = BsdfPdf (aMaterial,
+      aImpPDF = BsdfPdfLayered (aBSDF,
         toLocalSpace (-theRay.Direct, aSpace), toLocalSpace (aLight.xyz, aSpace), aThroughput);
 
       // MIS weight including division by explicit PDF
       float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);
 
-      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalMaterial (
-          aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));
+      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalBsdfLayered (
+          aBSDF, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));
 
       if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important
       {
@@ -852,25 +892,29 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
         float aVisibility = SceneAnyHit (aShadow,
           InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance);
 
-        aRadiance += aVisibility * aThroughput * aContrib;
+        aRadiance += aVisibility * (aThroughput * aContrib);
       }
     }
 
+    // account for self-emission
+    aRadiance += aThroughput * texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;
+
     if (aInMedium) // handle attenuation
     {
-      aThroughput *= exp (-aHit.Time *
-        aMaterial.Absorption.w * (UNIT - aMaterial.Absorption.rgb));
+      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w));
+
+      aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb));
     }
 
     vec3 anInput = UNIT; // sampled input direction
 
-    aImpPDF = SampleBsdf (aMaterial,
+    aImpPDF = SampleBsdfLayered (aBSDF,
       toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium);
 
-    float aSurvive = 1.f;
+    float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT)));
 
 #ifdef RUSSIAN_ROULETTE
-    aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f);
+    aSurvive = aDepth < 3 ? aSurvive : min (dot (LUMA, aThroughput), 0.95f);
 #endif
 
     // here, we additionally increase path length for non-diffuse bounces
index f38a132..4f08b83 100644 (file)
@@ -353,16 +353,16 @@ struct SSubTree
   ivec4 SubData;
 };
 
-#define MATERIAL_AMBN(index) (18 * index + 0)
-#define MATERIAL_DIFF(index) (18 * index + 1)
-#define MATERIAL_SPEC(index) (18 * index + 2)
-#define MATERIAL_EMIS(index) (18 * index + 3)
-#define MATERIAL_REFL(index) (18 * index + 4)
-#define MATERIAL_REFR(index) (18 * index + 5)
-#define MATERIAL_TRAN(index) (18 * index + 6)
-#define MATERIAL_TRS1(index) (18 * index + 7)
-#define MATERIAL_TRS2(index) (18 * index + 8)
-#define MATERIAL_TRS3(index) (18 * index + 9)
+#define MATERIAL_AMBN(index) (19 * index + 0)
+#define MATERIAL_DIFF(index) (19 * index + 1)
+#define MATERIAL_SPEC(index) (19 * index + 2)
+#define MATERIAL_EMIS(index) (19 * index + 3)
+#define MATERIAL_REFL(index) (19 * index + 4)
+#define MATERIAL_REFR(index) (19 * index + 5)
+#define MATERIAL_TRAN(index) (19 * index + 6)
+#define MATERIAL_TRS1(index) (19 * index + 7)
+#define MATERIAL_TRS2(index) (19 * index + 8)
+#define MATERIAL_TRS3(index) (19 * index + 9)
 
 #define TRS_OFFSET(treelet) treelet.SubData.x
 #define BVH_OFFSET(treelet) treelet.SubData.y
index 04cd95a..f33cc5b 100644 (file)
@@ -32,25 +32,25 @@ static const char Shaders_PathtraceBase_fs[] =
   "};\n"
   "\n"
   "//! Describes material properties (BSDF).\n"
-  "struct SMaterial\n"
+  "struct SBSDF\n"
   "{\n"
-  "  //! Weight of the Lambertian BRDF.\n"
+  "  //! Weight of coat specular/glossy BRDF.\n"
+  "  vec4 Kc;\n"
+  "\n"
+  "  //! Weight of base diffuse BRDF.\n"
   "  vec4 Kd;\n"
   "\n"
-  "  //! Weight of the reflection BRDF.\n"
-  "  vec3 Kr;\n"
+  "  //! Weight of base specular/glossy BRDF.\n"
+  "  vec4 Ks;\n"
   "\n"
-  "  //! Weight of the transmission BTDF.\n"
+  "  //! Weight of base specular/glossy BTDF.\n"
   "  vec3 Kt;\n"
   "\n"
-  "  //! Weight of the Blinn BRDF (and roughness).\n"
-  "  vec4 Ks;\n"
-  "\n"
-  "  //! Fresnel coefficients.\n"
-  "  vec3 Fresnel;\n"
+  "  //! Fresnel coefficients of coat layer.\n"
+  "  vec3 FresnelCoat;\n"
   "\n"
-  "  //! Absorption color and intensity of the media.\n"
-  "  vec4 Absorption;\n"
+  "  //! Fresnel coefficients of base layer.\n"
+  "  vec3 FresnelBase;\n"
   "};\n"
   "\n"
   "///////////////////////////////////////////////////////////////////////////////////////\n"
@@ -151,19 +151,19 @@ static const char Shaders_PathtraceBase_fs[] =
   "//=======================================================================\n"
   "float fresnelDielectric (in float theCosI, in float theIndex)\n"
   "{\n"
+  "  float aFresnel = 1.f;\n"
+  "\n"
   "  float anEtaI = theCosI > 0.f ? 1.f : theIndex;\n"
   "  float anEtaT = theCosI > 0.f ? theIndex : 1.f;\n"
   "\n"
-  "  float aSinT = (anEtaI / anEtaT) * sqrt (1.f - theCosI * theCosI);\n"
+  "  float aSinT2 = (anEtaI * anEtaI) / (anEtaT * anEtaT) * (1.f - theCosI * theCosI);\n"
   "\n"
-  "  if (aSinT >= 1.f)\n"
+  "  if (aSinT2 < 1.f)\n"
   "  {\n"
-  "    return 1.f;\n"
+  "    aFresnel = fresnelDielectric (abs (theCosI), sqrt (1.f - aSinT2), anEtaI, anEtaT);\n"
   "  }\n"
   "\n"
-  "  float aCosT = sqrt (1.f - aSinT * aSinT);\n"
-  "\n"
-  "  return fresnelDielectric (abs (theCosI), aCosT, anEtaI, anEtaT);\n"
+  "  return aFresnel;\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -200,22 +200,26 @@ static const char Shaders_PathtraceBase_fs[] =
   "//=======================================================================\n"
   "vec3 fresnelMedia (in float theCosI, in vec3 theFresnel)\n"
   "{\n"
+  "  vec3 aFresnel;\n"
+  "\n"
   "  if (theFresnel.x > FRESNEL_SCHLICK)\n"
   "  {\n"
-  "    return fresnelSchlick (abs (theCosI), theFresnel);\n"
+  "    aFresnel = fresnelSchlick (abs (theCosI), theFresnel);\n"
   "  }\n"
-  "\n"
-  "  if (theFresnel.x > FRESNEL_CONSTANT)\n"
+  "  else if (theFresnel.x > FRESNEL_CONSTANT)\n"
   "  {\n"
-  "    return vec3 (theFresnel.z);\n"
+  "    aFresnel = vec3 (theFresnel.z);\n"
   "  }\n"
-  "\n"
-  "  if (theFresnel.x > FRESNEL_CONDUCTOR)\n"
+  "  else if (theFresnel.x > FRESNEL_CONDUCTOR)\n"
+  "  {\n"
+  "    aFresnel = vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\n"
+  "  }\n"
+  "  else\n"
   "  {\n"
-  "    return vec3 (fresnelConductor (abs (theCosI), theFresnel.y, theFresnel.z));\n"
+  "    aFresnel = vec3 (fresnelDielectric (theCosI, theFresnel.y));\n"
   "  }\n"
   "\n"
-  "  return vec3 (fresnelDielectric (theCosI, theFresnel.y));\n"
+  "  return aFresnel;\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -252,36 +256,34 @@ static const char Shaders_PathtraceBase_fs[] =
   "  return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI);\n"
   "}\n"
   "\n"
+  "#define FLT_EPSILON 1.0e-5f\n"
+  "\n"
   "//=======================================================================\n"
   "// function : SmithG1\n"
   "// purpose  :\n"
   "//=======================================================================\n"
   "float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness)\n"
   "{\n"
-  "  if (dot (theDirection, theM) * theDirection.z <= 0.f)\n"
-  "  {\n"
-  "    return 0.f;\n"
-  "  }\n"
+  "  float aResult = 0.f;\n"
   "\n"
-  "  float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n"
-  "\n"
-  "  if (aTanThetaM == 0.0f)\n"
+  "  if (dot (theDirection, theM) * theDirection.z > 0.f)\n"
   "  {\n"
-  "    return 1.f;\n"
-  "  }\n"
+  "    float aTanThetaM = sqrt (1.f - theDirection.z * theDirection.z) / theDirection.z;\n"
   "\n"
-  "  float aVal = 1.f / (theRoughness * aTanThetaM);\n"
+  "    if (aTanThetaM == 0.f)\n"
+  "    {\n"
+  "      aResult = 1.f;\n"
+  "    }\n"
+  "    else\n"
+  "    {\n"
+  "      float aVal = 1.f / (theRoughness * aTanThetaM);\n"
   "\n"
-  "  if (aVal >= 1.6f)\n"
-  "  {\n"
-  "    return 1.f;\n"
+  "      // Use rational approximation to shadowing-masking function (from Mitsuba)\n"
+  "      aResult = (3.535f + 2.181f * aVal) / (1.f / aVal + 2.276f + 2.577f * aVal);\n"
+  "    }\n"
   "  }\n"
   "\n"
-  "  // Use fast and accurate rational approximation to the\n"
-  "  // shadowing-masking function (from Mitsuba renderer)\n"
-  "  float aSqr = aVal * aVal;\n"
-  "\n"
-  "  return (3.535f * aVal + 2.181f * aSqr) / (1.f + 2.276f * aVal + 2.577f * aSqr);\n"
+  "  return min (aResult, 1.f);\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -309,18 +311,31 @@ static const char Shaders_PathtraceBase_fs[] =
   "}\n"
   "\n"
   "//=======================================================================\n"
-  "// function : EvalMaterial\n"
+  "// function : EvalBsdfLayered\n"
   "// purpose  : Evaluates BSDF for specified material, with cos(N, PSI)\n"
   "//=======================================================================\n"
-  "vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo)\n"
+  "vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo)\n"
   "{\n"
   "#ifdef TWO_SIDED_BXDF\n"
   "  theWi.z *= sign (theWi.z);\n"
   "  theWo.z *= sign (theWo.z);\n"
   "#endif\n"
   "\n"
-  "  return theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo) +\n"
-  "    theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.Fresnel, theBSDF.Ks.w);\n"
+  "  vec3 aBxDF = theBSDF.Kd.rgb * EvalLambertianReflection (theWi, theWo);\n"
+  "\n"
+  "  if (theBSDF.Ks.w > FLT_EPSILON)\n"
+  "  {\n"
+  "    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w);\n"
+  "  }\n"
+  "\n"
+  "  aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n"
+  "\n"
+  "  if (theBSDF.Kc.w > FLT_EPSILON)\n"
+  "  {\n"
+  "    aBxDF += theBSDF.Kc.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelCoat, theBSDF.Kc.w);\n"
+  "  }\n"
+  "\n"
+  "  return aBxDF;\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -352,7 +367,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "}\n"
   "\n"
   "//=======================================================================\n"
-  "// function : SampleBlinnReflection\n"
+  "// function : SampleGlossyBlinnReflection\n"
   "// purpose  : Samples Blinn BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
   "//            The BRDF is a product of three main terms, D, G, and F,\n"
   "//            which is then divided by two cosine terms. Here we perform\n"
@@ -361,7 +376,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "//            terms would be complex, and it is the D term that accounts\n"
   "//            for most of the variation.\n"
   "//=======================================================================\n"
-  "vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)\n"
+  "vec3 SampleGlossyBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)\n"
   "{\n"
   "  float aKsi1 = RandFloat();\n"
   "  float aKsi2 = RandFloat();\n"
@@ -399,7 +414,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "  }\n"
   "\n"
   "  // Jacobian of half-direction mapping\n"
-  "  thePDF /= 4.f * dot (theWi, aM);\n"
+  "  thePDF /= 4.f * aCosDelta;\n"
   "\n"
   "  // compute shadow-masking coefficient\n"
   "  float aG = SmithG1 (theWo, aM, theRoughness) *\n"
@@ -414,139 +429,152 @@ static const char Shaders_PathtraceBase_fs[] =
   "}\n"
   "\n"
   "//=======================================================================\n"
-  "// function : SampleSpecularReflection\n"
-  "// purpose  : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
+  "// function : BsdfPdfLayered\n"
+  "// purpose  : Calculates BSDF of sampling input knowing output\n"
   "//=======================================================================\n"
-  "vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)\n"
+  "float BsdfPdfLayered (in SBSDF theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\n"
   "{\n"
-  "  // Sample input direction\n"
-  "  theWi = vec3 (-theWo.x,\n"
-  "                -theWo.y,\n"
-  "                 theWo.z);\n"
-  "\n"
-  "#ifdef TWO_SIDED_BXDF\n"
-  "  return fresnelMedia (theWo.z, theFresnel);\n"
-  "#else\n"
-  "  return fresnelMedia (theWo.z, theFresnel) * step (0.f, theWo.z);\n"
-  "#endif\n"
-  "}\n"
+  "  float aPDF = 0.f; // PDF of sampling input direction\n"
   "\n"
-  "//=======================================================================\n"
-  "// function : SampleSpecularTransmission\n"
-  "// purpose  : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
-  "//=======================================================================\n"
-  "vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)\n"
-  "{\n"
-  "  vec3 aFactor = fresnelMedia (theWo.z, theFresnel);\n"
+  "  // We choose whether the light is reflected or transmitted\n"
+  "  // by the coating layer according to the Fresnel equations\n"
+  "  vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n"
   "\n"
-  "  float aReflection = convolve (aFactor, theWeight);\n"
+  "  // Coat BRDF is scaled by its Fresnel reflectance term. For\n"
+  "  // reasons of simplicity we scale base BxDFs only by coat's\n"
+  "  // Fresnel transmittance term\n"
+  "  vec3 aCoatT = UNIT - aCoatF;\n"
   "\n"
-  "  // sample specular BRDF/BTDF\n"
-  "  if (RandFloat() <= aReflection)\n"
-  "  {\n"
-  "    theWi = vec3 (-theWo.x,\n"
-  "                  -theWo.y,\n"
-  "                   theWo.z);\n"
+  "  float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);\n"
+  "  float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);\n"
+  "  float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);\n"
+  "  float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);\n"
   "\n"
-  "    theWeight = aFactor * (1.f / aReflection);\n"
-  "  }\n"
-  "  else\n"
+  "  if (theWi.z * theWo.z > 0.f)\n"
   "  {\n"
-  "    theInside = !theInside;\n"
-  "\n"
-  "    transmitted (theFresnel.y, theWo, theWi);\n"
-  "\n"
-  "    theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection));\n"
-  "  }\n"
-  "\n"
-  "  return theWeight;\n"
-  "}\n"
-  "\n"
-  "#define FLT_EPSILON 1.0e-5F\n"
-  "\n"
-  "//=======================================================================\n"
-  "// function : BsdfPdf\n"
-  "// purpose  : Calculates BSDF of sampling input knowing output\n"
-  "//=======================================================================\n"
-  "float BsdfPdf (in SMaterial theBSDF, in vec3 theWo, in vec3 theWi, in vec3 theWeight)\n"
-  "{\n"
-  "  float aPd = convolve (theBSDF.Kd.rgb, theWeight);\n"
-  "  float aPs = convolve (theBSDF.Ks.rgb, theWeight);\n"
-  "  float aPr = convolve (theBSDF.Kr.rgb, theWeight);\n"
-  "  float aPt = convolve (theBSDF.Kt.rgb, theWeight);\n"
+  "    vec3 aH = normalize (theWi + theWo);\n"
   "\n"
-  "  float aReflection = aPd + aPs + aPr + aPt;\n"
+  "    aPDF = aPd * abs (theWi.z / M_PI);\n"
   "\n"
-  "  float aPDF = 0.f; // PDF of sampling input direction\n"
+  "    if (theBSDF.Kc.w > FLT_EPSILON)\n"
+  "    {\n"
+  "      float aPower = max (2.f / (theBSDF.Kc.w * theBSDF.Kc.w) - 2.f, 0.f); // roughness --> exponent\n"
   "\n"
-  "  if (theWi.z * theWo.z > 0.f)\n"
-  "  {\n"
-  "    vec3 aH = normalize (theWi + theWo);\n"
+  "      aPDF += aPc * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);\n"
+  "    }\n"
   "\n"
-  "    // roughness value --> Blinn exponent\n"
-  "    float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);\n"
+  "    if (theBSDF.Ks.w > FLT_EPSILON)\n"
+  "    {\n"
+  "      float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f); // roughness --> exponent\n"
   "\n"
-  "    aPDF = aPd * abs (theWi.z / M_PI) +\n"
-  "      aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / (4.f * dot (theWi, aH));\n"
+  "      aPDF += aPs * (aPower + 2.f) * (0.25f / M_2_PI) * pow (abs (aH.z), aPower + 1.f) / dot (theWi, aH);\n"
+  "    }\n"
   "  }\n"
   "\n"
-  "  return aPDF / aReflection;\n"
+  "  return aPDF / (aPc + aPd + aPs + aPt);\n"
   "}\n"
   "\n"
   "//! Tool macro to handle sampling of particular BxDF\n"
-  "#define PICK_BXDF(p, k) aPDF = p / aReflection; theWeight *= k / aPDF;\n"
+  "#define PICK_BXDF_LAYER(p, k) aPDF = p / aTotalR; theWeight *= k / aPDF;\n"
   "\n"
   "//=======================================================================\n"
-  "// function : SampleBsdf\n"
+  "// function : SampleBsdfLayered\n"
   "// purpose  : Samples specified composite material (BSDF)\n"
   "//=======================================================================\n"
-  "float SampleBsdf (in SMaterial theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)\n"
+  "float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout vec3 theWeight, inout bool theInside)\n"
   "{\n"
-  "  // compute probability of each reflection type (BxDF)\n"
-  "  float aPd = convolve (theBSDF.Kd.rgb, theWeight);\n"
-  "  float aPs = convolve (theBSDF.Ks.rgb, theWeight);\n"
-  "  float aPr = convolve (theBSDF.Kr.rgb, theWeight);\n"
-  "  float aPt = convolve (theBSDF.Kt.rgb, theWeight);\n"
-  "\n"
-  "  float aReflection = aPd + aPs + aPr + aPt;\n"
-  "\n"
-  "  // choose BxDF component to sample\n"
-  "  float aKsi = aReflection * RandFloat();\n"
+  "  // NOTE: OCCT uses two-layer material model. We have base diffuse, glossy, or transmissive\n"
+  "  // layer, covered by one glossy/specular coat. In the current model, the layers themselves\n"
+  "  // have no thickness; they can simply reflect light or transmits it to the layer under it.\n"
+  "  // We use actual BRDF model only for direct reflection by the coat layer. For transmission\n"
+  "  // through this layer, we approximate it as a flat specular surface.\n"
+  "\n"
+  "  float aPDF = 0.f; // PDF of sampled direction\n"
+  "\n"
+  "  // We choose whether the light is reflected or transmitted\n"
+  "  // by the coating layer according to the Fresnel equations\n"
+  "  vec3 aCoatF = fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n"
+  "\n"
+  "  // Coat BRDF is scaled by its Fresnel term. According to\n"
+  "  // Wilkie-Weidlich layered BSDF model, transmission term\n"
+  "  // for light passing through the coat at direction I and\n"
+  "  // leaving it in O is T = ( 1 - F (O) ) x ( 1 - F (I) ).\n"
+  "  // For reasons of simplicity, we discard the second term\n"
+  "  // and scale base BxDFs only by the first term.\n"
+  "  vec3 aCoatT = UNIT - aCoatF;\n"
+  "\n"
+  "  float aPc = dot (theBSDF.Kc.rgb * aCoatF, theWeight);\n"
+  "  float aPd = dot (theBSDF.Kd.rgb * aCoatT, theWeight);\n"
+  "  float aPs = dot (theBSDF.Ks.rgb * aCoatT, theWeight);\n"
+  "  float aPt = dot (theBSDF.Kt.rgb * aCoatT, theWeight);\n"
+  "\n"
+  "  // Calculate total reflection probability\n"
+  "  float aTotalR = (aPc + aPd) + (aPs + aPt);\n"
+  "\n"
+  "  // Generate random variable to select BxDF\n"
+  "  float aKsi = aTotalR * RandFloat();\n"
+  "\n"
+  "  if (aKsi < aPc) // REFLECTION FROM COAT\n"
+  "  {\n"
+  "    PICK_BXDF_LAYER (aPc, theBSDF.Kc.rgb)\n"
   "\n"
-  "  // BxDF's PDF of sampled direction\n"
-  "  float aPDF = 0.f;\n"
+  "    if (theBSDF.Kc.w < FLT_EPSILON)\n"
+  "    {\n"
+  "      theWeight *= aCoatF;\n"
   "\n"
-  "  if (aKsi < aPd) // diffuse reflection\n"
-  "  {\n"
-  "    PICK_BXDF (aPd, theBSDF.Kd.rgb);\n"
+  "      theWi = vec3 (-theWo.x,\n"
+  "                    -theWo.y,\n"
+  "                     theWo.z);\n"
+  "    }\n"
+  "    else\n"
+  "    {\n"
+  "      theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelCoat, theBSDF.Kc.w, aPDF);\n"
+  "    }\n"
   "\n"
-  "    theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\n"
+  "    aPDF = mix (aPDF, MAXFLOAT, theBSDF.Kc.w < FLT_EPSILON);\n"
   "  }\n"
-  "  else if (aKsi < aPd + aPs) // glossy reflection\n"
+  "  else if (aKsi < aTotalR) // REFLECTION FROM BASE\n"
   "  {\n"
-  "    PICK_BXDF (aPs, theBSDF.Ks.rgb);\n"
+  "    theWeight *= aCoatT;\n"
   "\n"
-  "    theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);\n"
-  "  }\n"
-  "  else if (aKsi < aPd + aPs + aPr) // specular reflection\n"
-  "  {\n"
-  "    PICK_BXDF (aPr, theBSDF.Kr.rgb);\n"
+  "    if (aKsi < aPc + aPd) // diffuse BRDF\n"
+  "    {\n"
+  "      PICK_BXDF_LAYER (aPd, theBSDF.Kd.rgb)\n"
+  "\n"
+  "      theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\n"
+  "    }\n"
+  "    else if (aKsi < (aPc + aPd) + aPs) // specular/glossy BRDF\n"
+  "    {\n"
+  "      PICK_BXDF_LAYER (aPs, theBSDF.Ks.rgb)\n"
   "\n"
-  "    aPDF = MAXFLOAT;\n"
+  "      if (theBSDF.Ks.w < FLT_EPSILON)\n"
+  "      {\n"
+  "        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);\n"
   "\n"
-  "    theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);\n"
-  "  }\n"
-  "  else if (aKsi < aReflection) // specular transmission\n"
-  "  {\n"
-  "    PICK_BXDF (aPt, theBSDF.Kt.rgb);\n"
+  "        theWi = vec3 (-theWo.x,\n"
+  "                      -theWo.y,\n"
+  "                       theWo.z);\n"
+  "      }\n"
+  "      else\n"
+  "      {\n"
+  "        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);\n"
+  "      }\n"
   "\n"
-  "    aPDF = MAXFLOAT;\n"
+  "      aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);\n"
+  "    }\n"
+  "    else // specular transmission\n"
+  "    {\n"
+  "      PICK_BXDF_LAYER (aPt, theBSDF.Kt.rgb)\n"
   "\n"
-  "    theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);\n"
+  "      // refracted direction should exist if we are here\n"
+  "      transmitted (theBSDF.FresnelCoat.y, theWo, theWi);\n"
+  "\n"
+  "      theInside = !theInside; aPDF = MAXFLOAT;\n"
+  "    }\n"
   "  }\n"
   "\n"
   "  // path termination for extra small weights\n"
-  "  theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aReflection));\n"
+  "  theWeight = mix (ZERO, theWeight, step (FLT_EPSILON, aTotalR));\n"
   "\n"
   "  return aPDF;\n"
   "}\n"
@@ -692,15 +720,16 @@ static const char Shaders_PathtraceBase_fs[] =
   "#define MIN_THROUGHPUT   vec3 (1.0e-3f)\n"
   "#define MIN_CONTRIBUTION vec3 (1.0e-2f)\n"
   "\n"
-  "#define MATERIAL_KD(index)      (18 * index + 11)\n"
-  "#define MATERIAL_KR(index)      (18 * index + 12)\n"
-  "#define MATERIAL_KT(index)      (18 * index + 13)\n"
-  "#define MATERIAL_KS(index)      (18 * index + 14)\n"
-  "#define MATERIAL_LE(index)      (18 * index + 15)\n"
-  "#define MATERIAL_FRESNEL(index) (18 * index + 16)\n"
-  "#define MATERIAL_ABSORPT(index) (18 * index + 17)\n"
+  "#define MATERIAL_KC(index)           (19 * index + 11)\n"
+  "#define MATERIAL_KD(index)           (19 * index + 12)\n"
+  "#define MATERIAL_KS(index)           (19 * index + 13)\n"
+  "#define MATERIAL_KT(index)           (19 * index + 14)\n"
+  "#define MATERIAL_LE(index)           (19 * index + 15)\n"
+  "#define MATERIAL_FRESNEL_COAT(index) (19 * index + 16)\n"
+  "#define MATERIAL_FRESNEL_BASE(index) (19 * index + 17)\n"
+  "#define MATERIAL_ABSORPT_BASE(index) (19 * index + 18)\n"
   "\n"
-  "//! Enables experimental russian roulette sampling path termination.\n"
+  "//! Enables experimental Russian roulette sampling path termination.\n"
   "//! In most cases, it provides faster image convergence with minimal\n"
   "//! bias, so it is enabled by default.\n"
   "#define RUSSIAN_ROULETTE\n"
@@ -716,6 +745,18 @@ static const char Shaders_PathtraceBase_fs[] =
   "#endif\n"
   "\n"
   "//=======================================================================\n"
+  "// function : IsNotZero\n"
+  "// purpose  : Checks whether BSDF reflects direct light\n"
+  "//=======================================================================\n"
+  "bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput)\n"
+  "{\n"
+  "  vec3 aGlossy = theBSDF.Kc.rgb * step (FLT_EPSILON, theBSDF.Kc.w) +\n"
+  "                 theBSDF.Ks.rgb * step (FLT_EPSILON, theBSDF.Ks.w);\n"
+  "\n"
+  "  return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON;\n"
+  "}\n"
+  "\n"
+  "//=======================================================================\n"
   "// function : PathTrace\n"
   "// purpose  : Calculates radiance along the given ray\n"
   "//=======================================================================\n"
@@ -769,17 +810,25 @@ static const char Shaders_PathtraceBase_fs[] =
   "      aRaytraceDepth = (aNDCPoint.z / aNDCPoint.w + aPolygonOffset * POLYGON_OFFSET_SCALE) * 0.5f + 0.5f;\n"
   "    }\n"
   "\n"
-  "    // fetch material (BSDF)\n"
-  "    SMaterial aMaterial = SMaterial (\n"
-  "      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KD      (aTriIndex.w))),\n"
-  "      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KR      (aTriIndex.w))),\n"
-  "      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KT      (aTriIndex.w))),\n"
-  "      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_KS      (aTriIndex.w))),\n"
-  "      vec3 (texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL (aTriIndex.w))),\n"
-  "      vec4 (texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT (aTriIndex.w))));\n"
+  "    SBSDF aBSDF;\n"
+  "\n"
+  "    // fetch BxDF weights\n"
+  "    aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriIndex.w));\n"
+  "    aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriIndex.w));\n"
+  "    aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriIndex.w));\n"
+  "    aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriIndex.w)).rgb;\n"
+  "\n"
+  "    // compute smooth normal (in parallel with fetch)\n"
+  "    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\n"
+  "\n"
+  "    aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),\n"
+  "                               dot (aInvTransf1, aNormal),\n"
+  "                               dot (aInvTransf2, aNormal)));\n"
+  "\n"
+  "    SLocalSpace aSpace = buildLocalSpace (aNormal);\n"
   "\n"
   "#ifdef USE_TEXTURES\n"
-  "    if (aMaterial.Kd.w >= 0.f)\n"
+  "    if (aBSDF.Kd.w >= 0.f)\n"
   "    {\n"
   "      vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);\n"
   "\n"
@@ -792,32 +841,23 @@ static const char Shaders_PathtraceBase_fs[] =
   "                           dot (aTrsfRow2, aTexCoord));\n"
   "\n"
   "      vec4 aTexColor = textureLod (\n"
-  "        sampler2D (uTextureSamplers[int (aMaterial.Kd.w)]), aTexCoord.st, 0.f);\n"
+  "        sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.f);\n"
   "\n"
-  "      aMaterial.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)\n"
+  "      aBSDF.Kd.rgb *= (aTexColor.rgb * aTexColor.rgb) * aTexColor.w; // de-gamma correction (for gamma = 2)\n"
   "\n"
   "      if (aTexColor.w != 1.0f)\n"
   "      {\n"
   "        // mix transparency BTDF with texture alpha-channel\n"
-  "        aMaterial.Kt = (UNIT - aTexColor.www) + aTexColor.w * aMaterial.Kt;\n"
+  "        aBSDF.Kt = (UNIT - aTexColor.www) + aTexColor.w * aBSDF.Kt;\n"
   "      }\n"
   "    }\n"
   "#endif\n"
   "\n"
-  "    // compute smooth normal\n"
-  "    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\n"
-  "\n"
-  "    aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),\n"
-  "                               dot (aInvTransf1, aNormal),\n"
-  "                               dot (aInvTransf2, aNormal)));\n"
-  "\n"
-  "    SLocalSpace aSpace = buildLocalSpace (aNormal);\n"
+  "    // fetch Fresnel reflectance for both layers\n"
+  "    aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriIndex.w)).xyz;\n"
+  "    aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriIndex.w)).xyz;\n"
   "\n"
-  "    // account for self-emission (not stored in the material)\n"
-  "    aRadiance += aThroughput * texelFetch (\n"
-  "      uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;\n"
-  "\n"
-  "    if (uLightCount > 0 && convolve (aMaterial.Kd.rgb + aMaterial.Ks.rgb, aThroughput) > 0.f)\n"
+  "    if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))\n"
   "    {\n"
   "      aExpPDF = 1.f / uLightCount;\n"
   "\n"
@@ -836,14 +876,14 @@ static const char Shaders_PathtraceBase_fs[] =
   "      aLight.xyz = SampleLight (aLight.xyz, aDistance,\n"
   "        aLight.w == 0.f /* is infinite */, aParam.w /* max cos or radius */, aExpPDF);\n"
   "\n"
-  "      aImpPDF = BsdfPdf (aMaterial,\n"
+  "      aImpPDF = BsdfPdfLayered (aBSDF,\n"
   "        toLocalSpace (-theRay.Direct, aSpace), toLocalSpace (aLight.xyz, aSpace), aThroughput);\n"
   "\n"
   "      // MIS weight including division by explicit PDF\n"
   "      float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);\n"
   "\n"
-  "      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalMaterial (\n"
-  "          aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));\n"
+  "      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalBsdfLayered (\n"
+  "          aBSDF, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));\n"
   "\n"
   "      if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important\n"
   "      {\n"
@@ -855,25 +895,29 @@ static const char Shaders_PathtraceBase_fs[] =
   "        float aVisibility = SceneAnyHit (aShadow,\n"
   "          InverseDirection (aLight.xyz), aLight.w == 0.f ? MAXFLOAT : aDistance);\n"
   "\n"
-  "        aRadiance += aVisibility * aThroughput * aContrib;\n"
+  "        aRadiance += aVisibility * (aThroughput * aContrib);\n"
   "      }\n"
   "    }\n"
   "\n"
+  "    // account for self-emission\n"
+  "    aRadiance += aThroughput * texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w)).rgb;\n"
+  "\n"
   "    if (aInMedium) // handle attenuation\n"
   "    {\n"
-  "      aThroughput *= exp (-aHit.Time *\n"
-  "        aMaterial.Absorption.w * (UNIT - aMaterial.Absorption.rgb));\n"
+  "      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w));\n"
+  "\n"
+  "      aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb));\n"
   "    }\n"
   "\n"
   "    vec3 anInput = UNIT; // sampled input direction\n"
   "\n"
-  "    aImpPDF = SampleBsdf (aMaterial,\n"
+  "    aImpPDF = SampleBsdfLayered (aBSDF,\n"
   "      toLocalSpace (-theRay.Direct, aSpace), anInput, aThroughput, aInMedium);\n"
   "\n"
-  "    float aSurvive = 1.f;\n"
+  "    float aSurvive = float (any (greaterThan (aThroughput, MIN_THROUGHPUT)));\n"
   "\n"
   "#ifdef RUSSIAN_ROULETTE\n"
-  "    aSurvive = aDepth < 3 ? 1.f : min (dot (LUMA, aThroughput), 0.95f);\n"
+  "    aSurvive = aDepth < 3 ? aSurvive : min (dot (LUMA, aThroughput), 0.95f);\n"
   "#endif\n"
   "\n"
   "    // here, we additionally increase path length for non-diffuse bounces\n"
index 321a854..a25d6fc 100644 (file)
@@ -356,16 +356,16 @@ static const char Shaders_RaytraceBase_fs[] =
   "  ivec4 SubData;\n"
   "};\n"
   "\n"
-  "#define MATERIAL_AMBN(index) (18 * index + 0)\n"
-  "#define MATERIAL_DIFF(index) (18 * index + 1)\n"
-  "#define MATERIAL_SPEC(index) (18 * index + 2)\n"
-  "#define MATERIAL_EMIS(index) (18 * index + 3)\n"
-  "#define MATERIAL_REFL(index) (18 * index + 4)\n"
-  "#define MATERIAL_REFR(index) (18 * index + 5)\n"
-  "#define MATERIAL_TRAN(index) (18 * index + 6)\n"
-  "#define MATERIAL_TRS1(index) (18 * index + 7)\n"
-  "#define MATERIAL_TRS2(index) (18 * index + 8)\n"
-  "#define MATERIAL_TRS3(index) (18 * index + 9)\n"
+  "#define MATERIAL_AMBN(index) (19 * index + 0)\n"
+  "#define MATERIAL_DIFF(index) (19 * index + 1)\n"
+  "#define MATERIAL_SPEC(index) (19 * index + 2)\n"
+  "#define MATERIAL_EMIS(index) (19 * index + 3)\n"
+  "#define MATERIAL_REFL(index) (19 * index + 4)\n"
+  "#define MATERIAL_REFR(index) (19 * index + 5)\n"
+  "#define MATERIAL_TRAN(index) (19 * index + 6)\n"
+  "#define MATERIAL_TRS1(index) (19 * index + 7)\n"
+  "#define MATERIAL_TRS2(index) (19 * index + 8)\n"
+  "#define MATERIAL_TRS3(index) (19 * index + 9)\n"
   "\n"
   "#define TRS_OFFSET(treelet) treelet.SubData.x\n"
   "#define BVH_OFFSET(treelet) treelet.SubData.y\n"
index 003a09a..8f356a7 100644 (file)
@@ -5247,7 +5247,7 @@ static Standard_Integer vr(Draw_Interpretor& , Standard_Integer , const char** a
 //function : VBsdf
 //purpose  :
 //===============================================================================================
-static int VBsdf (Draw_Interpretor& theDi,
+static int VBsdf (Draw_Interpretor& theDI,
                   Standard_Integer  theArgsNb,
                   const char**      theArgVec)
 {
@@ -5263,30 +5263,47 @@ static int VBsdf (Draw_Interpretor& theDi,
   ViewerTest_CmdParser aCmd;
 
   aCmd.AddDescription ("Adjusts parameters of material BSDF:");
-  aCmd.AddOption ("print|echo|p", "Print BSDF");
 
-  aCmd.AddOption ("kd", "Weight of the Lambertian BRDF");
-  aCmd.AddOption ("kr", "Weight of the reflection BRDF");
-  aCmd.AddOption ("kt", "Weight of the transmission BTDF");
-  aCmd.AddOption ("ks", "Weight of the glossy Blinn BRDF");
-  aCmd.AddOption ("le", "Self-emitted radiance");
+  aCmd.AddOption ("print|echo|p", "Prints BSDF");
 
-  aCmd.AddOption ("fresnel|f", "Fresnel coefficients; Allowed fresnel formats are: Constant x, Schlick x y z, Dielectric x, Conductor x y");
+  aCmd.AddOption ("noupdate|update", "Suppresses viewer redraw call");
 
-  aCmd.AddOption ("roughness|r",    "Roughness of material (Blinn's exponent)");
-  aCmd.AddOption ("absorpCoeff|af", "Absorption coeff (only for transparent material)");
-  aCmd.AddOption ("absorpColor|ac", "Absorption color (only for transparent material)");
+  aCmd.AddOption ("kc", "Weight of coat specular/glossy BRDF");
+  aCmd.AddOption ("kd", "Weight of base diffuse BRDF");
+  aCmd.AddOption ("ks", "Weight of base specular/glossy BRDF");
+  aCmd.AddOption ("kt", "Weight of base specular/glossy BTDF");
+  aCmd.AddOption ("le", "Radiance emitted by surface");
 
-  aCmd.AddOption ("normalize|n", "Normalize BSDF coefficients");
+  aCmd.AddOption ("coatFresnel|cf", "Fresnel reflectance of coat layer. Allowed formats: Constant R, Schlick R G B, Dielectric N, Conductor N K");
+  aCmd.AddOption ("baseFresnel|bf", "Fresnel reflectance of base layer. Allowed formats: Constant R, Schlick R G B, Dielectric N, Conductor N K");
+
+  aCmd.AddOption ("coatRoughness|cr", "Roughness of coat glossy BRDF");
+  aCmd.AddOption ("baseRoughness|br", "Roughness of base glossy BRDF");
+
+  aCmd.AddOption ("absorpCoeff|af", "Absorption coeff of base transmission BTDF");
+  aCmd.AddOption ("absorpColor|ac", "Absorption color of base transmission BTDF");
+
+  aCmd.AddOption ("normalize|n", "Normalizes BSDF to ensure energy conservation");
 
   aCmd.Parse (theArgsNb, theArgVec);
 
   if (aCmd.HasOption ("help"))
   {
-    theDi.PrintHelp (theArgVec[0]);
+    theDI.PrintHelp (theArgVec[0]);
     return 0;
   }
 
+  // check viewer update mode
+  ViewerTest_AutoUpdater anUpdateTool (ViewerTest::GetAISContext(), ViewerTest::CurrentView());
+
+  for (Standard_Integer anArgIter = 1; anArgIter < theArgsNb; ++anArgIter)
+  {
+    if (anUpdateTool.parseRedrawMode (theArgVec[anArgIter]))
+    {
+      break;
+    }
+  }
+
   TCollection_AsciiString aName (aCmd.Arg ("", 0).c_str());
 
   // find object
@@ -5303,62 +5320,92 @@ static int VBsdf (Draw_Interpretor& theDi,
 
   if (aCmd.HasOption ("print"))
   {
-    Graphic3d_Vec4 aFresnel = aBSDF.Fresnel.Serialize();
-
-    std::cout << "\n"
+    theDI << "\n"
+      << "Kc:               " << aBSDF.Kc.r() << ", " << aBSDF.Kc.g() << ", " << aBSDF.Kc.b() << "\n"
       << "Kd:               " << aBSDF.Kd.r() << ", " << aBSDF.Kd.g() << ", " << aBSDF.Kd.b() << "\n"
-      << "Kr:               " << aBSDF.Kr.r() << ", " << aBSDF.Kr.g() << ", " << aBSDF.Kr.b() << "\n"
-      << "Kt:               " << aBSDF.Kt.r() << ", " << aBSDF.Kt.g() << ", " << aBSDF.Kt.b() << "\n"
       << "Ks:               " << aBSDF.Ks.r() << ", " << aBSDF.Ks.g() << ", " << aBSDF.Ks.b() << "\n"
-      << "Le:               " << aBSDF.Le.r() << ", " << aBSDF.Le.g() << ", " << aBSDF.Le.b() << "\n"
-      << "Fresnel:          ";
+      << "Kt:               " << aBSDF.Kt.r() << ", " << aBSDF.Kt.g() << ", " << aBSDF.Kt.b() << "\n"
+      << "Le:               " << aBSDF.Le.r() << ", " << aBSDF.Le.g() << ", " << aBSDF.Le.b() << "\n";
 
-    if (aFresnel.x() >= 0.f)
-    {
-      std::cout
-        << "|Schlick| " << aFresnel.x() << ", " << aFresnel.y() << ", " << aFresnel.z() << "\n";
-    }
-    else if (aFresnel.x() >= -1.5f)
-    {
-      std::cout
-        << "|Constant| " << aFresnel.z() << "\n";
-    }
-    else if (aFresnel.x() >= -2.5f)
-    {
-      std::cout
-        << "|Conductor| " << aFresnel.y() << ", " << aFresnel.z() << "\n";
-    }
-    else
+    for (int aLayerID = 0; aLayerID < 2; ++aLayerID)
     {
-      std::cout
-        << "|Dielectric| " << aFresnel.y() << "\n";
-    }
+      const Graphic3d_Vec4 aFresnel = aLayerID < 1 ? aBSDF.FresnelCoat.Serialize()
+                                                   : aBSDF.FresnelBase.Serialize();
 
+      theDI << (aLayerID < 1 ? "Coat Fresnel:     "
+                             : "Base Fresnel:     ");
+
+      if (aFresnel.x() >= 0.f)
+      {
+        theDI << "Schlick " << "R = " << aFresnel.r() << ", "
+                            << "G = " << aFresnel.g() << ", "
+                            << "B = " << aFresnel.b() << "\n";
+      }
+      else if (aFresnel.x() >= -1.5f)
+      {
+        theDI << "Constant " << aFresnel.z() << "\n";
+      }
+      else if (aFresnel.x() >= -2.5f)
+      {
+        theDI << "Conductor " << "N = " << aFresnel.y() << ", "
+                              << "K = " << aFresnel.z() << "\n";
+      }
+      else
+      {
+        theDI << "Dielectric " << "N = " << aFresnel.y() << "\n";
+      }
+    }
 
-    std::cout 
-      << "Roughness:        " << aBSDF.Roughness           << "\n"
-      << "Absorption coeff: " << aBSDF.AbsorptionCoeff     << "\n"
-      << "Absorption color: " << aBSDF.AbsorptionColor.r() << ", "
-                              << aBSDF.AbsorptionColor.g() << ", "
-                              << aBSDF.AbsorptionColor.b() << "\n";
+    theDI << "Coat roughness:   " << aBSDF.Kc.w() << "\n"
+          << "Base roughness:   " << aBSDF.Ks.w() << "\n"
+          << "Absorption coeff: " << aBSDF.Absorption.w() << "\n"
+          << "Absorption color: " << aBSDF.Absorption.r() << ", "
+                                  << aBSDF.Absorption.g() << ", "
+                                  << aBSDF.Absorption.b() << "\n";
 
     return 0;
   }
 
-  if (aCmd.HasOption ("roughness", 1, Standard_True))
+  if (aCmd.HasOption ("coatRoughness", 1, Standard_True))
+  {
+    aBSDF.Kc.w() = aCmd.ArgFloat ("coatRoughness");
+  }
+
+  if (aCmd.HasOption ("baseRoughness", 1, Standard_True))
   {
-    aCmd.Arg ("roughness", 0);
-    aBSDF.Roughness = aCmd.ArgFloat ("roughness");
+    aBSDF.Ks.w () = aCmd.ArgFloat ("baseRoughness");
   }
 
   if (aCmd.HasOption ("absorpCoeff", 1, Standard_True))
   {
-    aBSDF.AbsorptionCoeff = aCmd.ArgFloat ("absorpCoeff");
+    aBSDF.Absorption.w() = aCmd.ArgFloat ("absorpCoeff");
   }
 
   if (aCmd.HasOption ("absorpColor", 3, Standard_True))
   {
-    aBSDF.AbsorptionColor = aCmd.ArgVec3f ("absorpColor");
+    const Graphic3d_Vec3 aRGB = aCmd.ArgVec3f ("absorpColor");
+
+    aBSDF.Absorption.r() = aRGB.r();
+    aBSDF.Absorption.g() = aRGB.g();
+    aBSDF.Absorption.b() = aRGB.b();
+  }
+
+  if (aCmd.HasOption ("kc", 3) || aCmd.HasOption ("kc", 1, Standard_True))
+  {
+    Graphic3d_Vec3 aKc;
+
+    if (aCmd.HasOption ("kc", 3))
+    {
+      aKc = aCmd.ArgVec3f ("kc");
+    }
+    else
+    {
+      aKc = Graphic3d_Vec3 (aCmd.ArgFloat ("kc"));
+    }
+
+    aBSDF.Kc.r() = aKc.r();
+    aBSDF.Kc.g() = aKc.g();
+    aBSDF.Kc.b() = aKc.b();
   }
 
   if (aCmd.HasOption ("kd", 3))
@@ -5370,13 +5417,22 @@ static int VBsdf (Draw_Interpretor& theDi,
     aBSDF.Kd = Graphic3d_Vec3 (aCmd.ArgFloat ("kd"));
   }
 
-  if (aCmd.HasOption ("kr", 3))
+  if (aCmd.HasOption ("ks", 3) || aCmd.HasOption ("ks", 1, Standard_True))
   {
-    aBSDF.Kr = aCmd.ArgVec3f ("kr");
-  }
-  else if (aCmd.HasOption ("kr", 1, Standard_True))
-  {
-    aBSDF.Kr = Graphic3d_Vec3 (aCmd.ArgFloat ("kr"));
+    Graphic3d_Vec3 aKs;
+
+    if (aCmd.HasOption ("ks", 3))
+    {
+      aKs = aCmd.ArgVec3f ("ks");
+    }
+    else
+    {
+      aKs = Graphic3d_Vec3 (aCmd.ArgFloat ("ks"));
+    }
+
+    aBSDF.Ks.r() = aKs.r();
+    aBSDF.Ks.g() = aKs.g();
+    aBSDF.Ks.b() = aKs.b();
   }
 
   if (aCmd.HasOption ("kt", 3))
@@ -5388,15 +5444,6 @@ static int VBsdf (Draw_Interpretor& theDi,
     aBSDF.Kt = Graphic3d_Vec3 (aCmd.ArgFloat ("kt"));
   }
 
-  if (aCmd.HasOption ("ks", 3))
-  {
-    aBSDF.Ks = aCmd.ArgVec3f ("ks");
-  }
-  else if (aCmd.HasOption ("ks", 1, Standard_True))
-  {
-    aBSDF.Ks = Graphic3d_Vec3 (aCmd.ArgFloat ("ks"));
-  }
-
   if (aCmd.HasOption ("le", 3))
   {
     aBSDF.Le = aCmd.ArgVec3f ("le");
@@ -5407,59 +5454,73 @@ static int VBsdf (Draw_Interpretor& theDi,
   }
 
   const std::string aFresnelErrorMessage =
-    "Error! Wrong Fresnel type. Allowed types are: Constant x, Schlick x y z, Dielectric x, Conductor x y.\n";
+    "Error! Wrong Fresnel type. Allowed types are: Constant F, Schlick R G B, Dielectric N, Conductor N K\n";
 
-  if (aCmd.HasOption ("fresnel", 4)) // Schlick: type, x, y ,z
+  for (int aLayerID = 0; aLayerID < 2; ++aLayerID)
   {
-    std::string aFresnelType = aCmd.Arg ("fresnel", 0);
-    std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower);
+    const std::string aFresnel = aLayerID < 1 ? "baseFresnel"
+                                              : "coatFresnel";
 
-    if (aFresnelType == "schlick")
+    if (aCmd.HasOption (aFresnel, 4)) // Schlick: type R G B
     {
-      aBSDF.Fresnel = Graphic3d_Fresnel::CreateSchlick (
-        Graphic3d_Vec3 (static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())),
-                        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 2).c_str())),
-                        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 3).c_str()))));
-    }
-    else
-    {
-      std::cout << aFresnelErrorMessage;
-    }
-  }
-  else if (aCmd.HasOption ("fresnel", 3)) // Conductor: type, x, y
-  {
-    std::string aFresnelType = aCmd.Arg ("fresnel", 0);
-    std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower);
+      std::string aFresnelType = aCmd.Arg (aFresnel, 0);
+      std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower);
 
-    if (aFresnelType == "conductor")
-    {
-      aBSDF.Fresnel = Graphic3d_Fresnel::CreateConductor (
-        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())),
-        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 2).c_str())));
-    }
-    else
-    {
-      std::cout << aFresnelErrorMessage;
-    }
-  }
-  else if (aCmd.HasOption ("fresnel", 2)) // Dielectric, Constant: type, x
-  {
-    std::string aFresnelType = aCmd.Arg ("fresnel", 0);
-    std::transform (aFresnelType.begin(), aFresnelType.end(), aFresnelType.begin(), ::tolower);
+      if (aFresnelType == "schlick")
+      {
+        Graphic3d_Vec3 aRGB (static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str())),
+                             static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 2).c_str())),
+                             static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 3).c_str())));
 
-    if (aFresnelType == "dielectric")
-    {
-      aBSDF.Fresnel = Graphic3d_Fresnel::CreateDielectric (
-        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())));
+        aRGB.r() = std::min (std::max (aRGB.r(), 0.f), 1.f);
+        aRGB.g() = std::min (std::max (aRGB.g(), 0.f), 1.f);
+        aRGB.b() = std::min (std::max (aRGB.b(), 0.f), 1.f);
+
+        (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateSchlick (aRGB);
+      }
+      else
+      {
+        theDI << aFresnelErrorMessage.c_str() << "\n";
+      }
     }
-    else if (aFresnelType == "constant")
+    else if (aCmd.HasOption (aFresnel, 3)) // Conductor: type N K
     {
-      aBSDF.Fresnel = Graphic3d_Fresnel::CreateConstant (
-        static_cast<Standard_ShortReal> (Draw::Atof (aCmd.Arg ("fresnel", 1).c_str())));
+      std::string aFresnelType = aCmd.Arg (aFresnel, 0);
+      std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower);
+
+      if (aFresnelType == "conductor")
+      {
+        const float aN = static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str()));
+        const float aK = static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 2).c_str()));
+
+        (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateConductor (aN, aK);
+      }
+      else
+      {
+        theDI << aFresnelErrorMessage.c_str() << "\n";
+      }
     }
-    else
+    else if (aCmd.HasOption (aFresnel, 2)) // Dielectric or Constant: type N|C
     {
-      std::cout << aFresnelErrorMessage;
+      std::string aFresnelType = aCmd.Arg (aFresnel, 0);
+      std::transform (aFresnelType.begin (), aFresnelType.end (), aFresnelType.begin (), ::tolower);
+
+      if (aFresnelType == "constant")
+      {
+        const float aR = static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str()));
+
+        (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateConstant (aR);
+      }
+      else if (aFresnelType == "dielectric")
+      {
+        const float aN = static_cast<float> (Draw::Atof (aCmd.Arg (aFresnel, 1).c_str()));
+
+        (aLayerID < 1 ? aBSDF.FresnelBase : aBSDF.FresnelCoat) = Graphic3d_Fresnel::CreateDielectric (aN);
+      }
+      else
+      {
+        theDI << aFresnelErrorMessage.c_str() << "\n";
+      }
     }
   }
 
@@ -5471,8 +5532,6 @@ static int VBsdf (Draw_Interpretor& theDi,
   aMaterial.SetBSDF (aBSDF);
   anIObj->SetMaterial (aMaterial);
 
-  aView->Redraw();
-
   return 0;
 }
 
index df43214..dfd7d58 100644 (file)
@@ -7,7 +7,7 @@ source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_ball.tcl
 
 vtexture ball 21 -scale 0.1 0.1
 vsetmaterial ball plaster
-vbsdf ball -fresnel Constant 0.0
+vbsdf ball -coatFresnel Constant 0.0
 
 vfps 100
 vdump $imagedir/${casename}_zoom.png
diff --git a/tests/v3d/raytrace/sample_materials b/tests/v3d/raytrace/sample_materials
new file mode 100644 (file)
index 0000000..95e25ea
--- /dev/null
@@ -0,0 +1,7 @@
+puts "============"
+puts "Visualization - Path Tracing, Materials sample"
+puts "============"
+puts ""
+
+source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_materials.tcl
+vdump $imagedir/${casename}_materials.png