float4 _TransmissionTints[SSS_N_PROFILES]; // RGB = 1/4 * color, A = unused
CBUFFER_END
// General constant
#define MIN_N_DOT_V 0.0001 // The minimum value of 'NdotV'
//-----------------------------------------------------------------------------
// Helper for cheap screen space raycasting
//-----------------------------------------------------------------------------
struct PreLightData
{
// General
float NdotV; // Geometric version (could be negative)
float NdotV;
// GGX
float partLambdaV;
{
PreLightData preLightData;
float3 N;
float NdotV;
N = bsdfData.coatNormalWS;
NdotV = saturate(dot(N, V) );
preLightData.coatNdotV = NdotV;
preLightData.coatNdotV = dot(bsdfData.coatNormalWS, V );
// In the case of IBL we want shift a bit the normal that are not toward the viewver to reduce artifact
float3 coatIblNormalWS = GetViewShiftedNormal(bsdfData.coatNormalWS, V, preLightData.coatNdotV, MIN_N_DOT_V); // Use non-clamped NdotV
preLightData.coatIblDirWS = reflect(-V, coatIblNormalWS);
float coatNdotV = max(preLightData.coatNdotV, MIN_N_DOT_V); // Use the modified (clamped) version
preLightData.coatIblDirWS = reflect(-V, N);
float theta = FastACosPos(coat NdotV);
float theta = FastACosPos(NdotV);
float2 uv = LTC_LUT_OFFSET + LTC_LUT_SCALE * float2(0.0, theta * INV_HALF_PI); // Use Roughness of 0.0 for clearCoat roughness
// Get the inverse LTC matrix for GGX
preLightData.ltcClearCoatFresnelTerm = preLightData.coatFresnel0 * ltcClearCoatFresnelMagnitudeDiff + ltcClearCoatFresnelMagnitude;
// TODO: Convert the area light with respect to Fresnel transmission
float3 N = bsdfData.coatNormalWS; // TODO : check with Laurentb
preLightData.refractV = ClearCoatTransform(V, bsdfData.coatNormalWS, ieta);
preLightData.refractV = ClearCoatTransform(V, N, ieta);
preLightData.NdotV = dot(bsdfData.normalWS, V); // Store the unaltered (geometric) version
N = bsdfData.normalWS;
NdotV = saturate(dot(N, V));
preLightData.NdotV = NdotV;
// In the case of IBL we want shift a bit the normal that are not toward the viewver to reduce artifact
float3 iblNormalWS = GetViewShiftedNormal(bsdfData.normalWS, V, preLightData.NdotV, MIN_N_DOT_V); // Use non-clamped NdotV
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
// GGX aniso
if (bsdfData.materialId == MATERIALID_LIT_ANISO && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_ANISO))
float3 grainDirWS = (bsdfData.anisotropy >= 0) ? bsdfData.bitangentWS : bsdfData.tangentWS;
// Reduce stretching for (perceptualRoughness < 0.2).
float stretch = abs(bsdfData.anisotropy) * saturate(5 * bsdfData.perceptualRoughness);
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NdotV) and use iblNormalWS
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NdotV) and use 'an isoI blNormalWS'
float3 anisoIblNormalWS = GetAnisotropicModifiedNormal(grainDirWS, ibl NormalWS , V, stretch);
float3 anisoIblNormalWS = GetAnisotropicModifiedNormal(grainDirWS, N, V, stretch);
iblR = reflect(-V, anisoIblNormalWS);
}
else // GGX iso
preLightData.partLambdaV = GetSmithJointGGXPartLambdaV(NdotV, bsdfData.roughness);
iblR = reflect(-V, ibl NormalWS );
iblR = reflect(-V, N);
}
float reflectivity;
float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
float shininess = Sqr(preLightData.ieta) * (2.0 / Sqr(roughness) - 2.0);
roughness = sqrt(2.0 / (shininess + 2.0));
preLightData.iblDirWS = GetSpecularDominantDir(ibl NormalWS , iblR, roughness, NdotV);
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, roughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(RoughnessToPerceptualRoughness(roughness));
}
else
iblPerceptualRoughness = bsdfData.perceptualRoughness;
}
preLightData.iblDirWS = GetSpecularDominantDir(ibl NormalWS , iblR, iblRoughness, NdotV);
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, iblRoughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(iblPerceptualRoughness);
}
preLightData.ltcTransformSpecular._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0);
// Construct a right-handed view-dependent orthogonal basis around the normal
preLightData.orthoBasisViewNormal[0] = normalize(V - bsdfData.normalWS * preLightData. NdotV);
preLightData.orthoBasisViewNormal[2] = bsdfData.normalWS ;
preLightData.orthoBasisViewNormal[0] = normalize(V - N * NdotV);
preLightData.orthoBasisViewNormal[2] = N ;
preLightData.orthoBasisViewNormal[1] = normalize(cross(preLightData.orthoBasisViewNormal[2], preLightData.orthoBasisViewNormal[0]));
float3 ltcMagnitude = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_MULTI_GGX_FRESNEL_DISNEY_DIFFUSE_INDEX, 0).rgb;
float NdotL = saturate(dot(bsdfData.coatNormalWS, L));
float NdotV = preLightData.coatNdotV;
float LdotV = dot(L, V);
float invLenLV = rsqrt(abs(2 * LdotV + 2 ));
float invLenLV = rsqrt(max(2 * LdotV + 2, FLT_SMALL ));
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);
float invLenLV = rsqrt(abs(2 * LdotV + 2)); // invLenLV = rcp(length(L + V))
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);
NdotV = max(NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
F *= F_Schlick(bsdfData.fresnel0, LdotH);
illuminance = Lambert() * wrappedNdotL;
#else
float tNdotL = saturate(-NdotL);
float NdotV = max( preLightData.NdotV, MIN_N_DOT_V) ;
float NdotV = preLightData.NdotV;
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL;
#endif
illuminance = Lambert() * wrappedNdotL;
#else
float tNdotL = saturate(-NdotL);
float NdotV = max( preLightData.NdotV, MIN_N_DOT_V) ;
float NdotV = preLightData.NdotV;
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL;
#endif