0028126: Visualization, Path tracing - Provide ability to use two-sided scattering...
authordbp <dbp@opencascade.org>
Fri, 18 Nov 2016 08:13:29 +0000 (11:13 +0300)
committerapn <apn@opencascade.com>
Thu, 8 Dec 2016 13:36:09 +0000 (16:36 +0300)
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/PathtraceBase.fs
src/Shaders/Shaders_PathtraceBase_fs.pxx
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/v3d/raytrace/sample_cube_twosided [new file with mode: 0644]

index 86f3389..60c8212 100644 (file)
@@ -28,9 +28,6 @@ public:
   //! Default pixels density.
   static const unsigned int THE_DEFAULT_RESOLUTION = 72u;
 
-  //! Default number of samples per pixel.
-  static const Standard_Integer THE_DEFAULT_SPP = 1;
-
   //! Default ray-tracing depth.
   static const Standard_Integer THE_DEFAULT_DEPTH = 3;
 
@@ -53,7 +50,6 @@ public:
     NbMsaaSamples               (0),
     // ray tracing parameters
     IsGlobalIlluminationEnabled (Standard_False),
-    SamplesPerPixel             (THE_DEFAULT_SPP),
     RaytracingDepth             (THE_DEFAULT_DEPTH),
     IsShadowEnabled             (Standard_True),
     IsReflectionEnabled         (Standard_False),
@@ -63,6 +59,7 @@ public:
     CoherentPathTracingMode     (Standard_False),
     AdaptiveScreenSampling      (Standard_False),
     ShowSamplingTiles           (Standard_False),
+    TwoSidedBsdfModels          (Standard_False),
     RebuildRayTracingShaders    (Standard_False),
     // stereoscopic parameters
     StereoMode (Graphic3d_StereoMode_QuadBuffer),
@@ -104,6 +101,7 @@ public:
   Standard_Boolean        CoherentPathTracingMode;     //!< enables/disables 'coherent' tracing mode (single RNG seed within 16x16 image blocks)
   Standard_Boolean        AdaptiveScreenSampling;      //!< enables/disables adaptive screen sampling mode for path tracing, FALSE by default
   Standard_Boolean        ShowSamplingTiles;           //!< enables/disables debug mode for adaptive screen sampling, FALSE by default
+  Standard_Boolean        TwoSidedBsdfModels;          //!< forces path tracing to use two-sided versions of original one-sided scattering models
   Standard_Boolean        RebuildRayTracingShaders;    //!< forces rebuilding ray tracing shaders at the next frame
 
   Graphic3d_StereoMode    StereoMode;                  //!< stereoscopic output mode, Graphic3d_StereoMode_QuadBuffer by default
index ba07b70..463b980 100644 (file)
@@ -696,6 +696,9 @@ protected: //! @name data types related to ray-tracing
     //! Enables/disables the use of OpenGL bindless textures.
     Standard_Boolean UseBindlessTextures;
 
+    //! Enables/disables two-sided BSDF models instead of one-sided.
+    Standard_Boolean TwoSidedBsdfModels;
+
     //! Enables/disables adaptive screen sampling for path tracing.
     Standard_Boolean AdaptiveScreenSampling;
 
@@ -703,13 +706,11 @@ protected: //! @name data types related to ray-tracing
     RaytracingParams()
     : StackSize (THE_DEFAULT_STACK_SIZE),
       NbBounces (THE_DEFAULT_NB_BOUNCES),
-      TransparentShadows (Standard_False),
-      GlobalIllumination  (Standard_False),
-      UseBindlessTextures (Standard_False),
-      AdaptiveScreenSampling (Standard_False)
-    {
-      //
-    }
+      TransparentShadows     (Standard_False),
+      GlobalIllumination     (Standard_False),
+      UseBindlessTextures    (Standard_False),
+      TwoSidedBsdfModels     (Standard_False),
+      AdaptiveScreenSampling (Standard_False) { }
   };
 
   //! Describes state of OpenGL structure.
index 062e558..ba286a2 100644 (file)
@@ -1144,6 +1144,11 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
           TCollection_AsciiString ("\n#define BLOCK_SIZE ") + TCollection_AsciiString (OpenGl_TileSampler::TileSize());
       }
     }
+
+    if (myRaytraceParameters.TwoSidedBsdfModels) // two-sided BSDFs requested
+    {
+      aPrefixString += TCollection_AsciiString ("\n#define TWO_SIDED_BXDF");
+    }
   }
 
   return aPrefixString;
@@ -1354,27 +1359,18 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Handle(OpenGl_Context
       }
     }
 
