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.
IsAntialiasingEnabled (Standard_False),
IsTransparentShadowEnabled (Standard_False),
UseEnvironmentMapBackground (Standard_False),
+ ToIgnoreNormalMapInRayTracing (Standard_False),
CoherentPathTracingMode (Standard_False),
AdaptiveScreenSampling (Standard_False),
AdaptiveScreenSamplingAtomic(Standard_False),
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
#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)
&& (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)"
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";
}
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;"
//! 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;
AdaptiveScreenSampling (Standard_False),
AdaptiveScreenSamplingAtomic (Standard_False),
UseEnvMapForBackground (Standard_False),
+ ToIgnoreNormalMap (Standard_False),
RadianceClampingValue (30.0),
DepthOfField (Standard_False),
CubemapForBack (Standard_False),
#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
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())
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)
}
}
+ if (myRaytraceParameters.ToIgnoreNormalMap)
+ {
+ aPrefixString += TCollection_AsciiString("\n#define IGNORE_NORMAL_MAP");
+ }
+
if (myRaytraceParameters.CubemapForBack)
{
aPrefixString += TCollection_AsciiString("\n#define BACKGROUND_CUBEMAP");
|| 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;
}
if (!aShaderFolder.IsEmpty())
{
const TCollection_AsciiString aFiles[] = { aShaderFolder + "/RaytraceBase.fs",
+ aShaderFolder + "/TangentSpaceNormal.glsl",
aShaderFolder + "/PathtraceBase.fs",
aShaderFolder + "/RaytraceRender.fs",
"" };
else
{
const TCollection_AsciiString aSrcShaders[] = { Shaders_RaytraceBase_fs,
+ Shaders_TangentSpaceNormal_glsl,
Shaders_PathtraceBase_fs,
Shaders_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
Shaders_PathtraceBase_fs.pxx
Shaders_RaytraceBase_vs.pxx
Shaders_RaytraceSmooth_fs.pxx
+Shaders_TangentSpaceNormal_glsl.pxx
//! 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;
};
///////////////////////////////////////////////////////////////////////////////////////
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);
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,
}
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);
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
{
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);
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));
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
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))
{
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));
}
return 1.0 / (theConstAttenuation + theLinearAttenuation * theDistance);
}
return occRangedPointLightAttenuation (theDistance, theRange);
-}
\ No newline at end of file
+}
vec3 Normal;
};
+//! Stores triangle's vertex indexes and vertexes itself
+struct STriangle
+{
+ ivec4 TriIndex;
+
+ vec3 Points[3];
+};
+
/////////////////////////////////////////////////////////////////////////////////////////
// Some useful constants
#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];
// 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
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);
}
}
- return aTriIndex;
+ return aTriangle;
}
// =======================================================================
// 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
{
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)
{
" //! 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"
"\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"
"\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"
" }\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"
"}\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"
" {\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"
" 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"
" 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"
" 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"
"\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"
" 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"
"#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"
"// 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"
"\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"
" }\n"
" }\n"
"\n"
- " return aTriIndex;\n"
+ " return aTriangle;\n"
"}\n"
"\n"
"// =======================================================================\n"
"// 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"
" {\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"
--- /dev/null
+// 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";
--- /dev/null
+//! 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;
+}
}
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)
"\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"
--- /dev/null
+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