0031275: Visualization, TKOpenGl - handle normal-map texture with Path-Tracing CR0_TS_740 IR-2020-01-24
authoriko <iko@opencascade.com>
Tue, 21 Jan 2020 13:01:03 +0000 (16:01 +0300)
committerbugmaster <bugmaster@opencascade.com>
Fri, 24 Jan 2020 13:35:41 +0000 (16:35 +0300)
Base normal map support has been implemented in path tracing (just geometry normal replacement).
Smooth normal adaptation has been implemeneted in order to avoid black areas artefacts during path tracing.
Tangent normal space calcuation has been moved to separate unified function.
Tangent space orthogonalization algorithm has been changed in order to handle all orientations and combinations of UV coordinates.

14 files changed:
src/Graphic3d/Graphic3d_RenderingParams.hxx
src/OpenGl/OpenGl_ShaderManager.cxx
src/OpenGl/OpenGl_View.hxx
src/OpenGl/OpenGl_View_Raytrace.cxx
src/Shaders/FILES
src/Shaders/PathtraceBase.fs
src/Shaders/PointLightAttenuation.glsl
src/Shaders/RaytraceBase.fs
src/Shaders/Shaders_PathtraceBase_fs.pxx
src/Shaders/Shaders_RaytraceBase_fs.pxx
src/Shaders/Shaders_TangentSpaceNormal_glsl.pxx [new file with mode: 0644]
src/Shaders/TangentSpaceNormal.glsl [new file with mode: 0644]
src/ViewerTest/ViewerTest_ViewerCommands.cxx
tests/v3d/raytrace/normal_map [new file with mode: 0644]

index a437d3a..fa18c5b 100644 (file)
@@ -115,6 +115,7 @@ public:
     IsAntialiasingEnabled       (Standard_False),
     IsTransparentShadowEnabled  (Standard_False),
     UseEnvironmentMapBackground (Standard_False),
+    ToIgnoreNormalMapInRayTracing (Standard_False),
     CoherentPathTracingMode     (Standard_False),
     AdaptiveScreenSampling      (Standard_False),
     AdaptiveScreenSamplingAtomic(Standard_False),
@@ -202,6 +203,7 @@ public:
   Standard_Boolean                  IsAntialiasingEnabled;       //!< enables/disables adaptive anti-aliasing, False by default
   Standard_Boolean                  IsTransparentShadowEnabled;  //!< enables/disables light propagation through transparent media, False by default
   Standard_Boolean                  UseEnvironmentMapBackground; //!< enables/disables environment map background
+  Standard_Boolean                  ToIgnoreNormalMapInRayTracing; //!< enables/disables normal map ignoring during path tracing; FALSE by default
   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                  AdaptiveScreenSamplingAtomic;//!< enables/disables usage of atomic float operations within adaptive screen sampling, FALSE by default
index e5c143a..4fda63f 100644 (file)
@@ -35,6 +35,7 @@
 #include "../Shaders/Shaders_PBREnvBaking_fs.pxx"
 #include "../Shaders/Shaders_PBREnvBaking_vs.pxx"
 #include "../Shaders/Shaders_PointLightAttenuation_glsl.pxx"
+#include "../Shaders/Shaders_TangentSpaceNormal_glsl.pxx"
 
 IMPLEMENT_STANDARD_RTTIEXT(OpenGl_ShaderManager,Standard_Transient)
 
@@ -2719,6 +2720,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
      && (theBits & OpenGl_PO_HasTextures) == OpenGl_PO_TextureNormal
      && myContext->hasFlatShading != OpenGl_FeatureNotAvailable)
     {
+      aSrcFrag += Shaders_TangentSpaceNormal_glsl;
       // apply normal map texture
       aSrcFragExtraMain +=
         EOL"#if defined(THE_HAS_TEXTURE_NORMAL)"
@@ -2726,15 +2728,9 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
         EOL"  vec4 aMapNormalValue = occTextureNormal(aTexCoord);"
         EOL"  if (aMapNormalValue.w > 0.5)"
         EOL"  {"
-        EOL"    aMapNormalValue.xyz = normalize (aMapNormalValue.xyz * 2.0 - vec3(1.0));"
         EOL"    mat2 aDeltaUVMatrix = mat2 (dFdx(aTexCoord), dFdy(aTexCoord));"
-        EOL"    aDeltaUVMatrix = mat2 (aDeltaUVMatrix[1][1], -aDeltaUVMatrix[0][1], -aDeltaUVMatrix[1][0], aDeltaUVMatrix[0][0]);"
         EOL"    mat2x3 aDeltaVectorMatrix = mat2x3 (dFdx (PositionWorld.xyz), dFdy (PositionWorld.xyz));"
-        EOL"    aDeltaVectorMatrix = aDeltaVectorMatrix * aDeltaUVMatrix;"
-        EOL"    aDeltaVectorMatrix[0] = normalize(aDeltaVectorMatrix[0] - dot(Normal, aDeltaVectorMatrix[0]) * Normal);"
-        EOL"    aDeltaVectorMatrix[1] = normalize(aDeltaVectorMatrix[1] - dot(Normal, aDeltaVectorMatrix[1]) * Normal);"
-        EOL"    float aDirection = gl_FrontFacing ? 1.0 : -1.0;"
-        EOL"    Normal = mat3 (aDirection * aDeltaVectorMatrix[0], aDirection * aDeltaVectorMatrix[1], Normal) * aMapNormalValue.xyz;"
+        EOL"    Normal = TangentSpaceNormal (aDeltaUVMatrix, aDeltaVectorMatrix, aMapNormalValue.xyz, Normal, !gl_FrontFacing);"
         EOL"  }"
         EOL"#endif";
     }