-    if (myRenderParams.RaytracingDepth != myRaytraceParameters.NbBounces)
-    {
-      myRaytraceParameters.NbBounces = myRenderParams.RaytracingDepth;
-      aToRebuildShaders = Standard_True;
-    }
-
-    if (myRaytraceGeometry.HasTextures() != myRaytraceParameters.UseBindlessTextures)
+    if (myRenderParams.RaytracingDepth             != myRaytraceParameters.NbBounces
+     || myRenderParams.IsTransparentShadowEnabled  != myRaytraceParameters.TransparentShadows
+     || myRenderParams.IsGlobalIlluminationEnabled != myRaytraceParameters.GlobalIllumination
+     || myRenderParams.TwoSidedBsdfModels          != myRaytraceParameters.TwoSidedBsdfModels
+     || myRaytraceGeometry.HasTextures()           != myRaytraceParameters.UseBindlessTextures)
     {
+      myRaytraceParameters.NbBounces           = myRenderParams.RaytracingDepth;
+      myRaytraceParameters.TransparentShadows  = myRenderParams.IsTransparentShadowEnabled;
+      myRaytraceParameters.GlobalIllumination  = myRenderParams.IsGlobalIlluminationEnabled;
+      myRaytraceParameters.TwoSidedBsdfModels  = myRenderParams.TwoSidedBsdfModels;
       myRaytraceParameters.UseBindlessTextures = myRaytraceGeometry.HasTextures();
-      aToRebuildShaders = Standard_True;
-    }
 
-    if (myRenderParams.IsTransparentShadowEnabled != myRaytraceParameters.TransparentShadows)
-    {
-      myRaytraceParameters.TransparentShadows = myRenderParams.IsTransparentShadowEnabled;
-      aToRebuildShaders = Standard_True;
-    }
-
-    if (myRenderParams.IsGlobalIlluminationEnabled != myRaytraceParameters.GlobalIllumination)
-    {
-      myRaytraceParameters.GlobalIllumination = myRenderParams.IsGlobalIlluminationEnabled;
       aToRebuildShaders = Standard_True;
     }
 
index 916ac69..6b743d4 100644 (file)
@@ -244,12 +244,12 @@ void transmitted (in float theIndex, in vec3 theIncident, out vec3 theTransmit)
 //////////////////////////////////////////////////////////////////////////////////////////////
 
 //=======================================================================
-// function : HandleLambertianReflection
-// purpose  : Handles Lambertian BRDF, with cos(N, PSI)
+// function : EvalLambertianReflection
+// purpose  : Evaluates Lambertian BRDF, with cos(N, PSI)
 //=======================================================================
-float HandleLambertianReflection (in vec3 theInput, in vec3 theOutput)
+float EvalLambertianReflection (in vec3 theWi, in vec3 theWo)
 {
-  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? 0.f : theInput.z * (1.f / M_PI);
+  return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI);
 }
 
 //=======================================================================
@@ -285,13 +285,13 @@ float SmithG1 (in vec3 theDirection, in vec3 theM, in float theRoughness)
 }
 
 //=======================================================================
-// function : HandleBlinnReflection
-// purpose  : Handles Blinn glossy BRDF, with cos(N, PSI)
+// function : EvalBlinnReflection
+// purpose  : Evaluates Blinn glossy BRDF, with cos(N, PSI)
 //=======================================================================
-vec3 HandleBlinnReflection (in vec3 theInput, in vec3 theOutput, in vec3 theFresnel, in float theRoughness)
+vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in float theRoughness)
 {
   // calculate the reflection half-vec
-  vec3 aH = normalize (theInput + theOutput);
+  vec3 aH = normalize (theWi + theWo);
 
   // roughness value -> Blinn exponent
   float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f);
@@ -300,41 +300,55 @@ vec3 HandleBlinnReflection (in vec3 theInput, in vec3 theOutput, in vec3 theFres
   float aD = (aPower + 2.f) * (1.f / M_2_PI) * pow (aH.z, aPower);
 
   // calculate shadow-masking function
-  float aG = SmithG1 (theOutput, aH, theRoughness) * SmithG1 (theInput, aH, theRoughness);
+  float aG = SmithG1 (theWo, aH, theRoughness) *
+             SmithG1 (theWi, aH, theRoughness);
 
   // return total amount of reflection
-  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? ZERO :
-    aD * aG / (4.f * theOutput.z) * fresnelMedia (dot (theOutput, aH), theFresnel);
+  return (theWi.z <= 0.f || theWo.z <= 0.f) ? ZERO :
+    aD * aG / (4.f * theWo.z) * fresnelMedia (dot (theWo, aH), theFresnel);
 }
 
 //=======================================================================
-// function : HandleMaterial
-// purpose  : Returns BSDF value for specified material, with cos(N, PSI)
+// function : EvalMaterial
+// purpose  : Evaluates BSDF for specified material, with cos(N, PSI)
 //=======================================================================
