// _DoubleSidedConstants is float3(-1, -1, -1) in flip mode and float3(1, 1, -1) in mirror mode
float flipSign = input.isFrontFace ? 1.0 : _DoubleSidedConstants.x; // TOCHECK : GetOddNegativeScale() is not necessary here as it is apply for tangent space creation.
#ifdef SURFACE_GRADIENT
input.vtxNormalWS = flipSign * input.vtxNormalWS;
input.mikktsBino = flipSign * input.mikktsBino;
// TOCHECK: seems that we don't need to invert any genBasisTB(), sign cancel. Which is expected as we deal with surface gradient.
#else
#ifdef SURFACE_GRADIENT
// TOCHECK: seems that we don't need to invert any genBasisTB(), sign cancel. Which is expected as we deal with surface gradient.
#endif
#endif
}
{
ppdParam.uv = layerTexCoord.base.uv;
#ifdef SURFACE_GRADIENT
// The TBN is not normalize, normalize it to do per pixel displacement
// This function convert the tangent space normal/tangent to world space and orthonormalize it + apply a correction of the normal if it is not pointing towards the near plane
// used for vertex level tangent space only (support on UV set 0 only)
float3 vtxNormalWS;
float3 mikktsTang;
float3 mikktsBino;
// Use for the 3 other UVSet;
float3 vT1, vB1;
float3 vT2, vB2;
float3 vT3, vB3;
#else
float3 worldToTangent[3]; // These 3 vectors are normalized (no need for the material to normalize) and these are only for UVSet 0
#endif
// When using worldToTangent with surface gradient, it doesn't normalize the tangent/bitangent vector (We instead use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// this mean that any usage of worldToTangent[1] or worldToTangent[2] outside of the context of normal map (like for POM) must normalize the TBN (TCHECK if this make any difference ?)
// When not using surface gradient, each vector of worldToTangent are normalize (TODO: Maybe they should not even in case of no surface gradient ? Ask Morten)
float3x3 worldToTangent;
// For two sided lighting
bool isFrontFace;
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
// Init to some default value to make the computer quiet (else it output "divide by zero" warning even if value is not used).
// TODO: We should be able to not make distinction between the two path, but it mean material need to be aware to normalize the TBN when required, like for example for POM.
// For now do some test by keeping code consistent with previous visual.
#ifdef SURFACE_GRADIENT
// Normalize normalWS vector but keep the renormFactor to apply it to bitangent and tangent
// prepare for surfgrad formulation without breaking compliance (use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// Caution: We assume that tangent space are always use in a context where positionWS is availble.
// Which is true in our framework. When positionWS is 0 it mean we are in a deffered or compute pass which don't use our tangent space (so code will be remove by the compiler)
// TODO: We should use relative camera position here - This will be automatic when we will move to camera relative space.
float flipSign = dot(dPdy, cross(nrmVertexNormal, dPdx)) < 0 ? -1 : 1; // gives same as the commented out line above
output.vtxNormalWS = nrmVertexNormal;
// mikkts for conventional vertex level tspace (no normalizes is mandatory)
output.mikktsTang = input.interpolators2.xyz;
// bitangent on the fly option in xnormal to reduce vertex shader outputs. Also described in https://wiki.blender.org/index.php/Dev:Shading/Tangent_Space_Normal_Maps
output.mikktsBino = (input.interpolators2.w > 0.0 ? 1.0 : -1.0) * GetOddNegativeScale() * cross(input.interpolators1, input.interpolators2.xyz); // TODO: use CreateWorldToTangent instead once we clean code
// prepare for surfgrad formulation without breaking compliance (use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// NdotV should not be negative for visible pixels, but it can happen due to the
// perspective projection and the normal mapping + decals. In that case, the normal
// should be modified to become valid (i.e facing the camera) to avoid weird artifacts.
// Note: certain applications (e.g. SpeedTree) require to still have negative normal to perform their own two sided lighting
// This will potentially reduce the length of the normal at edges of geometry.
float GetShiftedNdotV(inout float3 N, float3 V, bool twoSided)
// Note: certain applications (e.g. SpeedTree) require to still have negative normal to perform their own two sided lighting, they can use wantNegativeNormal
// This will potentially reduce the length of the normal at edges of geometry.
float GetShiftedNdotV(inout float3 N, float3 V, bool wantNegativeNormal)
if (!twoSided && NdotV < limit)
if (!wantNegativeNormal && NdotV < limit)
{
// We do not renormalize the normal because { abs(length(N) - 1.0) < limit }.