@@ -2777,7 +2773,7 @@ Standard_Boolean OpenGl_ShaderManager::prepareStdProgramPhong (Handle(OpenGl_Sha
   const TCollection_AsciiString aLights = stdComputeLighting (aNbLights, !aSrcFragGetVertColor.IsEmpty(), theIsPBR,
                                                               (theBits & OpenGl_PO_TextureRGB) == 0
                                                            || (theBits & OpenGl_PO_IsPoint) != 0);
-  aSrcFrag = TCollection_AsciiString()
+  aSrcFrag += TCollection_AsciiString()
     + EOL
     + aSrcFragGetVertColor
     + EOL"vec3  Normal;"
index 8f39d03..346c753 100644 (file)
@@ -756,6 +756,9 @@ protected: //! @name data types related to ray-tracing
     //! Enables/disables environment map for background.
     Standard_Boolean UseEnvMapForBackground;
 
+    //! Enables/disables normal map ignoring during path tracing.
+    Standard_Boolean ToIgnoreNormalMap;
+
     //! Maximum radiance value used for clamping radiance estimation.
     Standard_ShortReal RadianceClampingValue;
     
@@ -779,6 +782,7 @@ protected: //! @name data types related to ray-tracing
       AdaptiveScreenSampling (Standard_False),
       AdaptiveScreenSamplingAtomic (Standard_False),
       UseEnvMapForBackground (Standard_False),
+      ToIgnoreNormalMap      (Standard_False),
       RadianceClampingValue  (30.0),
       DepthOfField           (Standard_False),
       CubemapForBack         (Standard_False),
index 90377c6..d39623b 100644 (file)
@@ -28,6 +28,7 @@
 #include "../Shaders/Shaders_RaytraceRender_fs.pxx"
 #include "../Shaders/Shaders_RaytraceSmooth_fs.pxx"
 #include "../Shaders/Shaders_Display_fs.pxx"
+#include "../Shaders/Shaders_TangentSpaceNormal_glsl.pxx"
 
 //! Use this macro to output ray-tracing debug info
 // #define RAY_TRACE_PRINT_INFO
@@ -411,6 +412,7 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_Aspects* theA
 
   aResMat.BSDF.FresnelCoat = aBSDF.FresnelCoat.Serialize ();
   aResMat.BSDF.FresnelBase = aBSDF.FresnelBase.Serialize ();
+  aResMat.BSDF.FresnelBase.w() = -1.0; // no normal map texture
 
   // Handle material textures
   if (!theAspect->Aspect()->ToMapTexture())
@@ -447,6 +449,11 @@ OpenGl_RaytraceMaterial OpenGl_View::convertMaterial (const OpenGl_Aspects* theA
         buildTextureTransform (aTexture->Sampler()->Parameters(), aResMat.TextureTransform);
         aResMat.BSDF.Le.w() = static_cast<Standard_ShortReal> (myRaytraceGeometry.AddTexture (aTexture));
       }
+      else if (aTexIter.Unit() == Graphic3d_TextureUnit_Normal)
+      {
+        buildTextureTransform (aTexture->Sampler()->Parameters(), aResMat.TextureTransform);
+        aResMat.BSDF.FresnelBase.w() = static_cast<Standard_ShortReal> (myRaytraceGeometry.AddTexture (aTexture));
+      }
     }
   }
   else if (!myIsRaytraceWarnTextures)
@@ -1160,6 +1167,11 @@ TCollection_AsciiString OpenGl_View::generateShaderPrefix (const Handle(OpenGl_C
     }
   }
 
+  if (myRaytraceParameters.ToIgnoreNormalMap)
+  {
+    aPrefixString += TCollection_AsciiString("\n#define IGNORE_NORMAL_MAP");
+  }
+
   if (myRaytraceParameters.CubemapForBack)
   {
     aPrefixString += TCollection_AsciiString("\n#define BACKGROUND_CUBEMAP");
@@ -1335,13 +1347,15 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
      || myRenderParams.IsTransparentShadowEnabled  != myRaytraceParameters.TransparentShadows
      || myRenderParams.IsGlobalIlluminationEnabled != myRaytraceParameters.GlobalIllumination
      || myRenderParams.TwoSidedBsdfModels          != myRaytraceParameters.TwoSidedBsdfModels
-     || myRaytraceGeometry.HasTextures()           != myRaytraceParameters.UseBindlessTextures)
+     || myRaytraceGeometry.HasTextures()           != myRaytraceParameters.UseBindlessTextures
+     || myRenderParams.ToIgnoreNormalMapInRayTracing != myRaytraceParameters.ToIgnoreNormalMap)
     {
       myRaytraceParameters.NbBounces           = myRenderParams.RaytracingDepth;
       myRaytraceParameters.TransparentShadows  = myRenderParams.IsTransparentShadowEnabled;
       myRaytraceParameters.GlobalIllumination  = myRenderParams.IsGlobalIlluminationEnabled;
       myRaytraceParameters.TwoSidedBsdfModels  = myRenderParams.TwoSidedBsdfModels;
       myRaytraceParameters.UseBindlessTextures = myRaytraceGeometry.HasTextures();
+      myRaytraceParameters.ToIgnoreNormalMap     = myRenderParams.ToIgnoreNormalMapInRayTracing;
       aToRebuildShaders = Standard_True;
     }
 
@@ -1484,6 +1498,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
       if (!aShaderFolder.IsEmpty())
       {
         const TCollection_AsciiString aFiles[] = { aShaderFolder + "/RaytraceBase.fs",
+                                                   aShaderFolder + "/TangentSpaceNormal.glsl",
                                                    aShaderFolder + "/PathtraceBase.fs",
                                                    aShaderFolder + "/RaytraceRender.fs",
                                                    "" };
@@ -1495,6 +1510,7 @@ Standard_Boolean OpenGl_View::initRaytraceResources (const Standard_Integer theS
       else
       {
         const TCollection_AsciiString aSrcShaders[] = { Shaders_RaytraceBase_fs,
+                                                        Shaders_TangentSpaceNormal_glsl,
                                                         Shaders_PathtraceBase_fs,
                                                         Shaders_RaytraceRender_fs,
                                                         "" };
index 06d166b..7c7ada0 100644 (file)
@@ -16,6 +16,7 @@ srcinc:::RaytraceRender.fs
 srcinc:::PathtraceBase.fs
 srcinc:::RaytraceBase.vs
 srcinc:::RaytraceSmooth.fs
+srcinc:::TangentSpaceNormal.glsl
 Shaders_Declarations_glsl.pxx
 Shaders_DeclarationsImpl_glsl.pxx
 Shaders_Display_fs.pxx
@@ -32,3 +33,4 @@ Shaders_RaytraceRender_fs.pxx
 Shaders_PathtraceBase_fs.pxx
 Shaders_RaytraceBase_vs.pxx
 Shaders_RaytraceSmooth_fs.pxx
+Shaders_TangentSpaceNormal_glsl.pxx
index 0d8c908..e7a324f 100644 (file)
@@ -46,8 +46,8 @@ struct SBSDF
   //! Fresnel coefficients of coat layer.
   vec3 FresnelCoat;
 
-  //! Fresnel coefficients of base layer.
-  vec3 FresnelBase;
+  //! Fresnel coefficients of base layer + normal map texture index in W.
+  vec4 FresnelBase;
 };
 
 ///////////////////////////////////////////////////////////////////////////////////////
@@ -322,7 +322,7 @@ vec3 EvalBsdfLayered (in SBSDF theBSDF, in vec3 theWi, in vec3 theWo)
 
   if (theBSDF.Ks.w > FLT_EPSILON)
   {
-    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w);
+    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase.rgb, theBSDF.Ks.w);
   }
 
   aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat);