-vec3 HandleMaterial (in SMaterial theBSDF, in vec3 theInput, in vec3 theOutput)
+vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo)
 {
-  return theBSDF.Kd.rgb * HandleLambertianReflection (theInput, theOutput) +
-    theBSDF.Ks.rgb * HandleBlinnReflection (theInput, theOutput, theBSDF.Fresnel, theBSDF.Ks.w);
+#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);
 }
 
 //=======================================================================
 // function : SampleLambertianReflection
 // purpose  : Samples Lambertian BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)
 //=======================================================================
-vec3 SampleLambertianReflection (in vec3 theOutput, out vec3 theInput, inout float thePDF)
+vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float thePDF)
 {
   float aKsi1 = RandFloat();
   float aKsi2 = RandFloat();
 
-  float aTemp = sqrt (aKsi2);
+  theWi = vec3 (cos (M_2_PI * aKsi1),
+                sin (M_2_PI * aKsi1),
+                sqrt (1.f - aKsi2));
+
+  theWi.xy *= sqrt (aKsi2);
 
-  theInput = vec3 (aTemp * cos (M_2_PI * aKsi1),
-                   aTemp * sin (M_2_PI * aKsi1),
-                   sqrt (1.f - aKsi2));
+#ifdef TWO_SIDED_BXDF
+  theWi.z *= sign (theWo.z);
+#endif
 
-  thePDF *= abs (theInput.z) * (1.f / M_PI);
+  thePDF *= theWi.z * (1.f / M_PI);
 
-  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? ZERO : UNIT;
+#ifdef TWO_SIDED_BXDF
+  return UNIT;
+#else
+  return mix (UNIT, ZERO, theWo.z <= 0.f);
+#endif
 }
 
 //=======================================================================
@@ -347,7 +361,7 @@ vec3 SampleLambertianReflection (in vec3 theOutput, out vec3 theInput, inout flo
 //            terms would be complex, and it is the D term that accounts
 //            for most of the variation.
 //=======================================================================
-vec3 SampleBlinnReflection (in vec3 theOutput, out vec3 theInput, in vec3 theFresnel, in float theRoughness, inout float thePDF)
+vec3 SampleBlinnReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel, in float theRoughness, inout float thePDF)
 {
   float aKsi1 = RandFloat();
   float aKsi2 = RandFloat();
@@ -367,56 +381,72 @@ vec3 SampleBlinnReflection (in vec3 theOutput, out vec3 theInput, in vec3 theFre
   // calculate PDF of sampled direction
   thePDF *= (aPower + 2.f) * (1.f / M_2_PI) * pow (aCosThetaM, aPower + 1.f);
 
-  float aCosDelta = dot (theOutput, aM);
+#ifdef TWO_SIDED_BXDF
+  bool toFlip = theWo.z < 0.f;
+
+  if (toFlip)
+    theWo.z = -theWo.z;
+#endif
+
+  float aCosDelta = dot (theWo, aM);
 
   // pick input based on half direction
-  theInput = -theOutput + 2.f * aCosDelta * aM;
+  theWi = -theWo + 2.f * aCosDelta * aM;
 
-  if (theInput.z <= 0.f || theOutput.z <= 0.f)
+  if (theWi.z <= 0.f || theWo.z <= 0.f)
   {
     return ZERO;
   }
 
   // Jacobian of half-direction mapping
-  thePDF /= 4.f * dot (theInput, aM);
+  thePDF /= 4.f * dot (theWi, aM);
 
   // compute shadow-masking coefficient
-  float aG = SmithG1 (theOutput, aM, theRoughness) * SmithG1 (theInput, aM, theRoughness);
+  float aG = SmithG1 (theWo, aM, theRoughness) *
+             SmithG1 (theWi, aM, theRoughness);
+
+#ifdef TWO_SIDED_BXDF
+  if (toFlip)
+    theWi.z = -theWi.z;
+#endif
 
-  return aG * aCosDelta / (theOutput.z * aM.z) * fresnelMedia (aCosDelta, theFresnel);
+  return (aG * aCosDelta) / (theWo.z * aM.z) * fresnelMedia (aCosDelta, theFresnel);
 }
 
 //=======================================================================
 // function : SampleSpecularReflection
 // purpose  : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)
 //=======================================================================
-vec3 SampleSpecularReflection (in vec3 theOutput, out vec3 theInput, in vec3 theFresnel)
+vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)
 {
   // Sample input direction
-  theInput = vec3 (-theOutput.x,
-                   -theOutput.y,
-                    theOutput.z);
-
-  return fresnelMedia (theOutput.z, theFresnel);
+  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
 }
 
 //=======================================================================
 // function : SampleSpecularTransmission
 // purpose  : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI)
 //=======================================================================
