2021-09-09 20:42:29 -04:00

772 lines
42 KiB
HLSL

//-------------------------------------------------------------------------------------
// Defines
//-------------------------------------------------------------------------------------
// Gradients are now required:
#define SURFACE_GRADIENT // Note: this affects Material/MaterialUtilities.hlsl's GetNormalWS() and makes it expect a surface gradient.
// Obviously in SHADER_STAGE_RAY_TRACING, we don't have the on the fly tangent frame calculation because we don't have derivatives,
// but we can still use surface gradients for the rest (ie can't use anything else than UV0 if using UVs, and the vertex interpolated TB)
//to test #define FLAKES_TILE_BEFORE_SCALE
#define AXF_REUSE_SCREEN_DDXDDY // we generically plug the cone apprimation into the most general grad codepath so this is forced def in raytracing
// ...ie use _GRAD sampling for everything and calculate those only one time:
// offset doesn't change derivatives, and scales just scales them, so we can cache them.
// The compiler can't unroll the lightloop if flakes are sampled inside it, so we need to cache either LOD
// or derivatives. We prefer the later, as the CalculateLevelOfDetail will not work when anisotropic filtering
// is used, and AxF materials textures often have trilinear filtering set.
#define FLAKES_USE_DDXDDY
#define AXF_USES_RG_NORMAL_MAPS // else, RGB
#define AXF_RAYTRACING_USE_CONE_TO_GRAD
//#define AXF_RAYTRACING_CONE_TO_GRAD_SCALE (0.1)
#define AXF_RAYTRACING_CONE_TO_GRAD_SCALE (_RayTracingTexFilteringScale)
//-------------------------------------------------------------------------------------
// Fill SurfaceData/Builtin data function
//-------------------------------------------------------------------------------------
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Sampling/SampleUVMapping.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/BuiltinUtilities.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/MaterialUtilities.hlsl"
#ifndef SHADER_STAGE_RAY_TRACING
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Decal/DecalUtilities.hlsl"
#endif
//-----------------------------------------------------------------------------
// Texture Mapping
//-----------------------------------------------------------------------------
#ifdef AXF_USES_RG_NORMAL_MAPS
#define AXF_DERIVATIVE_NORMAL UnpackDerivativeNormalRGorAG
#else
#define AXF_DERIVATIVE_NORMAL UnpackDerivativeNormalRGB
#endif
// Note: the scaling _Material_SO.xy should already be in texuv, but NOT the bias.
#define AXF_TRANSFORM_TEXUV_BYNAME(texuv, name) ((texuv.xy) * name##_SO.xy + name##_SO.zw + _Material_SO.zw)
#define AXF_GET_SINGLE_SCALE_OFFSET(name) (name##_SO)
#define AXF_TEXSIZE_FROM_NAME(name) (name##_TexelSize.zw)
#define AXF_TRANSFORM_TEXUV(texuv, scaleOffset) ((texuv.xy) * scaleOffset.xy + scaleOffset.zw + _Material_SO.zw)
// Note: the scaling _Material_SO.xy should already be in ddx and ddy:
#define AXF_SCALE_DDXDDY_BYNAME(vddx, name) ((vddx) * (name##_SO.xy))
struct TextureUVMapping
{
#ifdef _MAPPING_TRIPLANAR
float2 uvZY;
float2 uvXZ;
float2 uvXY;
float3 triplanarWeights;
float2 ddxZY;
float2 ddyZY;
float2 ddxXZ;
float2 ddyXZ;
float2 ddxXY;
float2 ddyXY;
#else
float2 uvBase; // uv0..uv3 or a planar set (ZY, XZ or XY)
float2 ddxBase;
float2 ddyBase;
#endif
float2 mainScales; // only used when raytracing as these need to be tracked, no pixel quads mean not possible to have ddx automatically get the effect of scalings multiplied in UVs
float3 vertexNormalWS;
float3 vertexTangentWS;
float3 vertexBitangentWS;
};
//-----------------------------------------------------------------------------
#if !defined(SHADER_STAGE_RAY_TRACING)
#ifndef RayCone
#define RayCone float // dummy type
#endif
// For AXF_DD*, in non raytracing context, only first param is used and these uvs can be prescaled
#if 0
#define AXF_DDX(uv, scales, rayCone, geomNormalWS, V) ddx_fine(uv)
#define AXF_DDY(uv, scales, rayCone, geomNormalWS, V) ddy_fine(uv)
#else
#define AXF_DDX(uv, scales, rayCone, geomNormalWS, V) ddx(uv)
#define AXF_DDY(uv, scales, rayCone, geomNormalWS, V) ddy(uv)
#endif
#define GETSURFACEANDBUILTINDATA_RAYCONE_PARAM ((RayCone)0)
#define AXF_CALCULATE_TEXTURE2D_LOD(a,b,c,duvdx,duvdy,scales,texelSize,rayCone) CALCULATE_TEXTURE2D_LOD(a,b,c)
#else
//-----------------------------------------------------------------------------
//defined(SHADER_STAGE_RAY_TRACING)
// we generically plug the cone apprimation into the most general grad codepath so this is needed in raytracing:
#ifndef AXF_REUSE_SCREEN_DDXDDY
#define AXF_REUSE_SCREEN_DDXDDY
#endif
//
// Fake isotropic filtering grads as constants or from RayCone.
//
#define GETSURFACEANDBUILTINDATA_RAYCONE_PARAM (rayCone)
#define AXF_RAYTRACING_DEFAULT_GRAD 0.001
float2 AxFFakeDDX(real2 x) { return (float2)AXF_RAYTRACING_DEFAULT_GRAD; }
#if !defined(AXF_RAYTRACING_USE_CONE_TO_GRAD)
#define AXF_CALCULATE_TEXTURE2D_LOD(a,b,c,duvdx,duvdy,scales,texelSize,rayCone) (0)
#define AXF_DDX(uv, scales, rayCone, geomNormalWS, V) AxFFakeDDX(uv)
#define AXF_DDY(uv, scales, rayCone, geomNormalWS, V) AxFFakeDDX(uv)
#else
//
//AXF_RAYTRACING_USE_CONE_TO_GRAD
//
float2 AxFDDXFromCone(float2 scales, RayCone rayCone, float3 geomNormalWS, float3 V)
{
// Very simple for now, try to use normal vs view dir also
return scales * rayCone.width * AXF_RAYTRACING_CONE_TO_GRAD_SCALE * rcp( max(abs(dot(geomNormalWS, V)), 0.7) );
// rayCone is in worldspace unit: this is because the spread slope of a pixel is built from the FOV divided by camera resolution,
// and the slope scales the WS distance of the ray to get the cone (pixel footprint approximative) width.
// (actually the angle is used directly but tan(small angle) ~= small angle)
}
float AxFCalculateLODFromCone(float2 dUVdx, float2 dUVdy,float2 scales /* texture property specific compounded with main scales */, float2 texelSize, RayCone rayCone)
{
// cone actually not required here, all its effects are dumped in the "derivatives" and sent to AXF_CALCULATE_TEXTURE2D_LOD
//Compiler will simplify this as dUVdx and dUVdy are all the same obviously, even component wise
float dSq = max(dot(dUVdx * texelSize.xy * scales.xy, dUVdx * texelSize.xy * scales.xy),
dot(dUVdy * texelSize.xy * scales.xy, dUVdy * texelSize.xy * scales.xy));
float lodCustom = (0.5 * log2(dSq));
return lodCustom;
}
#define AXF_CALCULATE_TEXTURE2D_LOD(textureName, samplerName, coord2, dpdx, dpdy, scales, texelSize, rayCone) AxFCalculateLODFromCone(dpdx, dpdy, scales, texelSize, rayCone)
#define AXF_DDX(uv, scales, rayCone, geomNormalWS, V) AxFDDXFromCone(scales, rayCone, geomNormalWS, V)
#define AXF_DDY(uv, scales, rayCone, geomNormalWS, V) AxFDDXFromCone(scales, rayCone, geomNormalWS, V) // isotropic, one function used only
#endif //#if !defined(AXF_RAYTRACING_USE_CONE_TO_GRAD)
#endif //#if !defined(SHADER_STAGE_RAY_TRACING)
//-----------------------------------------------------------------------------
void InitTextureUVMapping(FragInputs input, float3 V, out TextureUVMapping uvMapping, RayCone rayCone)
{
float3 geomNormalWS = input.tangentToWorld[2];
float2 uvZY;
float2 uvXZ;
float2 uvXY;
float2 uv3 = 0;
// Save main scalings, will be used for raytracing, optimized out otherwise
uvMapping.mainScales = _Material_SO.xy;
// Set uv* variables above: they will contain a set of uv0...3 or a planar set:
#if (defined(_MAPPING_PLANAR) || defined(_MAPPING_TRIPLANAR))
// planar/triplanar
uv3 = 0;
#ifdef _PLANAR_LOCAL
// If we use local planar mapping, convert to local space
GetTriplanarCoordinate(TransformWorldToObject(input.positionRWS), uvXZ, uvXY, uvZY);
#else
GetTriplanarCoordinate(GetAbsolutePositionWS(input.positionRWS), uvXZ, uvXY, uvZY);
#endif
// Note: if only planar mapping is selected, we don't apply AxF main material tiling scale here,
// we select one set with _MappingMask into the uvBase and scale that.
#ifdef _MAPPING_TRIPLANAR
// In that case, we will need to store the 3 sets of planar coordinates:
// (Apply AxF's main material tiling scale also)
uvMapping.uvZY = uvZY * _Material_SO.xy;
uvMapping.uvXZ = uvXZ * _Material_SO.xy;
uvMapping.uvXY = uvXY * _Material_SO.xy;
// These 2 last params are ignored in rasterizer context, but the first is ignore in raytracing context
uvMapping.ddxZY = AXF_DDX(uvMapping.uvZY, _Material_SO.xy, rayCone, geomNormalWS, V); // Because in raytracing without derivatives, we can't prescale the UVs
uvMapping.ddyZY = AXF_DDY(uvMapping.uvZY, _Material_SO.xy, rayCone, geomNormalWS, V); // and thus consider the derivatives will be scaled
uvMapping.ddxXZ = AXF_DDX(uvMapping.uvXZ, _Material_SO.xy, rayCone, geomNormalWS, V); // so we must know the scalings explicitly in our custom ddx function.
uvMapping.ddyXZ = AXF_DDY(uvMapping.uvXZ, _Material_SO.xy, rayCone, geomNormalWS, V);
uvMapping.ddxXY = AXF_DDX(uvMapping.uvXY, _Material_SO.xy, rayCone, geomNormalWS, V);
uvMapping.ddyXY = AXF_DDY(uvMapping.uvXY, _Material_SO.xy, rayCone, geomNormalWS, V);
#endif
#else // #if (defined(_MAPPING_PLANAR) || defined(_MAPPING_TRIPLANAR))
// No planar and no triplanar: uvZY will alias uv0, uvXZ uv1 and uvXY uv2 and _MappingMask will select one:
uv3 = input.texCoord3.xy;
uvZY = input.texCoord0.xy;
uvXZ = input.texCoord1.xy;
uvXY = input.texCoord2.xy;
#endif // #if (defined(_MAPPING_PLANAR) || defined(_MAPPING_TRIPLANAR))
// Set uvBase if not triplanar from the uv* variables above
#ifndef _MAPPING_TRIPLANAR
// No triplanar: uvBase will store the selected single uv or planar coordinate set using _MappingMask:
uvMapping.uvBase = _MappingMask.x * uvZY + // texCoord0 if no planar
_MappingMask.y * uvXZ + // texCoord1 if no planar
_MappingMask.z * uvXY + // texCoord2 if no planar
_MappingMask.w * uv3; // _MappingMask.w should be 0 anyway if planar, but we force uv3 to 0
// Apply AxF's main material tiling scale:
uvMapping.uvBase *= _Material_SO.xy;
uvMapping.ddxBase = AXF_DDX(uvMapping.uvBase, _Material_SO.xy, rayCone, geomNormalWS, V);
uvMapping.ddyBase = AXF_DDY(uvMapping.uvBase, _Material_SO.xy, rayCone, geomNormalWS, V);
#endif
// Calculate triplanar weights, interpreting "local planar space" for coordinates
// as applying to the normal (used for weighting the samples fetched from those planar coords) also.
#ifdef _MAPPING_TRIPLANAR
float3 vertexNormal = input.tangentToWorld[2].xyz;
#ifdef _PLANAR_LOCAL
// If we use local planar mapping, convert to local space
vertexNormal = TransformWorldToObjectDir(vertexNormal);
#endif
uvMapping.triplanarWeights = ComputeTriplanarWeights(vertexNormal);
#endif
// Use surface gradients to build an extra TBN is using anything other than UV0
// Otherwise, use the vertex stage provided TBN as default:
float3 vertexNormalWS = input.tangentToWorld[2];
uvMapping.vertexNormalWS = vertexNormalWS;
uvMapping.vertexTangentWS = input.tangentToWorld[0];
uvMapping.vertexBitangentWS = input.tangentToWorld[1];
#if (defined(_REQUIRE_UV1)||defined(_REQUIRE_UV2)||defined(_REQUIRE_UV3))
float3 dPdx = ddx_fine(input.positionRWS);
float3 dPdy = ddy_fine(input.positionRWS);
float3 sigmaX = dPdx - dot(dPdx, vertexNormalWS) * vertexNormalWS;
float3 sigmaY = dPdy - dot(dPdy, vertexNormalWS) * vertexNormalWS;
//float flipSign = dot(sigmaY, cross(vertexNormalWS, sigmaX) ) ? -1.0 : 1.0;
float flipSign = dot(dPdy, cross(vertexNormalWS, dPdx)) < 0.0 ? -1.0 : 1.0; // gives same as the commented out line above
#if defined(_REQUIRE_UV1)
SurfaceGradientGenBasisTB(vertexNormalWS, sigmaX, sigmaY, flipSign, input.texCoord1.xy, uvMapping.vertexTangentWS, uvMapping.vertexBitangentWS);
#elif defined(_REQUIRE_UV2)
SurfaceGradientGenBasisTB(vertexNormalWS, sigmaX, sigmaY, flipSign, input.texCoord2.xy, uvMapping.vertexTangentWS, uvMapping.vertexBitangentWS);
#elif defined(_REQUIRE_UV3)
SurfaceGradientGenBasisTB(vertexNormalWS, sigmaX, sigmaY, flipSign, input.texCoord3.xy, uvMapping.vertexTangentWS, uvMapping.vertexBitangentWS);
#endif
#endif //#if (defined(_REQUIRE_UV1)||defined(_REQUIRE_UV2)||defined(_REQUIRE_UV3))
}
// Make sure lodBiasOrGrad is used statically!
//
#define AXF_SAMPLE_USE_LOD 1
#define AXF_SAMPLE_USE_BIAS 2
#define AXF_SAMPLE_USE_GRAD 3
// Note that scaleOffset are the texture specific ones, not the main material ones!
float4 AxfSampleTexture2D(TEXTURE2D_PARAM(textureName, samplerName), float4 scaleOffset, TextureUVMapping uvMapping,
int lodBiasOrGrad = 0, float3 lodOrBias = 0, float3x2 triDdx = (float3x2)0, float3x2 triDdy = (float3x2)0)
{
bool useLod = lodBiasOrGrad == 1;
bool useBias = lodBiasOrGrad == 2;
bool useGrad = lodBiasOrGrad == 3;
bool useCachedDdxDdy = false;
#ifdef AXF_REUSE_SCREEN_DDXDDY
useCachedDdxDdy = true;
#endif
#ifdef _MAPPING_TRIPLANAR
float4 val = 0;
val += uvMapping.triplanarWeights.x
* ( useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), lodOrBias.x)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), lodOrBias.x)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), triDdx[0], triDdy[0])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), scaleOffset.xy * uvMapping.ddxZY, scaleOffset.xy * uvMapping.ddyZY)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset)) );
val += uvMapping.triplanarWeights.y
* ( useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), lodOrBias.y)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), lodOrBias.y)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), triDdx[1], triDdy[1])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), scaleOffset.xy * uvMapping.ddxXZ, scaleOffset.xy * uvMapping.ddyXZ)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset)) );
val += uvMapping.triplanarWeights.z
* ( useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), lodOrBias.z)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), lodOrBias.z)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), triDdx[2], triDdy[2])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), scaleOffset.xy * uvMapping.ddxXY, scaleOffset.xy * uvMapping.ddyXY)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset)) );
return val;
#else
return useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), lodOrBias.x)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), lodOrBias.x)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), triDdx[0], triDdy[0])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), scaleOffset.xy * uvMapping.ddxBase, scaleOffset.xy * uvMapping.ddyBase)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset));
#endif
}
// Normal map sampling requires special care especially for triplanar, we will use gradients for that.
// Also, AxF normal maps are encoded on 3 channels (xyz) but are still tangent space.
// Make sure useLod is used statically!
// Note that scaleOffset are the texture specific ones, not the main material ones!
float3 AxFSampleTexture2DNormalAsSurfaceGrad(TEXTURE2D_PARAM(textureName, samplerName), float4 scaleOffset, TextureUVMapping uvMapping,
int lodBiasOrGrad = 0, float3 lodOrBias = 0, float3x2 triDdx = (float3x2)0, float3x2 triDdy = (float3x2)0)
{
float scale = 1.0;
bool useLod = lodBiasOrGrad == 1;
bool useBias = lodBiasOrGrad == 2;
bool useGrad = lodBiasOrGrad == 3;
bool useCachedDdxDdy = false;
#ifdef AXF_REUSE_SCREEN_DDXDDY
useCachedDdxDdy = true;
#endif
#ifdef _MAPPING_TRIPLANAR
float2 derivXplane;
float2 derivYPlane;
float2 derivZPlane;
float4 packedNormal;
derivXplane = derivYPlane = derivZPlane = float2(0.0, 0.0);
// UnpackDerivativeNormalRGB will unpack an RGB tangent space normal map and output a corresponding height map gradient
// (We will sum those to get a volume gradient and from it a surface gradient (and/or a final normal). Both have 3 coordinates)
packedNormal = useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), lodOrBias.x)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), lodOrBias.x)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), triDdx[0], triDdy[0])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset), scaleOffset.xy * uvMapping.ddxZY, scaleOffset.xy * uvMapping.ddyZY)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvZY, scaleOffset));
derivXplane = uvMapping.triplanarWeights.x * AXF_DERIVATIVE_NORMAL(packedNormal, scale);
packedNormal = useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), lodOrBias.y)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), lodOrBias.y)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), triDdx[1], triDdy[1])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset), scaleOffset.xy * uvMapping.ddxXZ, scaleOffset.xy * uvMapping.ddyXZ)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXZ, scaleOffset));
derivYPlane = uvMapping.triplanarWeights.y * AXF_DERIVATIVE_NORMAL(packedNormal, scale);
packedNormal = useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), lodOrBias.z)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), lodOrBias.z)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), triDdx[2], triDdy[2])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset), scaleOffset.xy * uvMapping.ddxXY, scaleOffset.xy * uvMapping.ddyXY)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvXY, scaleOffset));
derivZPlane = uvMapping.triplanarWeights.z * AXF_DERIVATIVE_NORMAL(packedNormal, scale);
// Important note! See SurfaceGradientFromTriplanarProjection:
// Tiling scales should NOT be negative!
// Assume derivXplane, derivYPlane and derivZPlane sampled using (z,y), (z,x) and (x,y) respectively.
float3 volumeGrad = float3(derivZPlane.x + derivYPlane.y, derivZPlane.y + derivXplane.y, derivXplane.x + derivYPlane.x);
float3 surfaceGrad = SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad);
// We don't need to process further operation on the gradient, but we dont resolve it to a normal immediately:
// ie by doing return SurfaceGradientResolveNormal(uvMapping.vertexNormalWS, surfaceGrad);
// This is because we use GetNormalWS() later which with #define SURFACE_GRADIENT, expects a surface gradient.
return surfaceGrad;
#else
// No triplanar: in that case, just sample the texture, but also unpacks it as a surface gradient! See comment above
float4 packedNormal = useLod ? SAMPLE_TEXTURE2D_LOD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), lodOrBias.x)
: useBias ? SAMPLE_TEXTURE2D_BIAS(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), lodOrBias.x)
: useGrad ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), triDdx[0], triDdy[0])
: useCachedDdxDdy ? SAMPLE_TEXTURE2D_GRAD(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset), scaleOffset.xy * uvMapping.ddxBase, scaleOffset.xy * uvMapping.ddyBase)
: SAMPLE_TEXTURE2D(textureName, samplerName, AXF_TRANSFORM_TEXUV(uvMapping.uvBase, scaleOffset));
float2 deriv = AXF_DERIVATIVE_NORMAL(packedNormal, scale);
#ifndef _MAPPING_PLANAR
// No planar mapping, in that case, just use the generated (or simply cached if using uv0) TBN:
return SurfaceGradientFromTBN(deriv, uvMapping.vertexTangentWS, uvMapping.vertexBitangentWS);
#else
float3 volumeGrad;
// We will use the mapping selector mask to know which plane we used.
// This allows us to properly build the volume gradient:
if (_MappingMask.x == 1.0) // uvZY
volumeGrad = float3(0.0, deriv.y, deriv.x);
else if (_MappingMask.y == 1.0) // uvXZ
volumeGrad = float3(deriv.y, 0.0, deriv.x);
else if (_MappingMask.z == 1.0) // uvXY
volumeGrad = float3(deriv.x, deriv.y, 0.0);
return SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad);
#endif // if not _MAPPING_PLANAR
#endif // if triplanar.
}
#define AXF_SAMPLE_TEXTURE2D(name, uvMapping) AxfSampleTexture2D(name, sampler##name, name##_SO, uvMapping)
#define AXF_SAMPLE_SMP_TEXTURE2D(name, samplername, uvMapping) AxfSampleTexture2D(name, samplername, name##_SO, uvMapping)
#define AXF_SAMPLE_SMP_TEXTURE2D_LOD(name, samplername, lod, uvMapping) AxfSampleTexture2D(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_LOD, lod)
#define AXF_SAMPLE_SMP_TEXTURE2D_BIAS(name, samplername, bias, uvMapping) AxfSampleTexture2D(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_BIAS, bias)
#ifdef _MAPPING_TRIPLANAR
#define AXF_SAMPLE_SMP_TEXTURE2D_GRAD(name, samplername, triddx, triddy, uvMapping) AxfSampleTexture2D(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_GRAD, /*unused*/(float3)0, triddx, triddy)
#else
#define AXF_SAMPLE_SMP_TEXTURE2D_GRAD(name, samplername, vddx, vddy, uvMapping) AxfSampleTexture2D(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_GRAD, /*unused*/(float3)0, float3x2(vddx, (float2)0, (float2)0), float3x2(vddy, (float2)0, (float2)0))
#endif
#define AXF_SAMPLE_TEXTURE2D_NORMAL_AS_GRAD(name, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, sampler##name, name##_SO, uvMapping)
#define AXF_SAMPLE_SMP_TEXTURE2D_NORMAL_AS_GRAD(name, samplername, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, samplername, name##_SO, uvMapping)
#define AXF_SAMPLE_SMP_TEXTURE2D_LOD_NORMAL_AS_GRAD(name, samplername, lod, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_LOD, lod)
#define AXF_SAMPLE_SMP_TEXTURE2D_BIAS_NORMAL_AS_GRAD(name, samplername, bias, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_BIAS, bias)
#ifdef _MAPPING_TRIPLANAR
#define AXF_SAMPLE_SMP_TEXTURE2D_GRAD_NORMAL_AS_GRAD(name, samplername, triddx, triddy, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_GRAD, /*unused*/(float3)0, triddx, triddy)
#else
#define AXF_SAMPLE_SMP_TEXTURE2D_GRAD_NORMAL_AS_GRAD(name, samplername, vddx, vddy, uvMapping) AxFSampleTexture2DNormalAsSurfaceGrad(name, samplername, name##_SO, uvMapping, /*lodBiasOrGrad*/ AXF_SAMPLE_USE_GRAD, /*unused*/(float3)0, float3x2(vddx, (float2)0, (float2)0), float3x2(vddy, (float2)0, (float2)0))
#endif
float2 TileFlakesUV(float2 flakesUV)
{
// Create mirrored UVs to hide flakes tiling
// TODO_FLAKES: this isn't tiling!
if ((int(flakesUV.y) & 1) == 0)
flakesUV.x += 0.5;
else if ((uint(1000.0 + flakesUV.x) % 3) == 0)
flakesUV.y = 1.0 - flakesUV.y;
else
flakesUV.x = 1.0 - flakesUV.x;
return flakesUV;
}
void SetFlakesSurfaceData(TextureUVMapping uvMapping, inout SurfaceData surfaceData, RayCone rayCone)
{
surfaceData.flakesDdxZY = surfaceData.flakesDdyZY = surfaceData.flakesDdxXZ = surfaceData.flakesDdyXZ =
surfaceData.flakesDdxXY = surfaceData.flakesDdyXY = 0;
float2 scales = AXF_GET_SINGLE_SCALE_OFFSET(_CarPaint2_BTFFlakeMap).xy; // this is used when raytracing: scales, texelSize, rayCone
float2 texelSize = AXF_TEXSIZE_FROM_NAME(_CarPaint2_BTFFlakeMap);
#ifdef _MAPPING_TRIPLANAR
float2 uv;
uv = AXF_TRANSFORM_TEXUV_BYNAME(uvMapping.uvZY, _CarPaint2_BTFFlakeMap);
//surfaceData.flakesMipLevelZY = CALCULATE_TEXTURE2D_LOD(_CarPaint2_BTFFlakeMap, sampler_CarPaint2_BTFFlakeMap, uv);
surfaceData.flakesMipLevelZY = AXF_CALCULATE_TEXTURE2D_LOD(_CarPaint2_BTFFlakeMap, sampler_CarPaint2_BTFFlakeMap, uv, uvMapping.ddxZY, uvMapping.ddyZY, scales, texelSize, rayCone);
#ifndef FLAKES_TILE_BEFORE_SCALE
surfaceData.flakesUVZY = TileFlakesUV(uv);
#else
surfaceData.flakesUVZY = AXF_TRANSFORM_TEXUV_BYNAME(TileFlakesUV(uvMapping.uvZY), _CarPaint2_BTFFlakeMap);
#endif
uv = AXF_TRANSFORM_TEXUV_BYNAME(uvMapping.uvXZ, _CarPaint2_BTFFlakeMap);
surfaceData.flakesMipLevelXZ = AXF_CALCULATE_TEXTURE2D_LOD(_CarPaint2_BTFFlakeMap, sampler_CarPaint2_BTFFlakeMap, uv, uvMapping.ddxXZ, uvMapping.ddyXZ, scales, texelSize, rayCone);
#ifndef FLAKES_TILE_BEFORE_SCALE
surfaceData.flakesUVXZ = TileFlakesUV(uv);
#else
surfaceData.flakesUVXZ = AXF_TRANSFORM_TEXUV_BYNAME(TileFlakesUV(uvMapping.uvXZ), _CarPaint2_BTFFlakeMap);
#endif
uv = AXF_TRANSFORM_TEXUV_BYNAME(uvMapping.uvXY, _CarPaint2_BTFFlakeMap);
surfaceData.flakesMipLevelXY = AXF_CALCULATE_TEXTURE2D_LOD(_CarPaint2_BTFFlakeMap, sampler_CarPaint2_BTFFlakeMap, uv, uvMapping.ddxXY, uvMapping.ddyXY, scales, texelSize, rayCone);
#ifndef FLAKES_TILE_BEFORE_SCALE
surfaceData.flakesUVXY = TileFlakesUV(uv);
#else
surfaceData.flakesUVXY = AXF_TRANSFORM_TEXUV_BYNAME(TileFlakesUV(uvMapping.uvXY), _CarPaint2_BTFFlakeMap);
#endif
surfaceData.flakesTriplanarWeights = uvMapping.triplanarWeights;
#ifdef FLAKES_USE_DDXDDY
// Filling surfaceData.flakesDdx* to nonzero values will automatically ignore surfaceData.flakesMipLevel*
// and the compiler will optimize them out (see SampleFlakes in AxF.hlsl)
surfaceData.flakesDdxZY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddxZY, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdyZY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddyZY, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdxXZ = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddxXZ, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdyXZ = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddyXZ, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdxXY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddxXY, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdyXY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddyXY, _CarPaint2_BTFFlakeMap);
#endif
#else // TRIPLANAR
float2 uv;
// NOTE: When not triplanar UVZY has one uv set or one planar coordinate set,
// and this planar coordinate set isn't necessarily ZY, we just reuse this field
// as a common one.
uv = AXF_TRANSFORM_TEXUV_BYNAME(uvMapping.uvBase, _CarPaint2_BTFFlakeMap);
surfaceData.flakesMipLevelZY = AXF_CALCULATE_TEXTURE2D_LOD(_CarPaint2_BTFFlakeMap, sampler_CarPaint2_BTFFlakeMap, uv, uvMapping.ddxBase, uvMapping.ddyBase, scales, texelSize, rayCone);
#ifndef FLAKES_TILE_BEFORE_SCALE
surfaceData.flakesUVZY = TileFlakesUV(uv);
#else
surfaceData.flakesUVZY = AXF_TRANSFORM_TEXUV_BYNAME(TileFlakesUV(uvMapping.uvBase), _CarPaint2_BTFFlakeMap);
#endif
#ifdef FLAKES_USE_DDXDDY
// Filling surfaceData.flakesDdx* to nonzero values will automatically ignore surfaceData.flakesMipLevel*
// and the compiler will optimize them out (see SampleFlakes in AxF.hlsl)
surfaceData.flakesDdxZY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddxBase, _CarPaint2_BTFFlakeMap);
surfaceData.flakesDdyZY = AXF_SCALE_DDXDDY_BYNAME(uvMapping.ddyBase, _CarPaint2_BTFFlakeMap);
#endif
surfaceData.flakesUVXZ = surfaceData.flakesUVXY = 0;
surfaceData.flakesMipLevelXZ = surfaceData.flakesMipLevelXY = 0;
surfaceData.flakesTriplanarWeights = 0;
#endif
}
#ifndef SHADER_STAGE_RAY_TRACING
void ApplyDecalToSurfaceData(DecalSurfaceData decalSurfaceData, float3 vtxNormal, inout SurfaceData surfaceData)
{
#if defined(_AXF_BRDF_TYPE_SVBRDF) || defined(_AXF_BRDF_TYPE_CAR_PAINT) // Not implemented for BTF
// using alpha compositing https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch23.html
surfaceData.diffuseColor.xyz = surfaceData.diffuseColor.xyz * decalSurfaceData.baseColor.w + decalSurfaceData.baseColor.xyz;
#ifdef _AXF_BRDF_TYPE_SVBRDF
surfaceData.clearcoatColor.xyz = surfaceData.clearcoatColor.xyz * decalSurfaceData.baseColor.w + decalSurfaceData.baseColor.xyz;
#endif
// Always test the normal as we can have decompression artifact
if (decalSurfaceData.normalWS.w < 1.0)
{
// Affect both normal and clearcoat normal
surfaceData.normalWS.xyz = SafeNormalize(surfaceData.normalWS.xyz * decalSurfaceData.normalWS.w + decalSurfaceData.normalWS.xyz);
surfaceData.clearcoatNormalWS = SafeNormalize(surfaceData.clearcoatNormalWS.xyz * decalSurfaceData.normalWS.w + decalSurfaceData.normalWS.xyz);
}
#ifdef DECALS_4RT // only smoothness in 3RT mode
#ifdef _AXF_BRDF_TYPE_SVBRDF
if (decalSurfaceData.MAOSBlend.x < 1.0)
{
float3 decalSpecularColor = ComputeFresnel0((decalSurfaceData.baseColor.w < 1.0) ? decalSurfaceData.baseColor.xyz : float3(1.0, 1.0, 1.0), decalSurfaceData.mask.x, DEFAULT_SPECULAR_VALUE);
surfaceData.specularColor = surfaceData.specularColor * decalSurfaceData.MAOSBlend.x + decalSpecularColor * (1.0f - decalSurfaceData.MAOSBlend.x);
}
#endif
surfaceData.clearcoatIOR = lerp(1.0, surfaceData.clearcoatIOR, decalSurfaceData.MAOSBlend.x); // Transition to IOR 1.0 with increase decal coverage (i.e decrease of decalSurfaceData.MAOSBlend.x value)
// Note:There is no ambient occlusion with AxF material
#endif
if (decalSurfaceData.mask.w < 1.0)
{
surfaceData.specularLobe.x = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.x) * decalSurfaceData.mask.w + decalSurfaceData.mask.z);
surfaceData.specularLobe.y = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.y) * decalSurfaceData.mask.w + decalSurfaceData.mask.z);
#ifdef _AXF_BRDF_TYPE_CAR_PAINT
surfaceData.specularLobe.z = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.z) * decalSurfaceData.mask.w + decalSurfaceData.mask.z);
#endif
}
#endif
}
#endif //...#ifndef SHADER_STAGE_RAY_TRACING
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData RAY_TRACING_OPTIONAL_PARAMETERS)
{
#if !defined(SHADER_STAGE_RAY_TRACING)
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group
LODDitheringTransition(ComputeFadeMaskSeed(V, posInput.positionSS), unity_LODFade.x);
#endif
#endif
#ifdef _DOUBLESIDED_ON
float3 doubleSidedConstants = _DoubleSidedConstants.xyz;
#else
float3 doubleSidedConstants = float3(1.0, 1.0, 1.0);
#endif
ApplyDoubleSidedFlipOrMirror(input, doubleSidedConstants); // Apply double sided flip on the vertex normal
// Note that in uvMapping, the main scaling _Material_SO.xy has been applied:
TextureUVMapping uvMapping;
InitTextureUVMapping(input, V, uvMapping, GETSURFACEANDBUILTINDATA_RAYCONE_PARAM);
ZERO_INITIALIZE(SurfaceData, surfaceData);
// Needed for raytracing.
// TODO: should just modify FitToStandardLit in ShaderPassRaytracingGBuffer.hlsl and callee
// to have "V" (from -incidentDir)
surfaceData.viewWS = V;
float alpha = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_AlphaMap, sampler_SVBRDF_AlphaMap, uvMapping).x;
#ifdef _ALPHATEST_ON
float alphaCutoff = _AlphaCutoff;
#if (SHADERPASS == SHADERPASS_SHADOWS) || (SHADERPASS == SHADERPASS_RAYTRACING_VISIBILITY)
alphaCutoff = _UseShadowThreshold ? _AlphaCutoffShadow : alphaCutoff;
#endif
GENERIC_ALPHA_TEST(alpha, alphaCutoff);
#endif
surfaceData.ambientOcclusion = 1.0;
surfaceData.specularOcclusion = 1.0;
surfaceData.specularLobe = 0;
//-----------------------------------------------------------------------------
// _AXF_BRDF_TYPE_SVBRDF
//-----------------------------------------------------------------------------
#ifdef _AXF_BRDF_TYPE_SVBRDF
surfaceData.diffuseColor = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_DiffuseColorMap, sampler_SVBRDF_DiffuseColorMap, uvMapping).xyz;
surfaceData.specularColor = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_SpecularColorMap, sampler_SVBRDF_SpecularColorMap, uvMapping).xyz;
surfaceData.specularLobe.xy = _SVBRDF_SpecularLobeMapScale * AxFGetRoughnessFromSpecularLobeTexture(
AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_SpecularLobeMap, sampler_SVBRDF_SpecularLobeMap, uvMapping).xy);
// The AxF models include both a general coloring term that they call "specular color" while the f0 is actually another term,
// seemingly always scalar:
surfaceData.fresnelF0 = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_FresnelMap, sampler_SVBRDF_FresnelMap, uvMapping).x;
surfaceData.height_mm = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_HeightMap, sampler_SVBRDF_HeightMap, uvMapping).x * _SVBRDF_HeightMapMaxMM;
// Our importer range remaps the [-HALF_PI, HALF_PI) range to [0,1). We map back here:
surfaceData.anisotropyAngle =
HALF_PI * (2.0 * AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_AnisoRotationMap, sampler_SVBRDF_AnisoRotationMap, uvMapping).x - 1.0);
surfaceData.clearcoatColor = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_ClearcoatColorMap, sampler_SVBRDF_ClearcoatColorMap, uvMapping).xyz;
// The importer transforms the IOR to an f0, we map it back here as an IOR clamped under at 1.0
// TODO: if we're reusing float textures anyway, we shouldn't need the normalization that transforming to an f0 provides.
float clearcoatF0 = AXF_SAMPLE_SMP_TEXTURE2D(_SVBRDF_ClearcoatIORMap, sampler_SVBRDF_ClearcoatIORMap, uvMapping).x;
float sqrtF0 = sqrt(clearcoatF0);
surfaceData.clearcoatIOR = max(1.0, (1.0 + sqrtF0) / (1.00001 - sqrtF0)); // We make sure it's working for F0=1
//
// TBN
//
// Note: since SURFACE_GRADIENT is enabled, resolve is done with input.tangentToWorld[2] in GetNormalWS(),
// and uvMapping uses that as vertexNormalWS.
//Normal sampling:
GetNormalWS(input, AXF_SAMPLE_SMP_TEXTURE2D_NORMAL_AS_GRAD(_SVBRDF_NormalMap, sampler_SVBRDF_NormalMap, uvMapping).xyz, surfaceData.normalWS, doubleSidedConstants);
GetNormalWS(input, AXF_SAMPLE_SMP_TEXTURE2D_NORMAL_AS_GRAD(_ClearcoatNormalMap, sampler_ClearcoatNormalMap, uvMapping).xyz, surfaceData.clearcoatNormalWS, doubleSidedConstants);
// Useless for SVBRDF, will be optimized out
//SetFlakesSurfaceData(uvMapping, surfaceData);
//-----------------------------------------------------------------------------
// _AXF_BRDF_TYPE_CAR_PAINT
//-----------------------------------------------------------------------------
#elif defined(_AXF_BRDF_TYPE_CAR_PAINT)
surfaceData.diffuseColor = _CarPaint2_CTDiffuse;
surfaceData.clearcoatIOR = max(1.001, _CarPaint2_ClearcoatIOR); // Can't be exactly 1 otherwise the precise fresnel divides by 0!
surfaceData.specularLobe = _CarPaint2_CTSpreads.xyz; // We may want to modify these (eg for Specular AA)
surfaceData.normalWS = input.tangentToWorld[2].xyz;
GetNormalWS(input, AXF_SAMPLE_SMP_TEXTURE2D_NORMAL_AS_GRAD(_ClearcoatNormalMap, sampler_ClearcoatNormalMap, uvMapping).xyz, surfaceData.clearcoatNormalWS, doubleSidedConstants);
SetFlakesSurfaceData(uvMapping, surfaceData, GETSURFACEANDBUILTINDATA_RAYCONE_PARAM);
surfaceData.clearcoatColor = 1;
// Useless for car paint BSDF
surfaceData.specularColor = 0;
surfaceData.fresnelF0 = 0;
surfaceData.height_mm = 0;
surfaceData.anisotropyAngle = 0;
#endif
// TODO
// Assume same xyz encoding for AxF bent normal as other normal maps.
//float3 bentNormalWS;
//GetNormalWS(input, 2.0 * SAMPLE_TEXTURE2D(_BentNormalMap, sampler_BentNormalMap, UV0).xyz - 1.0, bentNormalWS, doubleSidedConstants);
float perceptualRoughness = RoughnessToPerceptualRoughness(GetScalarRoughness(surfaceData.specularLobe));
surfaceData.perceptualSmoothness = PerceptualRoughnessToPerceptualSmoothness(perceptualRoughness);
//TODO
//#if defined(_SPECULAR_OCCLUSION_FROM_BENT_NORMAL_MAP)
// Note: we use normalWS as it will always exist and be equal to clearcoatNormalWS if there's no coat
// (otherwise we do SO with the base lobe, might be wrong depending on way AO is computed, will be wrong either way with a single non-lobe specific value)
//surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAO(V, bentNormalWS, surfaceData.normalWS, surfaceData.ambientOcclusion, perceptualRoughness);
//#endif
#if !defined(_SPECULAR_OCCLUSION_NONE)
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(ClampNdotV(dot(surfaceData.normalWS, V)), surfaceData.ambientOcclusion, perceptualRoughness);
#endif
// Propagate the geometry normal
surfaceData.geomNormalWS = input.tangentToWorld[2];
// Finalize tangent space
surfaceData.tangentWS = uvMapping.vertexTangentWS;
// TODOTODO:
// This is crappy: anisotropy rotation don't mix triplanar style like scalar values because of what it represents. That's why in HDRP we use
// tangent space tangent vector maps and triplanar sample those as we do normals in the surface gradients framework!
// Better to rebuild a gradient in the proper space from each rotation, combine those gradients as normals and resolve here.
if (HasAnisotropy())
{
float3 tangentTS = float3(1, 0, 0);
// We will keep anisotropyAngle in surfaceData for now for debug info, register will be freed
// anyway by the compiler (never used again after this)
sincos(surfaceData.anisotropyAngle, tangentTS.y, tangentTS.x);
float3x3 tbn = float3x3(uvMapping.vertexTangentWS, uvMapping.vertexBitangentWS, uvMapping.vertexNormalWS);
surfaceData.tangentWS = TransformTangentToWorld(tangentTS, input.tangentToWorld);
}
#if HAVE_DECALS
if (_EnableDecals)
{
// Both uses and modifies 'surfaceData.normalWS'.
DecalSurfaceData decalSurfaceData = GetDecalSurfaceData(posInput, input, alpha);
ApplyDecalToSurfaceData(decalSurfaceData, input.tangentToWorld[2], surfaceData);
}
#endif
surfaceData.tangentWS = Orthonormalize(surfaceData.tangentWS, surfaceData.normalWS);
// Instead of
// surfaceData.biTangentWS = Orthonormalize(input.tangentToWorld[1], surfaceData.normalWS),
// make AxF follow what we do in other HDRP shaders for consistency: use the
// cross product to finish building the TBN frame and thus get a frame matching
// the handedness of the world space (tangentToWorld can be passed right handed while
// Unity's WS is left handed, so this makes a difference here).
#if defined(_ENABLE_GEOMETRIC_SPECULAR_AA) && !defined(SHADER_STAGE_RAY_TRACING)
// Specular AA for geometric curvature
surfaceData.specularLobe.x = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.x), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold));
surfaceData.specularLobe.y = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.y), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold));
#if defined(_AXF_BRDF_TYPE_CAR_PAINT)
surfaceData.specularLobe.z = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.z), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold));
#endif
#endif
#if defined(DEBUG_DISPLAY) && !defined(SHADER_STAGE_RAY_TRACING)
if (_DebugMipMapMode != DEBUGMIPMAPMODE_NONE)
{
// Not debug streaming information with AxF (this should never be stream)
surfaceData.diffuseColor = float3(0.0, 0.0, 0.0);
}
// We need to call ApplyDebugToSurfaceData after filling the surfarcedata and before filling builtinData
// as it can modify attribute use for static lighting
ApplyDebugToSurfaceData(input.tangentToWorld, surfaceData);
#endif
// -------------------------------------------------------------
// Builtin Data:
// -------------------------------------------------------------
// No back lighting with AxF
InitBuiltinData(posInput, alpha, surfaceData.normalWS, surfaceData.normalWS, input.texCoord1, input.texCoord2, builtinData);
#ifdef _ALPHATEST_ON
// Used for sharpening by alpha to mask
builtinData.alphaClipTreshold = _AlphaCutoff;
#endif
PostInitBuiltinData(V, posInput, surfaceData, builtinData);
RAY_TRACING_OPTIONAL_ALPHA_TEST_PASS
}