浏览代码

Merge pull request #757 from Unity-Technologies/Update-CheapClearCoat

Refactor clear coat to be more usable in game
/main
GitHub 7 年前
当前提交
74479f14
共有 12 个文件被更改,包括 308 次插入342 次删除
  1. 6
      ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/CommonLighting.hlsl
  2. 57
      ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/CommonMaterial.hlsl
  3. 15
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Material/Lit/LitUI.cs
  4. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/LayeredLit/LayeredLitData.hlsl
  5. 15
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.cs
  6. 53
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.cs.hlsl
  7. 479
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.hlsl
  8. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.shader
  9. 8
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitDataIndividualLayer.hlsl
  10. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitProperties.hlsl
  11. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitReference.hlsl
  12. 3
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitTessellation.shader

6
ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/CommonLighting.hlsl


return real3x3(localX, localY, localZ);
}
// ior is a value between 1.0 and 2.5
real IORToFresnel0(real ior)
{
return Sq((ior - 1.0) / (ior + 1.0));
}
#endif // UNITY_COMMON_LIGHTING_INCLUDED

57
ScriptableRenderPipeline/Core/CoreRP/ShaderLibrary/CommonMaterial.hlsl


return max(roughness, 1.0/1024.0);
}
// 'bsdfData.roughnessT' and 'bsdfData.roughnessB' are clamped, and are meant to be used with analytical lights.
// 'bsdfData.perceptualRoughness' is not clamped, and is meant to be used for IBL.
// If IBL needs the linear roughness value for some reason, it can be computed as follows:
// real roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
void ConvertAnisotropyToRoughness(real perceptualRoughness, real anisotropy, out real roughnessT, out real roughnessB)
{
real roughness = PerceptualRoughnessToRoughness(perceptualRoughness);

roughnessT = roughness * (1 + anisotropy);
roughnessB = roughness * (1 - anisotropy);
}
// Same as ConvertAnisotropyToRoughness but
// roughnessT and roughnessB are clamped, and are meant to be used with punctual and directional lights.
void ConvertAnisotropyToClampRoughness(real perceptualRoughness, real anisotropy, out real roughnessT, out real roughnessB)
{
ConvertAnisotropyToRoughness(perceptualRoughness, anisotropy, roughnessT, roughnessB);
}
// Use with stack BRDF (clear coat / coat)
real roughnessToVariance(real roughness)
{
return 2.0 / Sq(roughness) - 2.0;
}
real varianceToRoughness(real variance)
{
return sqrt(2.0 / (variance + 2.0));
}
// ior is a value between 1.0 and 2.5
// Assume air interface for top
real IORToFresnel0(real ior)
{
return Sq((ior - 1.0) / (ior + 1.0));
}
real IORToFresnel0(real baseIor, real topIor)
{
return Sq((baseIor - topIor) / (baseIor + topIor));
}
// Assume air interface for top
real Fresnel0ToIor(real fresnel0)
{
real sqrtF0 = sqrt(fresnel0);
return (1.0 + sqrtF0) / (1.0 - sqrtF0);
}
// This function is a coarse approximation of computing fresnel0 for a different top than air (here clear coat of IOR 1.5) when we only have fresnel0 with air interface
// This function is equivalent to IORToFresnel0(Fresnel0ToIor(fresnel0), 1.5)
// mean
// real sqrtF0 = sqrt(fresnel0);
// return Sq(1.0 - 5.0 * sqrtF0) / Sq(5.0 - sqrtF0);
// Optimization: Fit of the function (3 mad) for range 0.04 (should return 0), 1 (should return 1)
// return saturate(-0.0256868 + fresnel0 * (0.326846 + (0.978946 - 0.283835 * fresnel0) * fresnel0));
TEMPLATE_1_REAL(Fresnel0ReajustFor15, fresnel0, return saturate(-0.0256868 + fresnel0 * (0.326846 + (0.978946 - 0.283835 * fresnel0) * fresnel0)) )
// same as regular refract except there is not the test for total internal reflection + the vector is flipped for processing
real3 CoatRefract(real3 X, real3 N, real ieta)
{
real XdotN = saturate(dot(N, X));
return ieta * X + (sqrt(1 + ieta * ieta * (XdotN * XdotN - 1)) - ieta * XdotN) * N;
}
// ----------------------------------------------------------------------------

15
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Editor/Material/Lit/LitUI.cs


public static GUIContent thicknessRemapText = new GUIContent("Thickness Remap", "Remaps values of the thickness map from [0, 1] to the specified range.");
// Clear Coat
public static GUIContent coatCoverageText = new GUIContent("Coat Coverage", "Percentage of clear coat coverage");
public static GUIContent coatIORText = new GUIContent("Coat IOR", "IOR of clear coat, value is [0..1] + 1.0. i.e 0.5 is IOR 1.5");
public static GUIContent coatMaskText = new GUIContent("Coat Mask", "attenuate the coating effect (similar to change to IOR of 1");
// Specular color
public static GUIContent specularColorText = new GUIContent("Specular Color", "Specular color (RGB)");

protected MaterialProperty anisotropyMap = null;
protected const string kAnisotropyMap = "_AnisotropyMap";
protected MaterialProperty coatCoverage = null;
protected const string kCoatCoverage = "_CoatCoverage";
protected MaterialProperty coatIOR = null;
protected const string kCoatIOR = "_CoatIOR";
protected MaterialProperty coatMask = null;
protected const string kCoatMask = "_CoatMask";
protected MaterialProperty emissiveColorMode = null;
protected const string kEmissiveColorMode = "_EmissiveColorMode";

anisotropyMap = FindProperty(kAnisotropyMap, props);
// clear coat
coatCoverage = FindProperty(kCoatCoverage, props);
coatIOR = FindProperty(kCoatIOR, props);
coatMask = FindProperty(kCoatMask, props);
// Transparency
refractionMode = FindProperty(kRefractionMode, props, false);

protected void ShaderClearCoatInputGUI()
{
m_MaterialEditor.ShaderProperty(coatCoverage, Styles.coatCoverageText);
m_MaterialEditor.ShaderProperty(coatIOR, Styles.coatIORText);
m_MaterialEditor.ShaderProperty(coatMask, Styles.coatMaskText);
}
protected void ShaderAnisoInputGUI()

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/LayeredLit/LayeredLitData.hlsl


// Init other parameters
surfaceData.anisotropy = 0;
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
surfaceData.coatNormalWS = float3(0.0, 0.0, 0.0);
surfaceData.coatCoverage = 0.0f;
surfaceData.coatIOR = 0.5;
surfaceData.coatMask = 0.0f;
// Transparency parameters
// Use thickness from SSS