-vec3 SampleSpecularTransmission (in vec3 theOutput,
-  out vec3 theInput, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)
+vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)
 {
-  vec3 aFactor = fresnelMedia (theOutput.z, theFresnel);
+  vec3 aFactor = fresnelMedia (theWo.z, theFresnel);
 
   float aReflection = convolve (aFactor, theWeight);
 
   // sample specular BRDF/BTDF
   if (RandFloat() <= aReflection)
   {
-    theInput = vec3 (-theOutput.x,
-                     -theOutput.y,
-                      theOutput.z);
+    theWi = vec3 (-theWo.x,
+                  -theWo.y,
+                   theWo.z);
 
     theWeight = aFactor * (1.f / aReflection);
   }
@@ -424,7 +454,7 @@ vec3 SampleSpecularTransmission (in vec3 theOutput,
   {
     theInside = !theInside;
 
-    transmitted (theFresnel.y, theOutput, theInput);
+    transmitted (theFresnel.y, theWo, theWi);
 
     theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection));
   }
@@ -438,10 +468,7 @@ vec3 SampleSpecularTransmission (in vec3 theOutput,
 // function : BsdfPdf
 // purpose  : Calculates BSDF of sampling input knowing output
 //=======================================================================
-float BsdfPdf (in SMaterial theBSDF,
-               in vec3      theOutput,
-               in vec3      theInput,
-               in vec3      theWeight)
+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);
@@ -452,15 +479,15 @@ float BsdfPdf (in SMaterial theBSDF,
 
   float aPDF = 0.f; // PDF of sampling input direction
 
-  if (theInput.z * theOutput.z > 0.f)
+  if (theWi.z * theWo.z > 0.f)
   {
-    vec3 aHalf = normalize (theInput + theOutput);
+    vec3 aH = normalize (theWi + theWo);
 
     // roughness value --> Blinn exponent
     float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);
 
-    aPDF = aPd * abs (theInput.z / M_PI) +
-      aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (aHalf.z, aPower + 1.f) / (4.f * dot (theInput, aHalf));
+    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));
   }
 
   return aPDF / aReflection;
@@ -473,11 +500,7 @@ float BsdfPdf (in SMaterial theBSDF,
 // function : SampleBsdf
 // purpose  : Samples specified composite material (BSDF)
 //=======================================================================
-float SampleBsdf (in SMaterial theBSDF,
-                  in vec3      theOutput,
-                  out vec3     theInput,
-                  inout vec3   theWeight,
-                  inout bool   theInside)
+float SampleBsdf (in SMaterial 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);
@@ -497,13 +520,13 @@ float SampleBsdf (in SMaterial theBSDF,
   {
     PICK_BXDF (aPd, theBSDF.Kd.rgb);
 
-    theWeight *= SampleLambertianReflection (theOutput, theInput, aPDF);
+    theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);
   }
   else if (aKsi < aPd + aPs) // glossy reflection
   {
     PICK_BXDF (aPs, theBSDF.Ks.rgb);
 
-    theWeight *= SampleBlinnReflection (theOutput, theInput, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);
+    theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);
   }
   else if (aKsi < aPd + aPs + aPr) // specular reflection
   {
@@ -511,7 +534,7 @@ float SampleBsdf (in SMaterial theBSDF,
 
     aPDF = MAXFLOAT;
 
-    theWeight *= SampleSpecularReflection (theOutput, theInput, theBSDF.Fresnel);
+    theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);
   }
   else if (aKsi < aReflection) // specular transmission
   {
@@ -519,7 +542,7 @@ float SampleBsdf (in SMaterial theBSDF,
 
     aPDF = MAXFLOAT;
 
-    theWeight *= SampleSpecularTransmission (theOutput, theInput, theWeight, theBSDF.Fresnel, theInside);
+    theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);
   }
 
   // path termination for extra small weights
@@ -810,7 +833,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse)
       // MIS weight including division by explicit PDF
       float aMIS = (aExpPDF == MAXFLOAT) ? 1.f : aExpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);
 
-      vec3 aContrib = aMIS * aParam.rgb /* Le */ * HandleMaterial (
+      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalMaterial (
           aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));
 
       if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important
index 187e127..59b0fb3 100644 (file)
@@ -247,12 +247,12 @@ static const char Shaders_PathtraceBase_fs[] =
   "//////////////////////////////////////////////////////////////////////////////////////////////\n"
   "\n"
   "//=======================================================================\n"
-  "// function : HandleLambertianReflection\n"
-  "// purpose  : Handles Lambertian BRDF, with cos(N, PSI)\n"
+  "// function : EvalLambertianReflection\n"
+  "// purpose  : Evaluates Lambertian BRDF, with cos(N, PSI)\n"
   "//=======================================================================\n"
-  "float HandleLambertianReflection (in vec3 theInput, in vec3 theOutput)\n"
+  "float EvalLambertianReflection (in vec3 theWi, in vec3 theWo)\n"
   "{\n"