@@ -546,7 +546,7 @@ float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout
 
       if (theBSDF.Ks.w < FLT_EPSILON)
       {
-        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);
+        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase.rgb);
 
         theWi = vec3 (-theWo.x,
                       -theWo.y,
@@ -554,7 +554,7 @@ float SampleBsdfLayered (in SBSDF theBSDF, in vec3 theWo, out vec3 theWi, inout
       }
       else
       {
-        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);
+        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase.rgb, theBSDF.Ks.w, aPDF);
       }
 
       aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);
@@ -759,6 +759,23 @@ bool IsNotZero (in SBSDF theBSDF, in vec3 theThroughput)
   return convolve (theBSDF.Kd.rgb + aGlossy, theThroughput) > FLT_EPSILON;
 }
 
+//=======================================================================
+// function : NormalAdaptation
+// purpose  : Adapt smooth normal (which may be different from geometry normal) in order to avoid black areas in render
+//=======================================================================
+bool NormalAdaptation (in vec3 theView, in vec3 theGeometryNormal, inout vec3 theSmoothNormal)
+{
+  float aMinCos = dot(theView, theGeometryNormal);
+  aMinCos = 0.5 * (sqrt(1.0 - aMinCos) + sqrt(1.0 + aMinCos));
+  float aCos = dot(theGeometryNormal, theSmoothNormal);
+  if (aCos < aMinCos)
+  {
+    theSmoothNormal = aMinCos * theGeometryNormal + normalize(theSmoothNormal - aCos * theGeometryNormal) * sqrt(1.0 - aMinCos * aMinCos);
+    return true;
+  }
+  return false;
+}
+
 //=======================================================================
 // function : PathTrace
 // purpose  : Calculates radiance along the given ray
@@ -780,12 +797,12 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
   {
     SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);
 
-    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTransfID);
+    STriangle aTriangle = SceneNearestHit (theRay, theInverse, aHit, aTransfID);
 
     // check implicit path
     vec3 aLe = IntersectLight (theRay, aDepth, aHit.Time, aExpPDF);
 
-    if (any (greaterThan (aLe, ZERO)) || aTriIndex.x == -1)
+    if (any (greaterThan (aLe, ZERO)) || aTriangle.TriIndex.x == -1)
     {
       float aMIS = (aDepth == 0 || aImpPDF == MAXFLOAT) ? 1.f :
         aImpPDF * aImpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);
@@ -816,31 +833,30 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
     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));
+    aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriangle.TriIndex.w));
+    aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriangle.TriIndex.w));
+    aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriangle.TriIndex.w));
+    aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriangle.TriIndex.w));
 
     // 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;
+    aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriangle.TriIndex.w)).xyz;
+    aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriangle.TriIndex.w));
 
-    vec4 anLE = texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w));
+    vec4 anLE = texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriangle.TriIndex.w));
 
     // compute smooth normal (in parallel with fetch)
-    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);
+    vec3 aNormal = SmoothNormal (aHit.UV, aTriangle.TriIndex);
     aNormal = normalize (vec3 (dot (aInvTransf0, aNormal),
                                dot (aInvTransf1, aNormal),
                                dot (aInvTransf2, aNormal)));
 
-    SLocalSpace aSpace = buildLocalSpace (aNormal);
-
 #ifdef USE_TEXTURES
-    if (aBSDF.Kd.w >= 0.0 || aBSDF.Kt.w >= 0.0 || anLE.w >= 0.0)
+    if (aBSDF.Kd.w >= 0.0 || aBSDF.Kt.w >= 0.0 || aBSDF.FresnelBase.w >=0.0 || anLE.w >= 0.0)
     {
-      vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);
-      vec4 aTrsfRow1 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriIndex.w));
-      vec4 aTrsfRow2 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriIndex.w));
+      vec2 aUVs[3];
+      vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriangle.TriIndex, aUVs), 0.f, 1.f);
+      vec4 aTrsfRow1 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriangle.TriIndex.w));
+      vec4 aTrsfRow2 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriangle.TriIndex.w));
       aTexCoord.st = vec2 (dot (aTrsfRow1, aTexCoord),
                            dot (aTrsfRow2, aTexCoord));
 
@@ -855,15 +871,15 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
         float aPbrRough2 = aTexMetRough.y * aTexMetRough.y;
         aBSDF.Ks.a *= aPbrRough2;
         // when using metal-roughness texture, global metalness of material (encoded in FresnelBase) is expected to be 1.0 so that Kd will be 0.0
-        aBSDF.Kd.rgb = aBSDF.FresnelBase * (1.0 - aPbrMetal);
-        aBSDF.FresnelBase *= aPbrMetal;
+        aBSDF.Kd.rgb = aBSDF.FresnelBase.rgb * (1.0 - aPbrMetal);
+        aBSDF.FresnelBase.rgb *= aPbrMetal;
       }
       if (aBSDF.Kd.w >= 0.0)
       {
         vec4 aTexColor = textureLod (sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.0);
         vec3 aDiff = aTexColor.rgb * aTexColor.a;
         aBSDF.Kd.rgb *= aDiff;
-        aBSDF.FresnelBase *= aDiff;
+        aBSDF.FresnelBase.rgb *= aDiff;
         if (aTexColor.a != 1.0)
         {
           // mix transparency BTDF with texture alpha-channel
@@ -871,8 +887,25 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
           aBSDF.Kt.rgb = (UNIT - aTexColor.aaa) + aTexColor.a * aBSDF.Kt.rgb;
         }
       }