15
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.cs


public Vector3 specularColor;
// ClearCoat
[SurfaceDataAttributes("Coat Normal", true)]
public Vector3 coatNormalWS;
[SurfaceDataAttributes("Coat coverage")]
public float coatCoverage;
[SurfaceDataAttributes("Coat IOR")]
public float coatIOR; // Value is [0..1] for artists but the UI will display the value between [1..2]
[SurfaceDataAttributes("Coat mask")]
public float coatMask;
[SurfaceDataAttributes("Indice of refraction")]
[SurfaceDataAttributes("Index of refraction")]
public float ior;
// Reuse thickness from SSS
[SurfaceDataAttributes("Transmittance Color")]

// fold into fresnel0
// ClearCoat
public Vector3 coatNormalWS;
public float coatCoverage;
public float coatIOR; // CoatIOR is in range[1..2] it is surfaceData + 1
public float coatMask;
public float coatRoughness;
// Only in forward
// Transparency

53
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.cs.hlsl


#define DEBUGVIEW_LIT_SURFACEDATA_THICKNESS (1010)
#define DEBUGVIEW_LIT_SURFACEDATA_SUBSURFACE_PROFILE (1011)
#define DEBUGVIEW_LIT_SURFACEDATA_SPECULAR_COLOR (1012)
#define DEBUGVIEW_LIT_SURFACEDATA_COAT_NORMAL_WS (1013)
#define DEBUGVIEW_LIT_SURFACEDATA_COAT_COVERAGE (1014)
#define DEBUGVIEW_LIT_SURFACEDATA_COAT_IOR (1015)
#define DEBUGVIEW_LIT_SURFACEDATA_IOR (1016)
#define DEBUGVIEW_LIT_SURFACEDATA_TRANSMITTANCE_COLOR (1017)
#define DEBUGVIEW_LIT_SURFACEDATA_AT_DISTANCE (1018)
#define DEBUGVIEW_LIT_SURFACEDATA_TRANSMITTANCE_MASK (1019)
#define DEBUGVIEW_LIT_SURFACEDATA_COAT_MASK (1013)
#define DEBUGVIEW_LIT_SURFACEDATA_IOR (1014)
#define DEBUGVIEW_LIT_SURFACEDATA_TRANSMITTANCE_COLOR (1015)
#define DEBUGVIEW_LIT_SURFACEDATA_AT_DISTANCE (1016)
#define DEBUGVIEW_LIT_SURFACEDATA_TRANSMITTANCE_MASK (1017)
//
// UnityEngine.Experimental.Rendering.HDPipeline.Lit+BSDFData: static fields

#define DEBUGVIEW_LIT_BSDFDATA_ENABLE_TRANSMISSION (1044)
#define DEBUGVIEW_LIT_BSDFDATA_USE_THICK_OBJECT_MODE (1045)
#define DEBUGVIEW_LIT_BSDFDATA_TRANSMITTANCE (1046)
#define DEBUGVIEW_LIT_BSDFDATA_COAT_NORMAL_WS (1047)
#define DEBUGVIEW_LIT_BSDFDATA_COAT_COVERAGE (1048)
#define DEBUGVIEW_LIT_BSDFDATA_COAT_IOR (1049)
#define DEBUGVIEW_LIT_BSDFDATA_IOR (1050)
#define DEBUGVIEW_LIT_BSDFDATA_ABSORPTION_COEFFICIENT (1051)
#define DEBUGVIEW_LIT_BSDFDATA_TRANSMITTANCE_MASK (1052)
#define DEBUGVIEW_LIT_BSDFDATA_COAT_MASK (1047)
#define DEBUGVIEW_LIT_BSDFDATA_COAT_ROUGHNESS (1048)
#define DEBUGVIEW_LIT_BSDFDATA_IOR (1049)
#define DEBUGVIEW_LIT_BSDFDATA_ABSORPTION_COEFFICIENT (1050)
#define DEBUGVIEW_LIT_BSDFDATA_TRANSMITTANCE_MASK (1051)
//
// UnityEngine.Experimental.Rendering.HDPipeline.Lit+GBufferMaterial: static fields

float thickness;
int subsurfaceProfile;
float3 specularColor;
float3 coatNormalWS;
float coatCoverage;
float coatIOR;
float coatMask;
float ior;
float3 transmittanceColor;
float atDistance;

bool enableTransmission;
bool useThickObjectMode;
float3 transmittance;
float3 coatNormalWS;
float coatCoverage;
float coatIOR;
float coatMask;
float coatRoughness;
float ior;
float3 absorptionCoefficient;
float transmittanceMask;

result = surfacedata.specularColor;
needLinearToSRGB = true;
break;
case DEBUGVIEW_LIT_SURFACEDATA_COAT_NORMAL_WS:
result = surfacedata.coatNormalWS * 0.5 + 0.5;
break;
case DEBUGVIEW_LIT_SURFACEDATA_COAT_COVERAGE:
result = surfacedata.coatCoverage.xxx;
break;
case DEBUGVIEW_LIT_SURFACEDATA_COAT_IOR:
result = surfacedata.coatIOR.xxx;
case DEBUGVIEW_LIT_SURFACEDATA_COAT_MASK:
result = surfacedata.coatMask.xxx;
break;
case DEBUGVIEW_LIT_SURFACEDATA_IOR:
result = surfacedata.ior.xxx;

case DEBUGVIEW_LIT_BSDFDATA_TRANSMITTANCE:
result = bsdfdata.transmittance;
break;
case DEBUGVIEW_LIT_BSDFDATA_COAT_NORMAL_WS:
result = bsdfdata.coatNormalWS;
case DEBUGVIEW_LIT_BSDFDATA_COAT_MASK:
result = bsdfdata.coatMask.xxx;
case DEBUGVIEW_LIT_BSDFDATA_COAT_COVERAGE:
result = bsdfdata.coatCoverage.xxx;
break;
case DEBUGVIEW_LIT_BSDFDATA_COAT_IOR:
result = bsdfdata.coatIOR.xxx;
case DEBUGVIEW_LIT_BSDFDATA_COAT_ROUGHNESS:
result = bsdfdata.coatRoughness.xxx;
break;
case DEBUGVIEW_LIT_BSDFDATA_IOR:
result = bsdfdata.ior.xxx;

479
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.hlsl