-  "  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? 0.f : theInput.z * (1.f / M_PI);\n"
+  "  return (theWi.z <= 0.f || theWo.z <= 0.f) ? 0.f : theWi.z * (1.f / M_PI);\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -288,13 +288,13 @@ static const char Shaders_PathtraceBase_fs[] =
   "}\n"
   "\n"
   "//=======================================================================\n"
-  "// function : HandleBlinnReflection\n"
-  "// purpose  : Handles Blinn glossy BRDF, with cos(N, PSI)\n"
+  "// function : EvalBlinnReflection\n"
+  "// purpose  : Evaluates Blinn glossy BRDF, with cos(N, PSI)\n"
   "//=======================================================================\n"
-  "vec3 HandleBlinnReflection (in vec3 theInput, in vec3 theOutput, in vec3 theFresnel, in float theRoughness)\n"
+  "vec3 EvalBlinnReflection (in vec3 theWi, in vec3 theWo, in vec3 theFresnel, in float theRoughness)\n"
   "{\n"
   "  // calculate the reflection half-vec\n"
-  "  vec3 aH = normalize (theInput + theOutput);\n"
+  "  vec3 aH = normalize (theWi + theWo);\n"
   "\n"
   "  // roughness value -> Blinn exponent\n"
   "  float aPower = max (2.f / (theRoughness * theRoughness) - 2.f, 0.f);\n"
@@ -303,41 +303,55 @@ static const char Shaders_PathtraceBase_fs[] =
   "  float aD = (aPower + 2.f) * (1.f / M_2_PI) * pow (aH.z, aPower);\n"
   "\n"
   "  // calculate shadow-masking function\n"
-  "  float aG = SmithG1 (theOutput, aH, theRoughness) * SmithG1 (theInput, aH, theRoughness);\n"
+  "  float aG = SmithG1 (theWo, aH, theRoughness) *\n"
+  "             SmithG1 (theWi, aH, theRoughness);\n"
   "\n"
   "  // return total amount of reflection\n"
-  "  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? ZERO :\n"
-  "    aD * aG / (4.f * theOutput.z) * fresnelMedia (dot (theOutput, aH), theFresnel);\n"
+  "  return (theWi.z <= 0.f || theWo.z <= 0.f) ? ZERO :\n"
+  "    aD * aG / (4.f * theWo.z) * fresnelMedia (dot (theWo, aH), theFresnel);\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
-  "// function : HandleMaterial\n"
-  "// purpose  : Returns BSDF value for specified material, with cos(N, PSI)\n"
+  "// function : EvalMaterial\n"
+  "// purpose  : Evaluates BSDF for specified material, with cos(N, PSI)\n"
   "//=======================================================================\n"
-  "vec3 HandleMaterial (in SMaterial theBSDF, in vec3 theInput, in vec3 theOutput)\n"
+  "vec3 EvalMaterial (in SMaterial theBSDF, in vec3 theWi, in vec3 theWo)\n"
   "{\n"
-  "  return theBSDF.Kd.rgb * HandleLambertianReflection (theInput, theOutput) +\n"
-  "    theBSDF.Ks.rgb * HandleBlinnReflection (theInput, theOutput, theBSDF.Fresnel, theBSDF.Ks.w);\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"
   "}\n"
   "\n"
   "//=======================================================================\n"
   "// function : SampleLambertianReflection\n"
   "// purpose  : Samples Lambertian BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
   "//=======================================================================\n"
-  "vec3 SampleLambertianReflection (in vec3 theOutput, out vec3 theInput, inout float thePDF)\n"
+  "vec3 SampleLambertianReflection (in vec3 theWo, out vec3 theWi, inout float thePDF)\n"
   "{\n"
   "  float aKsi1 = RandFloat();\n"
   "  float aKsi2 = RandFloat();\n"
   "\n"
-  "  float aTemp = sqrt (aKsi2);\n"
+  "  theWi = vec3 (cos (M_2_PI * aKsi1),\n"
+  "                sin (M_2_PI * aKsi1),\n"
+  "                sqrt (1.f - aKsi2));\n"
+  "\n"
+  "  theWi.xy *= sqrt (aKsi2);\n"
   "\n"
-  "  theInput = vec3 (aTemp * cos (M_2_PI * aKsi1),\n"
-  "                   aTemp * sin (M_2_PI * aKsi1),\n"
-  "                   sqrt (1.f - aKsi2));\n"
+  "#ifdef TWO_SIDED_BXDF\n"
+  "  theWi.z *= sign (theWo.z);\n"
+  "#endif\n"
   "\n"
-  "  thePDF *= abs (theInput.z) * (1.f / M_PI);\n"
+  "  thePDF *= theWi.z * (1.f / M_PI);\n"
   "\n"
