// To flip in case of double sided, we must flip the vertex normal and this will apply to the whole process either in surface gradient or not.
// As here we are in the function call GetSurfaceAndBuiltinData(), the tangent space is already built, so we need to flip both normal and bitangent.
// This function will modify FragInputs and this is not propagate outside of GetSurfaceAndBuiltinData(). This is ok as tangent space is not use outside of GetSurfaceAndBuiltinData().
void ApplyDoubleSidedFlip(inout FragInputs input)
{
#ifdef _DOUBLESIDED_ON
// _DoubleSidedMode is float3(-1, -1, -1) in flip mode and float3(1, 1, -1) in mirror mode
float flipSign = input.isFrontFace ? 1.0 : _DoubleSidedMode.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.
// _DoubleSidedMode is float3(-1, -1, -1) in flip mode and float3(1, 1, -1) in mirror mode
float flipSign = input.isFrontFace ? 1.0 : -_DoubleSidedMode.x; // TOCHECK : GetOddNegativeScale() is not necessary here as it is apply for tangent space creation.
#ifdef SURFACE_GRADIENT
normalTS = flipSign * normalTS;
#else
normalTS.z *= flipSign;
#endif
#endif
}
#ifndef LAYERED_LIT_SHADER
#define SAMPLER_NORMALMAP_IDX sampler_NormalMap
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
ApplyDoubleSidedFlip(input); // Apply double sided flip on the vertex normal
// to be able to combine object space normal with detail map we transform it to tangent space (object space normal composition is complex operation).
// to be able to combine object space normal with detail map or to apply a "scale" we transform it to tangent space (object space normal composition is complex operation).
// Note: There is no such a thing like triplanar with object space normal, so we call directly 2D function
// TODO : Test if GetOddNegativeScale() is necessary here in case of normal map, as GetOddNegativeScale is take into account in CreateTangentToWorld();
// 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
// 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 CreateTangentToWorld 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).