#define LTC_LUT_SCALE ((LTC_LUT_SIZE - 1) * rcp(LTC_LUT_SIZE))
#define LTC_LUT_OFFSET (0.5 * rcp(LTC_LUT_SIZE))
// Constant value for clear coat
#define CLEAR_COAT_IOR 1.5
#define CLEAR_COAT_IETA (1.0 / CLEAR_COAT_IOR)
#define CLEAR_COAT_F0 0.04 // IORToFresnel0(CLEAR_COAT_IOR)
#define CLEAR_COAT_PERCEPTUAL_ROUGHNESS 0.031622
#define CLEAR_COAT_ROUGHNESS (CLEAR_COAT_PERCEPTUAL_ROUGHNESS * CLEAR_COAT_PERCEPTUAL_ROUGHNESS) // 0.001
//-----------------------------------------------------------------------------
// Helper for cheap screen space raycasting
//-----------------------------------------------------------------------------

}
}
void FillMaterialIdClearCoatData(float3 coatNormalWS, float coatCoverage, float coatIOR, inout BSDFData bsdfData)
// Note: this modify the parameter perceptualRoughness and fresnel0, so they need to be setup
void FillMaterialIdClearCoatData(float coatMask, inout BSDFData bsdfData)
bsdfData.coatNormalWS = lerp(bsdfData.normalWS, coatNormalWS, coatCoverage);
bsdfData.coatIOR = lerp(1.0, 1.0 + coatIOR, coatCoverage);
bsdfData.coatCoverage = coatCoverage;
bsdfData.coatMask = coatMask;
float ieta = lerp(1.0, CLEAR_COAT_IETA, bsdfData.coatMask);
bsdfData.coatRoughness = CLEAR_COAT_ROUGHNESS;
// Approx to deal with roughness appearance of base layer (should appear rougher)
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 = Fresnel0ReajustFor15(bsdfData.fresnel0);
}
void FillMaterialIdTransparencyData(float3 baseColor, float metallic, float ior, float3 transmittanceColor, float atDistance, float thickness, float transmittanceMask, inout BSDFData bsdfData)

// Pre-integrate GGX FGD
// Integral{BSDF * <N,L> dw} =
// Integral{(F0 + (1 - F0) * (1 - <V,H>)^5) * (BSDF / F) * <N,L> dw} =
// F0 * Integral{(BSDF / F) * <N,L> dw} +
// (1 - F0) * Integral{(1 - <V,H>)^5 * (BSDF / F) * <N,L> dw} =
// (1 - F0) * Integral{(1 - <V,H>)^5 * (BSDF / F) * <N,L> dw} + F0 * Integral{(BSDF / F) * <N,L> dw}=
// (1 - F0) * x + F0 * y = lerp(x, y, F0)
// Pre integrate DisneyDiffuse FGD:
// z = DisneyDiffuse

bsdfData.normalWS = surfaceData.normalWS;
bsdfData.anisotropy = surfaceData.anisotropy;
bsdfData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surfaceData.perceptualSmoothness);
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughness, bsdfData.anisotropy, bsdfData.roughnessT, bsdfData.roughnessB);
if (surfaceData.materialId != MATERIALID_LIT_ANISO)
{

}
else if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT)
{
bsdfData.diffuseColor = ComputeDiffuseColor(surfaceData.baseColor, surfaceData.metallic);
bsdfData.fresnel0 = ComputeFresnel0(surfaceData.baseColor, surfaceData.metallic, DEFAULT_SPECULAR_VALUE);
// When using clear coat we assume that bottom layer is regular
FillMaterialIdClearCoatData(surfaceData.coatNormalWS, surfaceData.coatCoverage, surfaceData.coatIOR, bsdfData);
// Same as MATERIALID_LIT_STANDARD + coatMask
bsdfData.diffuseColor = ComputeDiffuseColor(surfaceData.baseColor, surfaceData.metallic);
bsdfData.fresnel0 = ComputeFresnel0(surfaceData.baseColor, surfaceData.metallic, DEFAULT_SPECULAR_VALUE);
FillMaterialIdClearCoatData(surfaceData.coatMask, bsdfData);
// roughnessT and roughnessB are clamped, and are meant to be used with punctual and directional lights.
// perceptualRoughness is not clamped, and is meant to be used for IBL.
// perceptualRoughness can be modify by FillMaterialIdClearCoatData, so ConvertAnisotropyToClampRoughness must be call after
ConvertAnisotropyToClampRoughness(bsdfData.perceptualRoughness, bsdfData.anisotropy, bsdfData.roughnessT, bsdfData.roughnessB);
#if HAS_REFRACTION
// Note: Will override thickness of SSS's property set

// We store perceptualRoughness instead of roughness because it save a sqrt ALU when decoding
// (as we want both perceptualRoughness and roughness for the lighting due to Disney Diffuse model)
// Encode normal on 20bit with oct compression + 2bit of sign
float2 octNormalWS = PackNormalOctEncode((surfaceData.materialId == MATERIALID_LIT_CLEAR_COAT) ? surfaceData.coatNormalWS : surfaceData.normalWS);
float2 octNormalWS = PackNormalOctEncode(surfaceData.normalWS);
// To have more precision encode the sign of xy in a separate uint
uint octNormalSign = (octNormalWS.x < 0.0 ? 1 : 0) | (octNormalWS.y < 0.0 ? 2 : 0);
// Store octNormalSign on two bits with perceptualRoughness

if (surfaceData.materialId == MATERIALID_LIT_STANDARD)
{
outGBuffer2 = float4(float3(0.0, 0.0, 0.0), PackFloatInt8bit(surfaceData.metallic, GBUFFER_LIT_STANDARD_REGULAR_ID, 4.0));
outGBuffer2 = float4(float3(0.0, 0.0, 0.0), PackFloatInt8bit(surfaceData.metallic, GBUFFER_LIT_STANDARD_REGULAR_ID, 2.0));
outGBuffer2 = float4(surfaceData.specularColor, PackFloatInt8bit(0.0, GBUFFER_LIT_STANDARD_SPECULAR_COLOR_ID, 4.0));
outGBuffer2 = float4(surfaceData.specularColor, PackFloatInt8bit(0.0, GBUFFER_LIT_STANDARD_SPECULAR_COLOR_ID, 2.0));
}
else if (surfaceData.materialId == MATERIALID_LIT_SSS)
{

}
else if (surfaceData.materialId == MATERIALID_LIT_CLEAR_COAT)
{
// In the cae of clear coat, we want more precision for the coat normal than for the bottom normal (as it is expected to be smooth). So swap the normal encoding storage in Gbuffer.
// It also allow to use clear coat normal for SSR
float2 octBottomNormalWS = PackNormalOctEncode(surfaceData.normalWS);
outGBuffer2 = float4(octBottomNormalWS * 0.5 + 0.5, surfaceData.coatCoverage, PackFloatInt8bit(surfaceData.coatIOR, (int)(surfaceData.metallic * 15.5f), 16.0) );
outGBuffer2 = float4(0.0f, 0.0f, 0.0f, PackFloatInt8bit(surfaceData.coatMask, (int)(surfaceData.metallic * 15.5f), 16.0) );
}
// Lighting

bsdfData.bitangentWS = cross(bsdfData.normalWS, bsdfData.tangentWS);
}
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughness, bsdfData.anisotropy, bsdfData.roughnessT, bsdfData.roughnessB);
if (HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_SSS))
{
int subsurfaceProfile = SSS_NEUTRAL_PROFILE_ID;

FillMaterialIdSssData(subsurfaceProfile, radius, thickness, transmissionMode, bsdfData);
}
float coatMask = 0.0;
UnpackFloatInt8bit(inGBuffer2.a, 4.0, metallic, materialIdExtent);
UnpackFloatInt8bit(inGBuffer2.a, 2.0, metallic, materialIdExtent);
// We have swap the encoding of the normal to have more precision for coat normal as it is more smooth
float3 coatNormalWS = bsdfData.normalWS;
bsdfData.normalWS = UnpackNormalOctEncode(float2(inGBuffer2.rg * 2.0 - 1.0));
float coatCoverage = inGBuffer2.b;
float coatIOR;
UnpackFloatInt8bit(inGBuffer2.a, 16.0, coatIOR, metallic15);
UnpackFloatInt8bit(inGBuffer2.a, 16.0, coatMask, metallic15);
// When using clear coat we assume that bottom layer is regular
FillMaterialIdClearCoatData(coatNormalWS, coatCoverage, coatIOR, bsdfData);
}
if (specularColorMode)