-  "  return (theInput.z <= 0.f || theOutput.z <= 0.f) ? ZERO : UNIT;\n"
+  "#ifdef TWO_SIDED_BXDF\n"
+  "  return UNIT;\n"
+  "#else\n"
+  "  return mix (UNIT, ZERO, theWo.z <= 0.f);\n"
+  "#endif\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
@@ -350,7 +364,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 theOutput, out vec3 theInput, in vec3 theFresnel, in float theRoughness, inout float thePDF)\n"
+  "vec3 SampleBlinnReflection (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"
@@ -370,56 +384,72 @@ static const char Shaders_PathtraceBase_fs[] =
   "  // calculate PDF of sampled direction\n"
   "  thePDF *= (aPower + 2.f) * (1.f / M_2_PI) * pow (aCosThetaM, aPower + 1.f);\n"
   "\n"
-  "  float aCosDelta = dot (theOutput, aM);\n"
+  "#ifdef TWO_SIDED_BXDF\n"
+  "  bool toFlip = theWo.z < 0.f;\n"
+  "\n"
+  "  if (toFlip)\n"
+  "    theWo.z = -theWo.z;\n"
+  "#endif\n"
+  "\n"
+  "  float aCosDelta = dot (theWo, aM);\n"
   "\n"
   "  // pick input based on half direction\n"
-  "  theInput = -theOutput + 2.f * aCosDelta * aM;\n"
+  "  theWi = -theWo + 2.f * aCosDelta * aM;\n"
   "\n"
-  "  if (theInput.z <= 0.f || theOutput.z <= 0.f)\n"
+  "  if (theWi.z <= 0.f || theWo.z <= 0.f)\n"
   "  {\n"
   "    return ZERO;\n"
   "  }\n"
   "\n"
   "  // Jacobian of half-direction mapping\n"
-  "  thePDF /= 4.f * dot (theInput, aM);\n"
+  "  thePDF /= 4.f * dot (theWi, aM);\n"
   "\n"
   "  // compute shadow-masking coefficient\n"
-  "  float aG = SmithG1 (theOutput, aM, theRoughness) * SmithG1 (theInput, aM, theRoughness);\n"
+  "  float aG = SmithG1 (theWo, aM, theRoughness) *\n"
+  "             SmithG1 (theWi, aM, theRoughness);\n"
+  "\n"
+  "#ifdef TWO_SIDED_BXDF\n"
+  "  if (toFlip)\n"
+  "    theWi.z = -theWi.z;\n"
+  "#endif\n"
   "\n"
-  "  return aG * aCosDelta / (theOutput.z * aM.z) * fresnelMedia (aCosDelta, theFresnel);\n"
+  "  return (aG * aCosDelta) / (theWo.z * aM.z) * fresnelMedia (aCosDelta, theFresnel);\n"
   "}\n"
   "\n"
   "//=======================================================================\n"
   "// function : SampleSpecularReflection\n"
   "// purpose  : Samples specular BRDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
   "//=======================================================================\n"
-  "vec3 SampleSpecularReflection (in vec3 theOutput, out vec3 theInput, in vec3 theFresnel)\n"
+  "vec3 SampleSpecularReflection (in vec3 theWo, out vec3 theWi, in vec3 theFresnel)\n"
   "{\n"
   "  // Sample input direction\n"
-  "  theInput = vec3 (-theOutput.x,\n"
-  "                   -theOutput.y,\n"
-  "                    theOutput.z);\n"
-  "\n"
-  "  return fresnelMedia (theOutput.z, theFresnel);\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"
   "\n"
   "//=======================================================================\n"
   "// function : SampleSpecularTransmission\n"
   "// purpose  : Samples specular BTDF, W = BRDF * cos(N, PSI) / PDF(PSI)\n"
   "//=======================================================================\n"
-  "vec3 SampleSpecularTransmission (in vec3 theOutput,\n"
-  "  out vec3 theInput, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)\n"
+  "vec3 SampleSpecularTransmission (in vec3 theWo, out vec3 theWi, in vec3 theWeight, in vec3 theFresnel, inout bool theInside)\n"
   "{\n"
-  "  vec3 aFactor = fresnelMedia (theOutput.z, theFresnel);\n"
+  "  vec3 aFactor = fresnelMedia (theWo.z, theFresnel);\n"
   "\n"
   "  float aReflection = convolve (aFactor, theWeight);\n"
   "\n"
   "  // sample specular BRDF/BTDF\n"
   "  if (RandFloat() <= aReflection)\n"
   "  {\n"
-  "    theInput = vec3 (-theOutput.x,\n"
-  "                     -theOutput.y,\n"
-  "                      theOutput.z);\n"
+  "    theWi = vec3 (-theWo.x,\n"
+  "                  -theWo.y,\n"
+  "                   theWo.z);\n"
   "\n"
   "    theWeight = aFactor * (1.f / aReflection);\n"
   "  }\n"