+      #ifndef IGNORE_NORMAL_MAP
+      if (aBSDF.FresnelBase.w >= 0.0)
+      {
+        for (int i = 0 ; i < 3; ++i)
+        {
+          aUVs[i] = vec2 (dot (aTrsfRow1, vec4(aUVs[i], 0.0, 1.0)),
+                          dot (aTrsfRow2, vec4(aUVs[i], 0.0, 1.0)));
+        }
+        vec3 aMapNormalValue = textureLod (sampler2D (uTextureSamplers[int (aBSDF.FresnelBase.w)]), aTexCoord.st, 0.0).xyz;
+        mat2 aDeltaUVMatrix = mat2 (aUVs[1] - aUVs[0], aUVs[1] - aUVs[2]);
+        mat2x3 aDeltaVectorMatrix = mat2x3 (aTriangle.Points[1] - aTriangle.Points[0], aTriangle.Points[1] - aTriangle.Points[2]);
+        aNormal = TangentSpaceNormal (aDeltaUVMatrix, aDeltaVectorMatrix, aMapNormalValue, aNormal, true);
+      }
+      #endif
     }
 #endif
+    NormalAdaptation (-theRay.Direct, aHit.Normal, aNormal);
+    aHit.Normal = aNormal;
+    SLocalSpace aSpace = buildLocalSpace (aNormal);
 
     if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))
     {
@@ -921,7 +954,7 @@ vec4 PathTrace (in SRay theRay, in vec3 theInverse, in int theNbSamples)
 
     if (aInMedium) // handle attenuation
     {
-      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w));
+      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriangle.TriIndex.w));
 
       aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb));
     }
index cb84039..3334e95 100644 (file)
@@ -32,4 +32,4 @@ float occPointLightAttenuation (in float theDistance, in float theRange, in floa
     return 1.0 / (theConstAttenuation + theLinearAttenuation * theDistance);
   }
   return occRangedPointLightAttenuation (theDistance, theRange);
-}
\ No newline at end of file
+}
index 5c73a67..0c7ead8 100644 (file)
@@ -170,6 +170,14 @@ struct SIntersect
   vec3 Normal;
 };
 
+//! Stores triangle's vertex indexes and vertexes itself
+struct STriangle
+{
+  ivec4 TriIndex;
+
+  vec3 Points[3];
+};
+
 /////////////////////////////////////////////////////////////////////////////////////////
 // Some useful constants
 
@@ -462,7 +470,7 @@ struct SSubTree
 #define TRG_OFFSET(treelet) treelet.SubData.w
 
 //! Identifies the absence of intersection.
-#define INALID_HIT ivec4 (-1)
+#define INVALID_HIT ivec4 (-1)
 
 //! Global stack shared between traversal functions.
 int Stack[STACK_SIZE];
@@ -498,9 +506,9 @@ int pop (inout int theHead)
 // function : SceneNearestHit
 // purpose  : Finds intersection with nearest scene triangle
 // =======================================================================
-ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)
+STriangle SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)
 {
-  ivec4 aTriIndex = INALID_HIT;
+  STriangle aTriangle = STriangle (INVALID_HIT, vec3[](vec3(0.0), vec3(0.0), vec3(0.0)));
 
   int aNode =  0; // node to traverse
   int aHead = -1; // pointer of stack
@@ -607,17 +615,22 @@ ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theH
 
       for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)
       {
-        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));
+        ivec4 aTriIndex = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));
+        vec3 aPoints[3];
 
-        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += VRT_OFFSET (aSubTree)).xyz;
-        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += VRT_OFFSET (aSubTree)).xyz;
-        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += VRT_OFFSET (aSubTree)).xyz;
+        aPoints[0] = texelFetch (uGeometryVertexTexture, aTriIndex.x += VRT_OFFSET (aSubTree)).xyz;
+        aPoints[1] = texelFetch (uGeometryVertexTexture, aTriIndex.y += VRT_OFFSET (aSubTree)).xyz;
+        aPoints[2] = texelFetch (uGeometryVertexTexture, aTriIndex.z += VRT_OFFSET (aSubTree)).xyz;
 
-        IntersectTriangle (aSubTree.TrsfRay, aPoint0, aPoint1, aPoint2, aTimeUV, aNormal);
+        IntersectTriangle (aSubTree.TrsfRay, aPoints[0], aPoints[1], aPoints[2], aTimeUV, aNormal);
 
         if (aTimeUV.x < theHit.Time)
         {
-          aTriIndex = aTriangle;
+          aTriangle.TriIndex = aTriIndex;
+          for (int i = 0; i < 3; ++i)
+          {
+            aTriangle.Points[i] = aPoints[i];
+          }
 
           theTrsfId = TRS_OFFSET (aSubTree);
 
@@ -664,7 +677,7 @@ ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theH
     }
   }
 
-  return aTriIndex;
+  return aTriangle;
 }
 
 // =======================================================================
@@ -914,15 +927,21 @@ float PolygonOffset (in vec3 theNormal, in vec3 thePoint)
 // purpose  : Interpolates UV coordinates across the triangle
 // =======================================================================
 #ifdef USE_TEXTURES
-vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle)
+vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle, out vec2[3] theUVs)
 {
-  vec2 aTexCrd0 = texelFetch (uGeometryTexCrdTexture, theTriangle.x).st;
-  vec2 aTexCrd1 = texelFetch (uGeometryTexCrdTexture, theTriangle.y).st;
-  vec2 aTexCrd2 = texelFetch (uGeometryTexCrdTexture, theTriangle.z).st;
+  theUVs[0] = texelFetch (uGeometryTexCrdTexture, theTriangle.x).st;
+  theUVs[1] = texelFetch (uGeometryTexCrdTexture, theTriangle.y).st;
+  theUVs[2] = texelFetch (uGeometryTexCrdTexture, theTriangle.z).st;
 
-  return aTexCrd1 * theUV.x +
-         aTexCrd2 * theUV.y +
-         aTexCrd0 * (1.0f - theUV.x - theUV.y);
+  return theUVs[1] * theUV.x +
+         theUVs[2] * theUV.y +
+         theUVs[0] * (1.0f - theUV.x - theUV.y);
+}
+
+vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle)
+{
+  vec2 aUVs[3];
+  return SmoothUV (theUV, theTriangle, aUVs);
 }
 #endif
 