bsdfData.diffuseColor = ComputeDiffuseColor(baseColor, metallic);
bsdfData.fresnel0 = ComputeFresnel0(baseColor, metallic, dielectricF0);
}
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
// fresnel0 must be init before calling FillMaterialIdClearCoatData
FillMaterialIdClearCoatData(coatMask, bsdfData);
}
// roughnessT and roughnessB are clamped, and are meant to be used with punctual and directional lights.
// perceptualRoughness is not clamped, and is meant to be used for IBL.
// perceptualRoughness can be modify by FillMaterialIdClearCoatData, so ConvertAnisotropyToClampRoughness must be call after
ConvertAnisotropyToClampRoughness(bsdfData.perceptualRoughness, bsdfData.anisotropy, bsdfData.roughnessT, bsdfData.roughnessB);
bakeDiffuseLighting = inGBuffer3.rgb;
}

struct PreLightData
{
// General
float NdotV;
float clampNdotV; // clamped NdotV
// Clear coat
float coatNdotV;
float ieta;
float coatFresnel0;
float3 refractV; // The view vector refracted through clear coat interface
float3 iblDirWS; // Dominant specular direction, used for IBL in EvaluateBSDF_Env()
float iblMipLevel;
// IBL clear coat
float3 coatIblDirWS;
float3 iblR; // Dominant specular direction, used for IBL in EvaluateBSDF_Env()
float iblPerceptualRoughness;
float3 specularFGD; // Store preconvoled BRDF for both specular and diffuse
float diffuseFGD;

float ltcMagnitudeDiffuse;
float3 ltcMagnitudeFresnel;
// area light clear coat
float3x3 ltcXformClearCoat; // TODO: make sure the compiler not wasting VGPRs on constants
float ltcClearCoatFresnelTerm;
float3x3 ltcCoatT;
// Clear coat
float coatPartLambdaV;
float3 coatIblR;
float coatIblF; // Fresnel term for view vector
float3x3 ltcTransformCoat; // Inverse transformation for GGX (4x VGPRs)
float ltcMagnitudeCoatFresnel;
// Refraction
float3 transmissionRefractV; // refracted view vector after exiting the shape

};
// This is a refract - TODO: do we call original refract or this one, original maybe slightly more expensive, to check
float3 ClearCoatTransform(float3 X, float3 N, float ieta)
{
float XdotN = saturate(dot(N, X));
return ieta * X + (sqrt(1 + ieta * ieta * (XdotN * XdotN - 1)) - ieta * XdotN) * N;
}
ZERO_INITIALIZE(PreLightData, preLightData);
float3 N;
float NdotV;
float3 N = bsdfData.normalWS;
float NdotV = saturate(dot(N, V));
preLightData.clampNdotV = NdotV; // Caution: The handling of edge cases where N is directed away from the screen is handled during Gbuffer/forward pass, so here do nothing
preLightData.iblPerceptualRoughness = bsdfData.perceptualRoughness;
// Clear coat need to modify roughness, V and NdotV for all the other precalculation below
N = bsdfData.coatNormalWS;
NdotV = saturate(dot(N, V));
preLightData.coatNdotV = NdotV;
float ieta = 1.0 / bsdfData.coatIOR; // inverse eta
preLightData.ieta = ieta;
preLightData.coatFresnel0 = Sq(bsdfData.coatIOR - 1.0) / Sq(bsdfData.coatIOR + 1.0);
// Clear coat IBL
preLightData.coatIblDirWS = reflect(-V, N);
// Clear coat area light
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
// Note we load the matrix transpose (avoid to have to transpose it in shader)
preLightData.ltcXformClearCoat = 0.0;
preLightData.ltcXformClearCoat._m22 = 1.0;
preLightData.ltcXformClearCoat._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0);
float3 ltcMagnitude = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_MULTI_GGX_FRESNEL_DISNEY_DIFFUSE_INDEX, 0).rgb;
float ltcClearCoatFresnelMagnitudeDiff = ltcMagnitude.r; // The difference of magnitudes of GGX and Fresnel
float ltcClearCoatFresnelMagnitude = ltcMagnitude.g;
preLightData.ltcClearCoatFresnelTerm = preLightData.coatFresnel0 * ltcClearCoatFresnelMagnitudeDiff + ltcClearCoatFresnelMagnitude;
// TODO: Convert the area light with respect to Fresnel transmission
preLightData.ltcCoatT = float3x3( ieta + (1.0 - ieta) * N.x * N.x, 0.0 + (1.0 - ieta) * N.y * N.x, 0.0 + (1.0 - ieta) * N.z * N.x,
0.0 + (1.0 - ieta) * N.x * N.y, ieta + (1.0 - ieta) * N.y * N.y, 0.0 + (1.0 - ieta) * N.z * N.y,
0.0 + (1.0 - ieta) * N.x * N.z, 0.0 + (1.0 - ieta) * N.y * N.z, ieta + (1.0 - ieta) * N.z * N.z );
// Modify V for following calculation
preLightData.refractV = ClearCoatTransform(V, N, ieta);
V = preLightData.refractV;
preLightData.coatPartLambdaV = GetSmithJointGGXPartLambdaV(NdotV, CLEAR_COAT_ROUGHNESS);
preLightData.coatIblR = reflect(-V, N);
preLightData.coatIblF = F_Schlick(CLEAR_COAT_F0, NdotV) * bsdfData.coatMask;
N = bsdfData.normalWS;
NdotV = saturate(dot(N, V));
preLightData.NdotV = NdotV;
float3 iblN, iblR;