@@ -427,7 +457,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "  {\n"
   "    theInside = !theInside;\n"
   "\n"
-  "    transmitted (theFresnel.y, theOutput, theInput);\n"
+  "    transmitted (theFresnel.y, theWo, theWi);\n"
   "\n"
   "    theWeight = (UNIT - aFactor) * (1.f / (1.f - aReflection));\n"
   "  }\n"
@@ -441,10 +471,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "// function : BsdfPdf\n"
   "// purpose  : Calculates BSDF of sampling input knowing output\n"
   "//=======================================================================\n"
-  "float BsdfPdf (in SMaterial theBSDF,\n"
-  "               in vec3      theOutput,\n"
-  "               in vec3      theInput,\n"
-  "               in vec3      theWeight)\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"
@@ -455,15 +482,15 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "  float aPDF = 0.f; // PDF of sampling input direction\n"
   "\n"
-  "  if (theInput.z * theOutput.z > 0.f)\n"
+  "  if (theWi.z * theWo.z > 0.f)\n"
   "  {\n"
-  "    vec3 aHalf = normalize (theInput + theOutput);\n"
+  "    vec3 aH = normalize (theWi + theWo);\n"
   "\n"
   "    // roughness value --> Blinn exponent\n"
   "    float aPower = max (2.f / (theBSDF.Ks.w * theBSDF.Ks.w) - 2.f, 0.f);\n"
   "\n"
-  "    aPDF = aPd * abs (theInput.z / M_PI) +\n"
-  "      aPs * (aPower + 2.f) * (1.f / M_2_PI) * pow (aHalf.z, aPower + 1.f) / (4.f * dot (theInput, aHalf));\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"
   "  }\n"
   "\n"
   "  return aPDF / aReflection;\n"
@@ -476,11 +503,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "// function : SampleBsdf\n"
   "// purpose  : Samples specified composite material (BSDF)\n"
   "//=======================================================================\n"
-  "float SampleBsdf (in SMaterial theBSDF,\n"
-  "                  in vec3      theOutput,\n"
-  "                  out vec3     theInput,\n"
-  "                  inout vec3   theWeight,\n"
-  "                  inout bool   theInside)\n"
+  "float SampleBsdf (in SMaterial 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"
@@ -500,13 +523,13 @@ static const char Shaders_PathtraceBase_fs[] =
   "  {\n"
   "    PICK_BXDF (aPd, theBSDF.Kd.rgb);\n"
   "\n"
-  "    theWeight *= SampleLambertianReflection (theOutput, theInput, aPDF);\n"
+  "    theWeight *= SampleLambertianReflection (theWo, theWi, aPDF);\n"
   "  }\n"
   "  else if (aKsi < aPd + aPs) // glossy reflection\n"
   "  {\n"
   "    PICK_BXDF (aPs, theBSDF.Ks.rgb);\n"
   "\n"
-  "    theWeight *= SampleBlinnReflection (theOutput, theInput, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);\n"
+  "    theWeight *= SampleBlinnReflection (theWo, theWi, theBSDF.Fresnel, theBSDF.Ks.w, aPDF);\n"
   "  }\n"
   "  else if (aKsi < aPd + aPs + aPr) // specular reflection\n"
   "  {\n"
@@ -514,7 +537,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "    aPDF = MAXFLOAT;\n"
   "\n"
-  "    theWeight *= SampleSpecularReflection (theOutput, theInput, theBSDF.Fresnel);\n"
+  "    theWeight *= SampleSpecularReflection (theWo, theWi, theBSDF.Fresnel);\n"
   "  }\n"
   "  else if (aKsi < aReflection) // specular transmission\n"
   "  {\n"
@@ -522,7 +545,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "    aPDF = MAXFLOAT;\n"
   "\n"
-  "    theWeight *= SampleSpecularTransmission (theOutput, theInput, theWeight, theBSDF.Fresnel, theInside);\n"
+  "    theWeight *= SampleSpecularTransmission (theWo, theWi, theWeight, theBSDF.Fresnel, theInside);\n"
   "  }\n"
   "\n"
   "  // path termination for extra small weights\n"
@@ -813,7 +836,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "      // 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 */ * HandleMaterial (\n"
+  "      vec3 aContrib = aMIS * aParam.rgb /* Le */ * EvalMaterial (\n"
   "          aMaterial, toLocalSpace (aLight.xyz, aSpace), toLocalSpace (-theRay.Direct, aSpace));\n"
   "\n"
   "      if (any (greaterThan (aContrib, MIN_CONTRIBUTION))) // check if light source is important\n"