@@ -1009,7 +1028,7 @@ vec4 Radiance (in SRay theRay, in vec3 theInverse)
   {
     SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);
 
-    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId);
+    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId).TriIndex;
 
     if (aTriIndex.x == -1)
     {
index 3db4426..fb32f24 100644 (file)
@@ -49,8 +49,8 @@ static const char Shaders_PathtraceBase_fs[] =
   "  //! Fresnel coefficients of coat layer.\n"
   "  vec3 FresnelCoat;\n"
   "\n"
-  "  //! Fresnel coefficients of base layer.\n"
-  "  vec3 FresnelBase;\n"
+  "  //! Fresnel coefficients of base layer + normal map texture index in W.\n"
+  "  vec4 FresnelBase;\n"
   "};\n"
   "\n"
   "///////////////////////////////////////////////////////////////////////////////////////\n"
@@ -325,7 +325,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "  if (theBSDF.Ks.w > FLT_EPSILON)\n"
   "  {\n"
-  "    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase, theBSDF.Ks.w);\n"
+  "    aBxDF += theBSDF.Ks.rgb * EvalBlinnReflection (theWi, theWo, theBSDF.FresnelBase.rgb, theBSDF.Ks.w);\n"
   "  }\n"
   "\n"
   "  aBxDF *= UNIT - fresnelMedia (theWo.z, theBSDF.FresnelCoat);\n"
@@ -549,7 +549,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "      if (theBSDF.Ks.w < FLT_EPSILON)\n"
   "      {\n"
-  "        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase);\n"
+  "        theWeight *= fresnelMedia (theWo.z, theBSDF.FresnelBase.rgb);\n"
   "\n"
   "        theWi = vec3 (-theWo.x,\n"
   "                      -theWo.y,\n"
@@ -557,7 +557,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "      }\n"
   "      else\n"
   "      {\n"
-  "        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase, theBSDF.Ks.w, aPDF);\n"
+  "        theWeight *= SampleGlossyBlinnReflection (theWo, theWi, theBSDF.FresnelBase.rgb, theBSDF.Ks.w, aPDF);\n"
   "      }\n"
   "\n"
   "      aPDF = mix (aPDF, MAXFLOAT, theBSDF.Ks.w < FLT_EPSILON);\n"
@@ -763,6 +763,23 @@ static const char Shaders_PathtraceBase_fs[] =
   "}\n"
   "\n"
   "//=======================================================================\n"
+  "// function : NormalAdaptation\n"
+  "// purpose  : Adapt smooth normal (which may be different from geometry normal) in order to avoid black areas in render\n"
+  "//=======================================================================\n"
+  "bool NormalAdaptation (in vec3 theView, in vec3 theGeometryNormal, inout vec3 theSmoothNormal)\n"
+  "{\n"
+  "  float aMinCos = dot(theView, theGeometryNormal);\n"
+  "  aMinCos = 0.5 * (sqrt(1.0 - aMinCos) + sqrt(1.0 + aMinCos));\n"
+  "  float aCos = dot(theGeometryNormal, theSmoothNormal);\n"
+  "  if (aCos < aMinCos)\n"
+  "  {\n"
+  "    theSmoothNormal = aMinCos * theGeometryNormal + normalize(theSmoothNormal - aCos * theGeometryNormal) * sqrt(1.0 - aMinCos * aMinCos);\n"
+  "    return true;\n"
+  "  }\n"
+  "  return false;\n"
+  "}\n"
+  "\n"
+  "//=======================================================================\n"
   "// function : PathTrace\n"
   "// purpose  : Calculates radiance along the given ray\n"
   "//=======================================================================\n"
@@ -783,12 +800,12 @@ static const char Shaders_PathtraceBase_fs[] =
   "  {\n"
   "    SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);\n"
   "\n"
-  "    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTransfID);\n"
+  "    STriangle aTriangle = SceneNearestHit (theRay, theInverse, aHit, aTransfID);\n"
   "\n"
   "    // check implicit path\n"
   "    vec3 aLe = IntersectLight (theRay, aDepth, aHit.Time, aExpPDF);\n"
   "\n"
-  "    if (any (greaterThan (aLe, ZERO)) || aTriIndex.x == -1)\n"
+  "    if (any (greaterThan (aLe, ZERO)) || aTriangle.TriIndex.x == -1)\n"
   "    {\n"
   "      float aMIS = (aDepth == 0 || aImpPDF == MAXFLOAT) ? 1.f :\n"
   "        aImpPDF * aImpPDF / (aExpPDF * aExpPDF + aImpPDF * aImpPDF);\n"
@@ -819,31 +836,30 @@ static const char Shaders_PathtraceBase_fs[] =
   "    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));\n"
+  "    aBSDF.Kc = texelFetch (uRaytraceMaterialTexture, MATERIAL_KC (aTriangle.TriIndex.w));\n"
+  "    aBSDF.Kd = texelFetch (uRaytraceMaterialTexture, MATERIAL_KD (aTriangle.TriIndex.w));\n"
+  "    aBSDF.Ks = texelFetch (uRaytraceMaterialTexture, MATERIAL_KS (aTriangle.TriIndex.w));\n"
+  "    aBSDF.Kt = texelFetch (uRaytraceMaterialTexture, MATERIAL_KT (aTriangle.TriIndex.w));\n"
   "\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"
+  "    aBSDF.FresnelCoat = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_COAT (aTriangle.TriIndex.w)).xyz;\n"
+  "    aBSDF.FresnelBase = texelFetch (uRaytraceMaterialTexture, MATERIAL_FRESNEL_BASE (aTriangle.TriIndex.w));\n"
   "\n"
-  "    vec4 anLE = texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriIndex.w));\n"
+  "    vec4 anLE = texelFetch (uRaytraceMaterialTexture, MATERIAL_LE (aTriangle.TriIndex.w));\n"
   "\n"
   "    // compute smooth normal (in parallel with fetch)\n"
-  "    vec3 aNormal = SmoothNormal (aHit.UV, aTriIndex);\n"
+  "    vec3 aNormal = SmoothNormal (aHit.UV, aTriangle.TriIndex);\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 (aBSDF.Kd.w >= 0.0 || aBSDF.Kt.w >= 0.0 || anLE.w >= 0.0)\n"
+  "    if (aBSDF.Kd.w >= 0.0 || aBSDF.Kt.w >= 0.0 || aBSDF.FresnelBase.w >=0.0 || anLE.w >= 0.0)\n"
   "    {\n"
-  "      vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriIndex), 0.f, 1.f);\n"
-  "      vec4 aTrsfRow1 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriIndex.w));\n"
-  "      vec4 aTrsfRow2 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriIndex.w));\n"
+  "      vec2 aUVs[3];\n"
+  "      vec4 aTexCoord = vec4 (SmoothUV (aHit.UV, aTriangle.TriIndex, aUVs), 0.f, 1.f);\n"
+  "      vec4 aTrsfRow1 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS1 (aTriangle.TriIndex.w));\n"
+  "      vec4 aTrsfRow2 = texelFetch (uRaytraceMaterialTexture, MATERIAL_TRS2 (aTriangle.TriIndex.w));\n"
   "      aTexCoord.st = vec2 (dot (aTrsfRow1, aTexCoord),\n"
   "                           dot (aTrsfRow2, aTexCoord));\n"
   "\n"
@@ -858,15 +874,15 @@ static const char Shaders_PathtraceBase_fs[] =
   "        float aPbrRough2 = aTexMetRough.y * aTexMetRough.y;\n"
   "        aBSDF.Ks.a *= aPbrRough2;\n"
   "        // when using metal-roughness texture, global metalness of material (encoded in FresnelBase) is expected to be 1.0 so that Kd will be 0.0\n"
-  "        aBSDF.Kd.rgb = aBSDF.FresnelBase * (1.0 - aPbrMetal);\n"
-  "        aBSDF.FresnelBase *= aPbrMetal;\n"
+  "        aBSDF.Kd.rgb = aBSDF.FresnelBase.rgb * (1.0 - aPbrMetal);\n"
+  "        aBSDF.FresnelBase.rgb *= aPbrMetal;\n"
   "      }\n"
   "      if (aBSDF.Kd.w >= 0.0)\n"
   "      {\n"
   "        vec4 aTexColor = textureLod (sampler2D (uTextureSamplers[int (aBSDF.Kd.w)]), aTexCoord.st, 0.0);\n"
   "        vec3 aDiff = aTexColor.rgb * aTexColor.a;\n"
   "        aBSDF.Kd.rgb *= aDiff;\n"
-  "        aBSDF.FresnelBase *= aDiff;\n"
+  "        aBSDF.FresnelBase.rgb *= aDiff;\n"
   "        if (aTexColor.a != 1.0)\n"
   "        {\n"
   "          // mix transparency BTDF with texture alpha-channel\n"
@@ -874,8 +890,25 @@ static const char Shaders_PathtraceBase_fs[] =
   "          aBSDF.Kt.rgb = (UNIT - aTexColor.aaa) + aTexColor.a * aBSDF.Kt.rgb;\n"
   "        }\n"
   "      }\n"
+  "      #ifndef IGNORE_NORMAL_MAP\n"
+  "      if (aBSDF.FresnelBase.w >= 0.0)\n"
+  "      {\n"
+  "        for (int i = 0 ; i < 3; ++i)\n"
+  "        {\n"
+  "          aUVs[i] = vec2 (dot (aTrsfRow1, vec4(aUVs[i], 0.0, 1.0)),\n"
+  "                          dot (aTrsfRow2, vec4(aUVs[i], 0.0, 1.0)));\n"
+  "        }\n"
+  "        vec3 aMapNormalValue = textureLod (sampler2D (uTextureSamplers[int (aBSDF.FresnelBase.w)]), aTexCoord.st, 0.0).xyz;\n"
+  "        mat2 aDeltaUVMatrix = mat2 (aUVs[1] - aUVs[0], aUVs[1] - aUVs[2]);\n"
+  "        mat2x3 aDeltaVectorMatrix = mat2x3 (aTriangle.Points[1] - aTriangle.Points[0], aTriangle.Points[1] - aTriangle.Points[2]);\n"
+  "        aNormal = TangentSpaceNormal (aDeltaUVMatrix, aDeltaVectorMatrix, aMapNormalValue, aNormal, true);\n"
+  "      }\n"
+  "      #endif\n"
   "    }\n"
   "#endif\n"
+  "    NormalAdaptation (-theRay.Direct, aHit.Normal, aNormal);\n"
+  "    aHit.Normal = aNormal;\n"
+  "    SLocalSpace aSpace = buildLocalSpace (aNormal);\n"
   "\n"
   "    if (uLightCount > 0 && IsNotZero (aBSDF, aThroughput))\n"
   "    {\n"
@@ -924,7 +957,7 @@ static const char Shaders_PathtraceBase_fs[] =
   "\n"
   "    if (aInMedium) // handle attenuation\n"
   "    {\n"
-  "      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriIndex.w));\n"
+  "      vec4 aScattering = texelFetch (uRaytraceMaterialTexture, MATERIAL_ABSORPT_BASE (aTriangle.TriIndex.w));\n"
   "\n"
   "      aThroughput *= exp (-aHit.Time * aScattering.w * (UNIT - aScattering.rgb));\n"
   "    }\n"
index 6b852d4..63f44e1 100644 (file)
@@ -173,6 +173,14 @@ static const char Shaders_RaytraceBase_fs[] =
   "  vec3 Normal;\n"
   "};\n"
   "\n"
+  "//! Stores triangle's vertex indexes and vertexes itself\n"
+  "struct STriangle\n"
+  "{\n"
+  "  ivec4 TriIndex;\n"
+  "\n"
+  "  vec3 Points[3];\n"
+  "};\n"
+  "\n"
   "/////////////////////////////////////////////////////////////////////////////////////////\n"
   "// Some useful constants\n"
   "\n"
@@ -465,7 +473,7 @@ static const char Shaders_RaytraceBase_fs[] =
   "#define TRG_OFFSET(treelet) treelet.SubData.w\n"
   "\n"
   "//! Identifies the absence of intersection.\n"
-  "#define INALID_HIT ivec4 (-1)\n"
+  "#define INVALID_HIT ivec4 (-1)\n"
   "\n"
   "//! Global stack shared between traversal functions.\n"
   "int Stack[STACK_SIZE];\n"
@@ -501,9 +509,9 @@ static const char Shaders_RaytraceBase_fs[] =
   "// function : SceneNearestHit\n"
   "// purpose  : Finds intersection with nearest scene triangle\n"
   "// =======================================================================\n"
-  "ivec4 SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)\n"
+  "STriangle SceneNearestHit (in SRay theRay, in vec3 theInverse, inout SIntersect theHit, out int theTrsfId)\n"
   "{\n"
-  "  ivec4 aTriIndex = INALID_HIT;\n"
+  "  STriangle aTriangle = STriangle (INVALID_HIT, vec3[](vec3(0.0), vec3(0.0), vec3(0.0)));\n"
   "\n"
   "  int aNode =  0; // node to traverse\n"
   "  int aHead = -1; // pointer of stack\n"
@@ -610,17 +618,22 @@ static const char Shaders_RaytraceBase_fs[] =
   "\n"
   "      for (int anIdx = aData.y; anIdx <= aData.z; ++anIdx)\n"
   "      {\n"
-  "        ivec4 aTriangle = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));\n"
+  "        ivec4 aTriIndex = texelFetch (uGeometryTriangTexture, anIdx + TRG_OFFSET (aSubTree));\n"
+  "        vec3 aPoints[3];\n"
   "\n"
-  "        vec3 aPoint0 = texelFetch (uGeometryVertexTexture, aTriangle.x += VRT_OFFSET (aSubTree)).xyz;\n"
-  "        vec3 aPoint1 = texelFetch (uGeometryVertexTexture, aTriangle.y += VRT_OFFSET (aSubTree)).xyz;\n"
-  "        vec3 aPoint2 = texelFetch (uGeometryVertexTexture, aTriangle.z += VRT_OFFSET (aSubTree)).xyz;\n"
+  "        aPoints[0] = texelFetch (uGeometryVertexTexture, aTriIndex.x += VRT_OFFSET (aSubTree)).xyz;\n"
+  "        aPoints[1] = texelFetch (uGeometryVertexTexture, aTriIndex.y += VRT_OFFSET (aSubTree)).xyz;\n"
+  "        aPoints[2] = texelFetch (uGeometryVertexTexture, aTriIndex.z += VRT_OFFSET (aSubTree)).xyz;\n"
   "\n"
-  "        IntersectTriangle (aSubTree.TrsfRay, aPoint0, aPoint1, aPoint2, aTimeUV, aNormal);\n"
+  "        IntersectTriangle (aSubTree.TrsfRay, aPoints[0], aPoints[1], aPoints[2], aTimeUV, aNormal);\n"
   "\n"
   "        if (aTimeUV.x < theHit.Time)\n"
   "        {\n"
-  "          aTriIndex = aTriangle;\n"
+  "          aTriangle.TriIndex = aTriIndex;\n"
+  "          for (int i = 0; i < 3; ++i)\n"
+  "          {\n"
+  "            aTriangle.Points[i] = aPoints[i];\n"
+  "          }\n"
   "\n"
   "          theTrsfId = TRS_OFFSET (aSubTree);\n"
   "\n"
@@ -667,7 +680,7 @@ static const char Shaders_RaytraceBase_fs[] =
   "    }\n"
   "  }\n"
   "\n"
-  "  return aTriIndex;\n"
+  "  return aTriangle;\n"
   "}\n"
   "\n"
   "// =======================================================================\n"
@@ -917,15 +930,21 @@ static const char Shaders_RaytraceBase_fs[] =
   "// purpose  : Interpolates UV coordinates across the triangle\n"
   "// =======================================================================\n"
   "#ifdef USE_TEXTURES\n"
-  "vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle)\n"
+  "vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle, out vec2[3] theUVs)\n"
   "{\n"
-  "  vec2 aTexCrd0 = texelFetch (uGeometryTexCrdTexture, theTriangle.x).st;\n"
-  "  vec2 aTexCrd1 = texelFetch (uGeometryTexCrdTexture, theTriangle.y).st;\n"
-  "  vec2 aTexCrd2 = texelFetch (uGeometryTexCrdTexture, theTriangle.z).st;\n"
+  "  theUVs[0] = texelFetch (uGeometryTexCrdTexture, theTriangle.x).st;\n"
+  "  theUVs[1] = texelFetch (uGeometryTexCrdTexture, theTriangle.y).st;\n"
+  "  theUVs[2] = texelFetch (uGeometryTexCrdTexture, theTriangle.z).st;\n"
   "\n"
-  "  return aTexCrd1 * theUV.x +\n"
-  "         aTexCrd2 * theUV.y +\n"
-  "         aTexCrd0 * (1.0f - theUV.x - theUV.y);\n"
+  "  return theUVs[1] * theUV.x +\n"
+  "         theUVs[2] * theUV.y +\n"
+  "         theUVs[0] * (1.0f - theUV.x - theUV.y);\n"
+  "}\n"
+  "\n"
+  "vec2 SmoothUV (in vec2 theUV, in ivec4 theTriangle)\n"
+  "{\n"
+  "  vec2 aUVs[3];\n"
+  "  return SmoothUV (theUV, theTriangle, aUVs);\n"
   "}\n"
   "#endif\n"
   "\n"
@@ -1012,7 +1031,7 @@ static const char Shaders_RaytraceBase_fs[] =
   "  {\n"
   "    SIntersect aHit = SIntersect (MAXFLOAT, vec2 (ZERO), ZERO);\n"
   "\n"
-  "    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId);\n"
+  "    ivec4 aTriIndex = SceneNearestHit (theRay, theInverse, aHit, aTrsfId).TriIndex;\n"
   "\n"
   "    if (aTriIndex.x == -1)\n"
   "    {\n"
diff --git a/src/Shaders/Shaders_TangentSpaceNormal_glsl.pxx b/src/Shaders/Shaders_TangentSpaceNormal_glsl.pxx
new file mode 100644 (file)
index 0000000..0878cd5
--- /dev/null
@@ -0,0 +1,20 @@
+// This file has been automatically generated from resource file src/Shaders/TangentSpaceNormal.glsl
+
+static const char Shaders_TangentSpaceNormal_glsl[] =
+  "//! Calculates transformation from tangent space and apply it to value from normal map to get normal in object space\n"
+  "vec3 TangentSpaceNormal (in mat2 theDeltaUVMatrix,\n"
+  "                         in mat2x3 theDeltaVectorMatrix,\n"
+  "                         in vec3 theNormalMapValue,\n"
+  "                         in vec3 theNormal,\n"
+  "                         in bool theIsInverse)\n"
+  "{\n"
+  "  theNormalMapValue = normalize(theNormalMapValue * 2.0 - vec3(1.0));\n"
+  "  // Inverse matrix\n"
+  "  theDeltaUVMatrix = mat2 (theDeltaUVMatrix[1][1], -theDeltaUVMatrix[0][1], -theDeltaUVMatrix[1][0], theDeltaUVMatrix[0][0]);\n"
+  "  theDeltaVectorMatrix = theDeltaVectorMatrix * theDeltaUVMatrix;\n"
+  "  // Gram-Schmidt orthogonalization\n"
+  "  theDeltaVectorMatrix[1] = normalize(theDeltaVectorMatrix[1] - dot(theNormal, theDeltaVectorMatrix[1]) * theNormal);\n"
+  "  theDeltaVectorMatrix[0] = cross(theDeltaVectorMatrix[1], theNormal);\n"
+  "  float aDirection = theIsInverse ? -1.0 : 1.0;\n"
+  "  return mat3 (aDirection * theDeltaVectorMatrix[0], aDirection * theDeltaVectorMatrix[1], theNormal) * theNormalMapValue;\n"
+  "}\n";
diff --git a/src/Shaders/TangentSpaceNormal.glsl b/src/Shaders/TangentSpaceNormal.glsl
new file mode 100644 (file)
index 0000000..539759f
--- /dev/null
@@ -0,0 +1,17 @@
+//! Calculates transformation from tangent space and apply it to value from normal map to get normal in object space
+vec3 TangentSpaceNormal (in mat2 theDeltaUVMatrix,
+                         in mat2x3 theDeltaVectorMatrix,
+                         in vec3 theNormalMapValue,
+                         in vec3 theNormal,
+                         in bool theIsInverse)
+{
+  theNormalMapValue = normalize(theNormalMapValue * 2.0 - vec3(1.0));
+  // Inverse matrix
+  theDeltaUVMatrix = mat2 (theDeltaUVMatrix[1][1], -theDeltaUVMatrix[0][1], -theDeltaUVMatrix[1][0], theDeltaUVMatrix[0][0]);
+  theDeltaVectorMatrix = theDeltaVectorMatrix * theDeltaUVMatrix;
+  // Gram-Schmidt orthogonalization
+  theDeltaVectorMatrix[1] = normalize(theDeltaVectorMatrix[1] - dot(theNormal, theDeltaVectorMatrix[1]) * theNormal);
+  theDeltaVectorMatrix[0] = cross(theDeltaVectorMatrix[1], theNormal);
+  float aDirection = theIsInverse ? -1.0 : 1.0;
+  return mat3 (aDirection * theDeltaVectorMatrix[0], aDirection * theDeltaVectorMatrix[1], theNormal) * theNormalMapValue;
+}
index 65a1834..d1aa802 100644 (file)
@@ -12022,6 +12022,22 @@ static Standard_Integer VRenderParams (Draw_Interpretor& theDI,
       }
       aParams.UseEnvironmentMapBackground = toEnable;
     }