// For positive anisotropy values: tangent = highlight stretch (anisotropy) direction, bitangent = grain (brush) direction.
float3 grainDirWS = (bsdfData.anisotropy >= 0) ? bsdfData.bitangentWS : bsdfData.tangentWS;
// Reduce stretching for (perceptualRoughness < 0.2).
float stretch = abs(bsdfData.anisotropy) * saturate(5 * bsdfData.perceptualRoughness);
float stretch = abs(bsdfData.anisotropy) * saturate(5 * preLightData.iblPerceptualRoughness);
// NOTE: If we follow the theory we should use the modified normal for the different calculation implying a normal (like NdotV) and use 'anisoIblNormalWS'
// into function like GetSpecularDominantDir(). However modified normal is just a hack. The goal is just to stretch a cubemap, no accuracy here.
// With this in mind and for performance reasons we chose to only use modified normal to calculate R.

iblN = N;
}
iblR = reflect(-V, iblN);
// IBL
// Handle IBL + multiscattering
// IBL
GetPreIntegratedFGD(NdotV, bsdfData.perceptualRoughness, bsdfData.fresnel0, preLightData.specularFGD, preLightData.diffuseFGD, reflectivity);
GetPreIntegratedFGD(NdotV, preLightData.iblPerceptualRoughness, bsdfData.fresnel0, preLightData.specularFGD, preLightData.diffuseFGD, reflectivity);
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
// Update the roughness and the IBL miplevel
// Bottom layer is affected by upper layer BRDF, result can't be more sharp than input (it is to mimic what a path tracer will do)
float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
float shininess = Sq(preLightData.ieta) * (2.0 / Sq(roughness) - 2.0);
roughness = sqrt(2.0 / (shininess + 2.0));
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, roughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(RoughnessToPerceptualRoughness(roughness));
}
else
{
// Note: this is a ad-hoc tweak.
// TODO: we need a better hack.
float iblPerceptualRoughness = bsdfData.perceptualRoughness * saturate(1.2 - abs(bsdfData.anisotropy));
float iblRoughness = PerceptualRoughnessToRoughness(iblPerceptualRoughness);
preLightData.iblDirWS = GetSpecularDominantDir(N, iblR, iblRoughness, NdotV);
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(iblPerceptualRoughness);
}
iblR = reflect(-V, iblN);
// This is a ad-hoc tweak to better match reference of anisotropic GGX.
// TODO: We need a better hack.
preLightData.iblPerceptualRoughness *= saturate(1.2 - abs(bsdfData.anisotropy));
float iblRoughness = PerceptualRoughnessToRoughness(preLightData.iblPerceptualRoughness);
// Corretion of reflected direction for better handling of rough material
preLightData.iblR = GetSpecularDominantDir(N, iblR, iblRoughness, NdotV);
#ifdef LIT_USE_GGX_ENERGY_COMPENSATION
// Ref: Practical multiple scattering compensation for microfacet models.

// TODO: the fit seems rather poor. The scaling factor of 0.5 allows us
// to match the reference for rough metals, but further darkens dielectrics.
preLightData.ltcMagnitudeFresnel = bsdfData.fresnel0 * ltcGGXFresnelMagnitudeDiff + (float3)ltcGGXFresnelMagnitude;
// Change the Fresnel term to account for transmission through Clear Coat and reflection on the base layer
float F = F_Schlick(preLightData.coatFresnel0, preLightData.coatNdotV);
F = Sq(-F * bsdfData.coatCoverage + 1.0);
F /= preLightData.ieta; //TODO: LaurentB why / ieta here and not for other lights ?
float2 uv = LTC_LUT_OFFSET + LTC_LUT_SCALE * float2(CLEAR_COAT_PERCEPTUAL_ROUGHNESS, theta * INV_HALF_PI);
preLightData.ltcMagnitudeFresnel = F * bsdfData.fresnel0 * ltcGGXFresnelMagnitudeDiff + (float3)ltcGGXFresnelMagnitude;
}
else
{
preLightData.ltcMagnitudeFresnel = bsdfData.fresnel0 * ltcGGXFresnelMagnitudeDiff + (float3)ltcGGXFresnelMagnitude;
// Get the inverse LTC matrix for GGX
// Note we load the matrix transpose (avoid to have to transpose it in shader)
preLightData.ltcTransformCoat = 0.0;
preLightData.ltcTransformCoat._m22 = 1.0;
preLightData.ltcTransformCoat._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0);
ltcMagnitude = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, s_linear_clamp_sampler, uv, LTC_MULTI_GGX_FRESNEL_DISNEY_DIFFUSE_INDEX, 0).rgb;
ltcGGXFresnelMagnitudeDiff = ltcMagnitude.r; // The difference of magnitudes of GGX and Fresnel
ltcGGXFresnelMagnitude = ltcMagnitude.g;
preLightData.ltcMagnitudeCoatFresnel = (CLEAR_COAT_F0 * ltcGGXFresnelMagnitudeDiff + (float3)ltcGGXFresnelMagnitude) * bsdfData.coatMask;
// refraction (forward only)
// Empirical remap to try to match a bit the refractio probe blurring for the fallback
preLightData.transmissionSSMipLevel = sqrt(bsdfData.perceptualRoughness) * uint(_GaussianPyramidColorMipSize.z);
#else
preLightData.transmissionRefractV = -V;
preLightData.transmissionPositionWS = posInput.positionWS;
preLightData.transmissionTransmittance = float3(1.0, 1.0, 1.0);
preLightData.transmissionSSMipLevel = 0;
// Empirical remap to try to match a bit the refraction probe blurring for the fallback
// Use IblPerceptualRoughness so we can handle approx of clear coat.
preLightData.transmissionSSMipLevel = sqrt(preLightData.iblPerceptualRoughness) * uint(_GaussianPyramidColorMipSize.z);
#endif
return preLightData;

