]> OCCT Git - occt-copy.git/commitdiff
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 db61c819ba815a71361122e54b833024b6ac0b57..0faa8baa73ef597d1f57ebf1d1212f7a4e5fece0 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 264ed7796e006b39593c82d4c80ea6dc445e5cf4..6cd321efdcd8f5ae800c66ef6d650772b549d00d 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 57840080a8bde9122e32cb683881865ca9c91922..4421e9c2d52a6a6a1a782cad3d36bdae134e3a8a 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 74e812f9a08ba4bb23206dfa8a34252cfbd7ac4c..7919089dd971c50641ea8d1360efd4b235a42019 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 2c4edbe05e80744be7e8a4da48286341629a666f..3b3f6db5a91a915087963030f5abf75a98d63e8c 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 17ff432f445b90040a6598706f3f20f227ce90d2..427ed303d5aa0affaedfe3e1543fbc5c2849b293 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 c078c546d543830a025ad320c3d4b87a6bfbea05..793d4acf15622694fcdc5eb77491098874ced799 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 7088826599cf7bc9e0a06b5252cf75c7d6113ec0..8dfe1afcb3e836345603ac6a4e13ac7078ff445d 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
@@ -712,6 +741,18 @@ vec3 IntersectLight (in SRay theRay, in int theDepth, in float theHitDistance, o
   #define FRAME_STEP 5
 #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 f38a132291eb1826d1d295b31216a51df64c77c5..4f08b8333d03b52b8360ff90a2986c022cb12cb2 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 04cd95aa08976e2fac2174794e5f699085454048..f33cc5bb397e2a084c80189290f0c64420163e9f 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 321a854f89c6bad64a72d0d012b0bbad82ef9512..a25d6fc36aca25f867a04cb4f9683bc776937db9 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 003a09a84e6ce2a6965aa957ef2c6ad5d62f3241..8f356a7e69f83fcc60fef10cdf6e43f4bd2878f8 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 df43214efdf09ae585f61494169267634a790592..dfd7d58b71bf8e07d4505fa6b8a1080b429ab7c4 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