+    else if (aFlag == "-ignorenormalmap")
+    {
+      if (toPrint)
+      {
+        theDI << (aParams.ToIgnoreNormalMapInRayTracing ? "on" : "off") << " ";
+        continue;
+      }
+
+      Standard_Boolean toEnable = Standard_True;
+      if (++anArgIter < theArgNb
+        && !ViewerTest::ParseOnOff (theArgVec[anArgIter], toEnable))
+      {
+        --anArgIter;
+      }
+      aParams.ToIgnoreNormalMapInRayTracing = toEnable;
+    }
     else if (aFlag == "-twoside")
     {
       if (toPrint)
@@ -14433,6 +14449,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      '-ignoreNormalMap on|off'   Enables/disables normal map ignoring during path tracing"
     "\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"
diff --git a/tests/v3d/raytrace/normal_map b/tests/v3d/raytrace/normal_map
new file mode 100644 (file)
index 0000000..f2b6ff8
--- /dev/null
@@ -0,0 +1,34 @@
+puts "========"
+puts "0031275: Visualization, TKOpenGl - handle normal-map texture with Path-Tracing"
+puts "========"
+
+pload XDE OCAF MODELING VISUALIZATION
+catch { Close D }
+ReadGltf D [locate_data_file bug31275_SphereWithNormalMap.glb]
+
+vclear
+vinit v -w 1024 -h 1024
+vbackground -cubemap [locate_data_file Circus_CubeMap_V.png]
+vcamera -persp
+vlight -clear
+vlight -add ambient
+XDisplay -dispmode 1 D
+vback
+vfit
+
+vrenderparams -ignoreNormalMap on
+vrenderparams -ray -gi -rayDepth 10
+vfps 200
+vdump ${imagedir}/${casename}_without_normal_map.png
+
+vrenderparams -ignoreNormalMap off
+vfps 200
+vdump ${imagedir}/${casename}_with_normal_map_back.png
+
+vfront
+vfps 200
+vdump ${imagedir}/${casename}_with_normal_map_front.png
+
+vlight -add positional -pos 0 0 0 -head 1 -intensity 10
+vfps 200
+vdump ${imagedir}/${casename}_point_light.png