// BSDF share between directional light, punctual light and area light (reference)
//-----------------------------------------------------------------------------
// This function apply BSDF * cos (required to deal with clear coating)
float3 F = 1.0;
specularLighting = float3(0.0, 0.0, 0.0);
float3 N = bsdfData.normalWS;
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT) )
{
// Optimized math. Ref: PBR Diffuse Lighting for GGX + Smith Microsurfaces (slide 114).
float NdotL = saturate(dot(bsdfData.coatNormalWS, L));
float NdotV = preLightData.coatNdotV;
float LdotV = dot(L, V);
float invLenLV = rsqrt(max(2 * LdotV + 2, FLT_EPS));
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);
// Evaluate Fresnel on the Clear Coat
F = F_Schlick(preLightData.coatFresnel0, LdotH);
// TODO: No need to call D (to see with LaurentB) + question on * NdotL
specularLighting += F * D_GGX(NdotH, 0.01) * NdotL * bsdfData.coatCoverage;
// Change the Fresnel term to account for transmission through Clear Coat and reflection on the base layer
F = Sq(-F * bsdfData.coatCoverage + 1.0);
// Change the Light and View direction to account for IOR change.
// Update the half vector accordingly
V = preLightData.refractV;
L = ClearCoatTransform(L, bsdfData.coatNormalWS, preLightData.ieta);
}
float NdotV = preLightData.clampNdotV;
float NdotL = saturate(dot(bsdfData.normalWS, L)); // Must have the same value without the clamp
float NdotV = preLightData.NdotV; // Get the unaltered (geometric) version
float LdotV = dot(L, V);
float invLenLV = rsqrt(max(2 * LdotV + 2, FLT_EPS)); // invLenLV = rcp(length(L + V)) - caution about the case where V and L are opposite, it can happen, use max to avoid this
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);
float NdotL = saturate(dot(N, L)); // Must have the same value without the clamp
F *= F_Schlick(bsdfData.fresnel0, LdotH);
float LdotV = dot(L, V);
float invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS)); // invLenLV = rcp(length(L + V)) - caution about the case where V and L are opposite, it can happen, use max to avoid this
float NdotH = saturate((NdotL + NdotV) * invLenLV);
float LdotH = saturate(invLenLV * LdotV + invLenLV);
float3 F = F_Schlick(bsdfData.fresnel0, LdotH);
float DV;
// We avoid divergent evaluation of the GGX, as that nearly doubles the cost.

float BdotH = dot(bsdfData.bitangentWS, H);
float BdotL = dot(bsdfData.bitangentWS, L);
// TODO: Do comparison between this correct version and the one from isotropic and see if there is any visual difference
DV = DV_SmithJointGGXAniso(TdotH, BdotH, NdotH, NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB, preLightData.partLambdaV);

specularLighting += F * DV;
specularLighting = F * DV;
#elif LIT_DIFFUSE_GGX_BRDF
float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
float3 diffuseTerm = DiffuseGGX(bsdfData.diffuseColor, NdotV, NdotL, NdotH, LdotV, roughness);
#else
// A note on subsurface scattering: [SSS-NOTE-TRSM]
// The correct way to handle SSS is to transmit light inside the surface, perform SSS,

// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
diffuseLighting = diffuseTerm;
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
// Apply isotropic GGX for clear coat
// Note: coat F is scalar as it is a dieletric
float coatF = F_Schlick(CLEAR_COAT_F0, LdotH) * bsdfData.coatMask;
// Scale base specular
specularLighting *= Sq(1.0 - coatF);
// Add top specular
// TODO: Should we call just D_GGX here ?
float DV = DV_SmithJointGGX(NdotH, NdotL, NdotV, bsdfData.coatRoughness, preLightData.coatPartLambdaV);
specularLighting += coatF * DV;
// Note: The modification of the base roughness and fresnel0 by the clear coat is already handled in FillMaterialIdClearCoatData
// Scale diffuse for energy conservation (use NdotL as an approximation)
diffuseLighting *= F_Schlick(CLEAR_COAT_F0, NdotL);
}
}
// In the "thin object" mode (for cards), we assume that the geometry is very thin.

float3 N = bsdfData.normalWS;
float3 L = -lightData.forward; // Lights point backward in Unity
float NdotL = dot(N, L);
float NdotL = dot(N, L); // Note: Ideally this N here should be vertex normal - use for transmisison
// Compute displacement for fake thickObject transmission
float3 color; float attenuation;
EvaluateLight_Directional(lightLoopContext, posInput, lightData, bakeLightingData, N, L,
color, attenuation);
float3 color;
float attenuation;
EvaluateLight_Directional(lightLoopContext, posInput, lightData, bakeLightingData, N, L, color, attenuation);
[branch] if (intensity > 0)
// Note: We use NdotL here to early out, but in case of clear coat this is not correct. But we are ok with this
[branch] if (intensity > 0.0)
{
BSDF(V, L, posInput.positionWS, preLightData, bsdfData, lighting.diffuse, lighting.specular);

[branch] if (bsdfData.enableTransmission)
{
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass.
lighting.diffuse += EvaluateTransmission(bsdfData, NdotL, preLightData.NdotV, attenuation * lightData.diffuseScale);
lighting.diffuse += EvaluateTransmission(bsdfData, NdotL, preLightData.clampNdotV, attenuation * lightData.diffuseScale);
}
// Save ALU by applying light and cookie colors only once.

float3 L = unL * distRcp;
float NdotL = dot(N, L);
// Compute displacement for fake thickObject transmission
float3 color; float attenuation;
EvaluateLight_Punctual(lightLoopContext, posInput, lightData, bakeLightingData, N, L, dist, distSq,
color, attenuation);
float3 color;
float attenuation;
EvaluateLight_Punctual(lightLoopContext, posInput, lightData, bakeLightingData, N, L, dist, distSq, color, attenuation);
[branch] if (intensity > 0)
// Note: We use NdotL here to early out, but in case of clear coat this is not correct. But we are ok with this
[branch] if (intensity > 0.0)
// Simulate a sphere light with this hack.
// Simulate a sphere light with this hack
// Note that it is not correct with our pre-computation of PartLambdaV (mean if we disable the optimization we will not have the
// same result) but we don't care as it is a hack anyway
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
bsdfData.coatRoughness = max(bsdfData.coatRoughness, lightData.minRoughness);
}
bsdfData.roughnessT = max(bsdfData.roughnessT, lightData.minRoughness);
bsdfData.roughnessB = max(bsdfData.roughnessB, lightData.minRoughness);

