// Evaluate the reflectance for a thin-film layer on top of a dielectric medum.
float3 EvalIridescence(float eta_1, float cosTheta1, BSDFData bsdfData)
{
eta_1 = 1.0; // air is 1.0 but for clear coat should be 1.5
// thicknessIridescence unit is micrometer for this equation here. Mean 0.5 is 500nm.
float Dinc = 2.0 * bsdfData.iorIridescence * bsdfData.thicknessIridescence;
// Force eta_2 -> eta_1 when Dinc -> 0.0
// Compound terms
float3 R123 = R12 * R23;
float3 r123 = sqrt(R123);
float3 Rs = Sq(T121) * R23 / (float3(1.0, 1.0, 1.0) - R123);
float3 Rs = Sq(T121) * R23 / (float3(1.0, 1.0, 1.0) - R123);
// Reflectance term for m = 0 (DC term amplitude)
float3 C0 = R12 + Rs;
}
// Convert back to RGB reflectance
//I = clamp(mul(I, XYZ_TO_RGB), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0));
//I = clamp(mul(I, XYZ_TO_RGB), float3(0.0, 0.0, 0.0), float3(1.0, 1.0, 1.0));
//I = mul(XYZ_TO_RGB, I);
return I;
bsdfData.bitangentWS = bitangentWS;
}
void FillMaterialIridescence(float3 V, float coatMask, float ior, float thickness, inout BSDFData bsdfData)
void FillMaterialIridescence(float ior, float thickness, inout BSDFData bsdfData)
float topIor = lerp(1.0, CLEAR_COAT_IOR, coatMask);
// HACK: Use the reflected direction to specify the Fresnel coefficient for pre-convolved envmaps
// TODO: Take into account roughness for peak ?
// TODO: take into account clear coat...
float NdotV = sqrt(1.0 + Sq(1.0 / ior) * (Sq(dot(bsdfData.normalWS, V)) - 1.0));
bsdfData.fresnel0 = EvalIridescence(topIor, NdotV, bsdfData);
}
// Note: this modify the parameter perceptualRoughness and fresnel0, so they need to be setup
float coatRoughnessScale = Sq(ieta);
float sigma = RoughnessToVariance(PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness));
bsdfData.perceptualRoughness = RoughnessToPerceptualRoughness(VarianceToRoughness(sigma * coatRoughnessScale));
// Fresnel0 is deduced from interface between air and material (Assume to be 1.5 in Unity, or a metal).
// but here we go from clear coat (1.5) to material, we need to update fresnel0
// Note: Schlick is a poor approximation of Fresnel when ieta is 1 (1.5 / 1.5), schlick target 1.4 to 2.2 IOR.
bsdfData.fresnel0 = lerp(bsdfData.fresnel0, ConvertF0ForAirInterfaceToF0ForClearCoat15(bsdfData.fresnel0), coatMask);
}
void FillMaterialTransparencyData(float3 baseColor, float metallic, float ior, float3 transmittanceColor, float atDistance, float thickness, float transmittanceMask, inout BSDFData bsdfData)
// conversion function for forward
//-----------------------------------------------------------------------------
BSDFData ConvertSurfaceDataToBSDFData(float3 V, uint2 positionSS, SurfaceData surfaceData)
BSDFData ConvertSurfaceDataToBSDFData(SurfaceData surfaceData)
{
BSDFData bsdfData;
ZERO_INITIALIZE(BSDFData, bsdfData);
if (HasFeatureFlag(surfaceData.materialFeatures, MATERIALFEATUREFLAGS_LIT_IRIDESCENCE))
{
// Modify fresnel0
FillMaterialIridescence(V, surfaceData.coatMask, surfaceData.iorIridescence, surfaceData.thicknessIridescence, bsdfData);
FillMaterialIridescence(surfaceData.iorIridescence, surfaceData.thicknessIridescence, bsdfData);
// Modify fresnel0 and perceptualRoughness
// Modify perceptualRoughness
FillMaterialClearCoatData(surfaceData.coatMask, bsdfData);
}
// Encode SurfaceData (BSDF parameters) into GBuffer
// Must be in sync with RT declared in HDRenderPipeline.cs ::Rebuild
void EncodeIntoGBuffer( float3 V, uint2 positionSS, SurfaceData surfaceData,
void EncodeIntoGBuffer( SurfaceData surfaceData,
uint2 positionSS,
out GBufferType0 outGBuffer0,
out GBufferType1 outGBuffer1,
out GBufferType2 outGBuffer2,
// If you're not using the feature classification system, pass UINT_MAX.
// Also, see comment in TileVariantToFeatureFlags. When we are the worse case (i.e last variant), we read the featureflags
// from the structured buffer use to generate the indirect draw call. It allow to not go through all branch and the branch is scalar (not VGPR)
uint DecodeFromGBuffer(float3 V, uint2 positionSS, uint tileFeatureFlags, out BSDFData bsdfData, out float3 bakeDiffuseLighting)
uint DecodeFromGBuffer(uint2 positionSS, uint tileFeatureFlags, out BSDFData bsdfData, out float3 bakeDiffuseLighting)
{
// Note: we have ZERO_INITIALIZE the struct, so bsdfData.diffusionProfile == DIFFUSION_PROFILE_NEUTRAL_ID,
// bsdfData.anisotropy == 0, bsdfData.subsurfaceMask == 0, etc...
if (HasFeatureFlag(pixelFeatureFlags, MATERIALFEATUREFLAGS_LIT_IRIDESCENCE))
{
// Range of IOR is 1..2.5
// Modify fresnel0
FillMaterialIridescence(V, coatMask, RemapIor01to25(inGBuffer2.r), inGBuffer2.g, bsdfData);
FillMaterialIridescence(RemapIor01to25(inGBuffer2.r), inGBuffer2.g, bsdfData);
// Modify fresnel0 and perceptualRoughness
// Modify perceptualRoughness
FillMaterialClearCoatData(coatMask, bsdfData);
}
float3 unused;
// Call the regular function, compiler will optimized out everything not used.
// Note that all material feature flag bellow are in the same GBuffer (inGBuffer2) and thus material classification only sample one Gbuffer
return DecodeFromGBuffer(float3(0.0, 0.0, 1.0), positionSS, UINT_MAX, bsdfData, unused);
return DecodeFromGBuffer(positionSS, UINT_MAX, bsdfData, unused);
}
//-----------------------------------------------------------------------------
float transparentSSMipLevel; // mip level of the screen space gaussian pyramid for rough refraction
};
PreLightData GetPreLightData(float3 V, PositionInputs posInput, BSDFData bsdfData)
PreLightData GetPreLightData(float3 V, PositionInputs posInput, inout BSDFData bsdfData)
{
PreLightData preLightData;
ZERO_INITIALIZE(PreLightData, preLightData);
float NdotV = ClampNdotV(preLightData.NdotV);
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_IRIDESCENCE))
{
float viewAngle = NdotV;
float topIor = 1.0; // Default is air
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
topIor = lerp(1.0, CLEAR_COAT_IOR, bsdfData.coatMask);
// HACK: Use the reflected direction to specify the Fresnel coefficient for pre-convolved envmaps
viewAngle = sqrt(1.0 + Sq(1.0 / topIor) * (Sq(dot(bsdfData.normalWS, V)) - 1.0));
}
// TEMP
if (bsdfData.thicknessIridescence != 0.0)
{
bsdfData.fresnel0 = EvalIridescence(topIor, viewAngle, bsdfData);
}
}
// Fresnel0 is deduced from interface between air and material (Assume to be 1.5 in Unity, or a metal).
// but here we go from clear coat (1.5) to material, we need to update fresnel0
// Note: Schlick is a poor approximation of Fresnel when ieta is 1 (1.5 / 1.5), schlick target 1.4 to 2.2 IOR.
bsdfData.fresnel0 = lerp(bsdfData.fresnel0, ConvertF0ForAirInterfaceToF0ForClearCoat15(bsdfData.fresnel0), bsdfData.coatMask);
preLightData.coatPartLambdaV = GetSmithJointGGXPartLambdaV(NdotV, CLEAR_COAT_ROUGHNESS);
preLightData.coatIblR = reflect(-V, N);
preLightData.coatIblF = F_Schlick(CLEAR_COAT_F0, NdotV) * bsdfData.coatMask;