]> OCCT Git - occt.git/commitdiff
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 a437d3a6eff523a3b30150faf48a00f33b3830d8..fa18c5b73b3ec23a77512065338cf5b644334758 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 e5c143a53b418b7e9c702007175763d2de09bf20..4fda63fe14269bc4144ec7301b247330b0b17b0a 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 8f39d03f22dc668eeb0fd929882e86a830ced6e9..346c753a67815bfe89a476ce13755f137eac2c6e 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 90377c68fb9c6db5459cd412c75afdbe92943474..d39623baa712e59217dfc88e2967d24f905907ba 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 06d166ba2e93efa8d45a7c512dc508338bec16e6..7c7ada0344433f84c6b66ea19ad8e031e0daecd5 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 0d8c9085586cfb7dcbf94e8506472e7a880b17d6..e7a324ff15875fbd2d9e9d38991754a0a0ddf5c8 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 cb840393a5da8a1b65e4097e44dbd65312d61f39..3334e956fb1478654625d8dd09ea35c65940bda5 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 5c73a6773a9ce44b9cbf6b20cc71880641cddc11..0c7ead83ca45be7f2278f9ab14c84952f2de1f11 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 3db4426aebac9df97e3b5828ae12c9d8adad89a7..fb32f24022f098756e7fea33db72e262f357d2d6 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 6b852d4a4b456374c3c3e93c3b93f8f96d624676..63f44e1a6906b645b8d5851a1c222fff53729ef1 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 65a1834d4908a7967a56e9996d983aab5961ad0f..d1aa802c1000bf6c6fb6a73eb0e1ff859ae5baa9 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