[branch] if (bsdfData.enableTransmission)
{
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass.
lighting.diffuse += EvaluateTransmission(bsdfData, NdotL, preLightData.NdotV, attenuation * lightData.diffuseScale);
lighting.diffuse += EvaluateTransmission(bsdfData, NdotL, preLightData.clampNdotV, attenuation * lightData.diffuseScale);
}
// Save ALU by applying light and cookie colors only once.

float ltcValue;
// Evaluate the diffuse part
{
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformDiffuse);
ltcValue *= lightData.diffuseScale;
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
lighting.diffuse = preLightData.ltcMagnitudeDiffuse * ltcValue;
}
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformDiffuse);
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
lighting.diffuse = preLightData.ltcMagnitudeDiffuse * ltcValue;
[branch] if (bsdfData.enableTransmission)
{

// The matrix multiplication should not generate any extra ALU on GCN.
// TODO: double evaluation is very inefficient! This is a temporary solution.
ltcValue = LTCEvaluate(P1, P2, B, mul(flipMatrix, k_identity3x3));
ltcValue *= lightData.diffuseScale;
// Evaluate the specular part
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformSpecular);
lighting.specular = preLightData.ltcMagnitudeFresnel * ltcValue;
// TODO
// ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcXformClearCoat);
// ltcValue *= lightData.specularScale;
// specularLighting = preLightData.ltcClearCoatFresnelTerm * (ltcValue * bsdfData.coatCoverage);
}
// Evaluate the specular part
{
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformSpecular);
ltcValue *= lightData.specularScale;
lighting.specular += preLightData.ltcMagnitudeFresnel * ltcValue;
lighting.diffuse *= (1.0 - preLightData.ltcMagnitudeCoatFresnel);
lighting.specular *= (1.0 - preLightData.ltcMagnitudeCoatFresnel);
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformCoat);
lighting.specular += preLightData.ltcMagnitudeCoatFresnel * ltcValue;
lighting.diffuse *= lightData.color;
lighting.specular *= lightData.color;
lighting.diffuse *= lightData.color * lightData.diffuseScale;
lighting.specular *= lightData.color * lightData.specularScale;;
#endif // LIT_DISPLAY_REFERENCE_AREA
return lighting;

float ltcValue;
// Evaluate the diffuse part
{
// Polygon irradiance in the transformed configuration.
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformDiffuse));
ltcValue *= lightData.diffuseScale;
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
lighting.diffuse = preLightData.ltcMagnitudeDiffuse * ltcValue;
}
// Polygon irradiance in the transformed configuration.
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformDiffuse));
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
lighting.diffuse = preLightData.ltcMagnitudeDiffuse * ltcValue;
[branch] if (bsdfData.enableTransmission)
{

// Polygon irradiance in the transformed configuration.
// TODO: double evaluation is very inefficient! This is a temporary solution.
ltcValue = PolygonIrradiance(mul(lightVerts, ltcTransform));
ltcValue *= lightData.diffuseScale;
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass.
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().

// Evaluate the specular part
// Polygon irradiance in the transformed configuration.
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformSpecular));
lighting.specular += preLightData.ltcMagnitudeFresnel * ltcValue;
// TODO
// ltcValue = LTCEvaluate(lightVerts, V, bsdfData.coatNormalWS, preLightData.coatNdotV, preLightData.ltcXformClearCoat);
// lighting.specular = preLightData.ltcClearCoatFresnelTerm * (ltcValue * bsdfData.coatCoverage);
// modify matL value based on Fresnel transmission
// matL = mul(matL, preLightData.ltcCoatT);
// V = preLightData.refractV;
}
// Evaluate the specular part
{
// Polygon irradiance in the transformed configuration.
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformSpecular));
ltcValue *= lightData.specularScale;
lighting.specular += preLightData.ltcMagnitudeFresnel * ltcValue;
lighting.diffuse *= (1.0 - preLightData.ltcMagnitudeCoatFresnel);
lighting.specular *= (1.0 - preLightData.ltcMagnitudeCoatFresnel);
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformCoat));
lighting.specular += preLightData.ltcMagnitudeCoatFresnel * ltcValue;
lighting.diffuse *= lightData.color;
lighting.specular *= lightData.color;
lighting.diffuse *= lightData.color * lightData.diffuseScale;
lighting.specular *= lightData.color * lightData.specularScale;
#endif // LIT_DISPLAY_REFERENCE_AREA
return lighting;

return lighting;
#endif
float3 envLighting = float3(0.0, 0.0, 0.0);
float3 envLighting;
float3 positionWS = posInput.positionWS;
float weight = 1.0;

// TODO: Do refraction reference (is it even possible ?)
// TODO: handle clear coat
// #ifdef LIT_DIFFUSE_LAMBERT_BRDF

// #endif
weight = 1.0;
// float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
// float roughness = PerceptualRoughnessToRoughness(preLightData.IblPerceptualRoughness);
// float shrunkRoughness = AnisotropicStrechAtGrazingAngle(roughness, roughness, NdotV);
// Guideline for reflection volume: In HDRenderPipeline we separate the projection volume (the proxy of the scene) from the influence volume (what pixel on the screen is affected)