index a50ffc7..4f99edc 100644 (file)
@@ -9077,16 +9077,17 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       case Graphic3d_RM_RAYTRACING:    theDI << "raytrace ";      break;
     }
     theDI << "\n";
-    theDI << "msaa:         " <<  aParams.NbMsaaSamples                               << "\n";
-    theDI << "rayDepth:     " <<  aParams.RaytracingDepth                             << "\n";
-    theDI << "fsaa:         " << (aParams.IsAntialiasingEnabled       ? "on" : "off") << "\n";
-    theDI << "shadows:      " << (aParams.IsShadowEnabled             ? "on" : "off") << "\n";
-    theDI << "reflections:  " << (aParams.IsReflectionEnabled         ? "on" : "off") << "\n";
-    theDI << "gleam:        " << (aParams.IsTransparentShadowEnabled  ? "on" : "off") << "\n";
-    theDI << "GI:           " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n";
-    theDI << "blocked RNG:  " << (aParams.CoherentPathTracingMode     ? "on" : "off") << "\n";
-    theDI << "iss:          " << (aParams.AdaptiveScreenSampling      ? "on" : "off") << "\n";
-    theDI << "iss debug:    " << (aParams.ShowSamplingTiles           ? "on" : "off") << "\n";
+    theDI << "msaa:           " <<  aParams.NbMsaaSamples                               << "\n";
+    theDI << "rayDepth:       " <<  aParams.RaytracingDepth                             << "\n";
+    theDI << "fsaa:           " << (aParams.IsAntialiasingEnabled       ? "on" : "off") << "\n";
+    theDI << "shadows:        " << (aParams.IsShadowEnabled             ? "on" : "off") << "\n";
+    theDI << "reflections:    " << (aParams.IsReflectionEnabled         ? "on" : "off") << "\n";
+    theDI << "gleam:          " << (aParams.IsTransparentShadowEnabled  ? "on" : "off") << "\n";
+    theDI << "GI:             " << (aParams.IsGlobalIlluminationEnabled ? "on" : "off") << "\n";
+    theDI << "blocked RNG:    " << (aParams.CoherentPathTracingMode     ? "on" : "off") << "\n";
+    theDI << "iss:            " << (aParams.AdaptiveScreenSampling      ? "on" : "off") << "\n";
+    theDI << "iss debug:      " << (aParams.ShowSamplingTiles           ? "on" : "off") << "\n";
+    theDI << "two-sided BSDF: " << (aParams.TwoSidedBsdfModels          ? "on" : "off") << "\n";
     theDI << "shadingModel: ";
     switch (aView->ShadingModel())
     {
@@ -9360,6 +9361,22 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aParams.UseEnvironmentMapBackground = toEnable;
     }
+    else if (aFlag == "-twoside")
+    {
+      if (toPrint)
+      {
+        theDI << (aParams.TwoSidedBsdfModels ? "on" : "off") << " ";
+        continue;
+      }
+
+      Standard_Boolean toEnable = Standard_True;
+      if (++anArgIter < theArgNb
+        && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+      {
+        --anArgIter;
+      }
+      aParams.TwoSidedBsdfModels = toEnable;
+    }
     else if (aFlag == "-shademodel"
           || aFlag == "-shadingmodel"
           || aFlag == "-shading")
@@ -10633,6 +10650,7 @@ void ViewerTest::ViewerCommands(Draw_Interpretor& theCommands)
     "\n      '-gi           on|off'  Enables/disables global illumination effects"
     "\n      '-brng         on|off'  Enables/disables blocked RNG (fast coherent PT)"
     "\n      '-env          on|off'  Enables/disables environment map background"
+    "\n      '-twoside      on|off'  Enables/disables two-sided BSDF models (PT mode)"
     "\n      '-iss          on|off'  Enables/disables adaptive screen sampling (PT mode)"
     "\n      '-issd         on|off'  Shows screen sampling distribution in ISS mode"
     "\n      '-rebuildGlsl  on|off'  Rebuild Ray-Tracing GLSL programs (for debugging)"
diff --git a/tests/v3d/raytrace/sample_cube_twosided b/tests/v3d/raytrace/sample_cube_twosided
new file mode 100644 (file)
index 0000000..ac872ad
--- /dev/null
@@ -0,0 +1,19 @@
+puts "============"
+puts "Visualization - Path Tracing, Cube sample"
+puts "============"
+puts ""
+
+source $env(CSF_OCCTSamplesPath)/tcl/pathtrace_cube.tcl
+
+vaxo
+vfit
+vfps 100
+
+# Dump image produced with one-sided BSDFs
+vdump $imagedir/${casename}_onesided.png
+
+vrenderparams -twoside
+vfps 100
+
+# Dump image produced with two-sided BSDFs
+vdump $imagedir/${casename}_twosided.png