// that simulate effect of no shape projection
float3 R = preLightData.iblDirWS;
float3 coatR = preLightData.coatIblDirWS;
float3 R = preLightData.iblR;
float3 coatR = preLightData.coatIblR;
if (GPUImageBasedLightingType == GPUIMAGEBASEDLIGHTINGTYPE_REFRACTION)
{

{
dirLS = mul(coatR, worldToLocal);
projectionDistance = SphereRayIntersectSimple(positionLS, dirLS, sphereOuterDistance);
projectionDistance = max(projectionDistance, lightData.minProjectionDistance); // Setup projection to infinite if requested (mean no projection shape)
coatR = (positionWS + projectionDistance * coatR) - lightData.positionWS;
}

{
dirLS = mul(coatR, worldToLocal);
projectionDistance = BoxRayIntersectSimple(positionLS, dirLS, -boxOuterDistance, boxOuterDistance);
projectionDistance = max(projectionDistance, lightData.minProjectionDistance); // Setup projection to infinite if requested (mean no projection shape)
coatR = (positionWS + projectionDistance * coatR) - lightData.positionWS;
}

// Smooth weighting
weight = Smoothstep01(weight);
float3 F = 1.0;
// When we are rough, we tend to see outward shifting of the reflection when at the boundary of the projection volume
// Also it appear like more sharp. To avoid these artifact and at the same time get better match to reference we lerp to original unmodified reflection.
// Formula is empirical.
float roughness = PerceptualRoughnessToRoughness(preLightData.iblPerceptualRoughness);
R = lerp(R, preLightData.iblR, saturate(smoothstep(0, 1, roughness * roughness)));
float3 F = preLightData.specularFGD;
float iblMipLevel = PerceptualRoughnessToMipmapLevel(preLightData.iblPerceptualRoughness);
float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, R, iblMipLevel);
// Evaluate the Clear Coat component if needed and change the BSDF roughness to match Fresnel transmission
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
if (GPUImageBasedLightingType == GPUIMAGEBASEDLIGHTINGTYPE_REFLECTION)
F = F_Schlick(preLightData.coatFresnel0, preLightData.coatNdotV);
envLighting = F * preLD.rgb;
// Evaluate the Clear Coat component if needed
if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT && HasMaterialFeatureFlag(MATERIALFEATUREFLAGS_LIT_CLEAR_COAT))
{
// No correction needed for coatR as it is smooth
// Note: coat F is scalar as it is a dieletric
envLighting *= Sq(1.0 - preLightData.coatIblF);
// Evaluate the Clear Coat color
float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, coatR, 0.0);
envLighting += F * preLD.rgb * bsdfData.coatCoverage;
// Evaluate the Clear Coat color
float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, coatR, 0.0);
envLighting += preLightData.coatIblF * preLD.rgb;
// Change the Fresnel term to account for transmission through Clear Coat and reflection on the base layer.
F = Sq(-F * bsdfData.coatCoverage + 1.0);
// Can't attenuate diffuse lighting here, may try to apply something on bakeLighting in PostEvaluateBSDF
}
else
{
// specular transmisted lighting is the remaining of the reflection (let's use this approx)
// With refraction, we don't care about the clear coat value, only about the Fresnel, thus why we use 'envLighting ='
envLighting = (1.0 - F) * preLD.rgb * preLightData.transmissionTransmittance;
// When we are rough, we tend to see outward shifting of the reflection when at the boundary of the projection volume
// Also it appear like more sharp. To avoid these artifact and at the same time get better match to reference we lerp to original unmodified reflection.
// Formula is empirical.
float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
R = lerp(R, preLightData.iblDirWS, saturate(smoothstep(0, 1, roughness * roughness)));
// Don't account for clear coat to save ALU
}
float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, R, preLightData.iblMipLevel);
envLighting += F * preLD.rgb;
#endif
#endif // LIT_DISPLAY_REFERENCE_IBL
lighting.specularReflected = envLighting * preLightData.specularFGD;
lighting.specularReflected = envLighting;
// specular transmisted lighting is the remaining of the reflection (let's use this approx)
lighting.specularTransmitted = (1.0 - preLightData.specularFGD) * envLighting * preLightData.transmissionTransmittance;
lighting.specularTransmitted = envLighting * preLightData.transmissionTransmittance;
return lighting;
}

#endif
float roughness = PerceptualRoughnessToRoughness(bsdfData.perceptualRoughness);
float specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(preLightData.NdotV, indirectAmbientOcclusion, roughness);
float specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(preLightData.clampNdotV, indirectAmbientOcclusion, roughness);
// Try to mimic multibounce with specular color. Not the point of the original formula but ok result.
// Take the min of screenspace specular occlusion and visibility cone specular occlusion
#if GTAO_MULTIBOUNCE_APPROX

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/Lit.shader


_ThicknessMap("Thickness Map", 2D) = "white" {}
_ThicknessRemap("Thickness Remap", Vector) = (0, 1, 0, 0)
_CoatCoverage("Coat Coverage", Range(0.0, 1.0)) = 1.0
_CoatIOR("Coat IOR", Range(0.0, 1.0)) = 0.5
_CoatMask("Coat Mask", Range(0.0, 1.0)) = 1.0
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
_SpecularColorMap("SpecularColorMap", 2D) = "white" {}

8
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitDataIndividualLayer.hlsl


surfaceData.transmittanceMask = 0.0;
#endif
surfaceData.coatNormalWS = input.worldToTangent[2].xyz; // Assign vertex normal
surfaceData.coatCoverage = _CoatCoverage;
surfaceData.coatIOR = _CoatIOR;
surfaceData.coatMask = _CoatMask;
#else // #if !defined(LAYERED_LIT_SHADER)

surfaceData.tangentWS = float3(0.0, 0.0, 0.0);
surfaceData.anisotropy = 0.0;
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
surfaceData.coatNormalWS = float3(0.0, 0.0, 0.0);
surfaceData.coatCoverage = 0.0f;
surfaceData.coatIOR = 0.5;
surfaceData.coatMask = 0.0f;
// Transparency
surfaceData.ior = 1.0;

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitProperties.hlsl


float _Thickness;
float4 _ThicknessRemap;
float _CoatCoverage;
float _CoatIOR;
float _CoatMask;
float4 _SpecularColor;

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitReference.hlsl


uint sampleCount = 4096)
{
float3x3 localToWorld = float3x3(bsdfData.tangentWS, bsdfData.bitangentWS, bsdfData.normalWS);
float NdotV = preLightData.NdotV;
float NdotV = preLightData.clampNdotV;
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d

localToWorld = GetLocalFrame(bsdfData.normalWS);
}
float NdotV = preLightData.NdotV;
float NdotV = preLightData.clampNdotV;
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d

3
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/Lit/LitTessellation.shader


_ThicknessMap("Thickness Map", 2D) = "white" {}
_ThicknessRemap("Thickness Remap", Vector) = (0, 1, 0, 0)
_CoatCoverage("Coat Coverage", Range(0.0, 1.0)) = 1.0
_CoatIOR("Coat IOR", Range(0.0, 1.0)) = 0.5
_CoatMask("Coat Mask", Range(0.0, 1.0)) = 1.0
_SpecularColor("SpecularColor", Color) = (1, 1, 1, 1)
_SpecularColorMap("SpecularColorMap", 2D) = "white" {}

正在加载...
取消
保存