浏览代码

Working analytical lights with anisotropy and coat, per light recalculations should work.

/main
Stephane Laroche 7 年前
当前提交
d760c6a1
共有 3 个文件被更改,包括 217 次插入158 次删除
  1. 369
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLit.hlsl
  2. 4
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLit.shader
  3. 2
      ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLitProperties.hlsl

369
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLit.hlsl


// Vertically Layered BSDF : "vlayering"
//
//#define VLAYERED_RECOMPUTE_PERLIGHT // TODOTODO, and to test, make it a shader_features
// probably too slow but just to check also the difference it makes
// Vlayer config options:
//#define VLAYERED_RECOMPUTE_PERLIGHT // TODO test more, make it a shader_features
// probably too slow but just to check the difference it makes
//#define VLAYERED_USE_REFRACTED_ANGLES_FOR_BASE
//#define VLAYERED_DIFFUSE_ENERGY_HACKED_TERM
//#define VLAYERED_IBL_DESTRETCH_COMPENSATION_FOR_ANISOTROPY
// Automatic:

// TODO: if dual lobe base
//#define BASE_NB_LOBES 1
#define BASE_NB_LOBES 2
//#define TOTAL_NB_LOBES (BASE_NB_LOBES+COAT_NB_LOBES)
#define TOTAL_NB_LOBES 3 // TODO, for now, this will do (fix compilation errors before static pruning on some platforms)
#define TOTAL_NB_LOBES (BASE_NB_LOBES+COAT_NB_LOBES)
// TODO CLEANUP and put in proper define above

#define NB_VLAYERS 3
//#define NB_TRUE_INTERFACES 2
//#define INTERFACE_TOP_IDX 0
//#define INTERFACE_BASE_IDX 1
// TODOTODO
#define VLAYERED_DIFFUSE_ENERGY_HACKED_TERM
//-----------------------------------------------------------------------------
// Configuration

float GetCoatEta(in BSDFData bsdfData)
{
float eta = bsdfData.coatIor / 1.0;
//float eta = 1.5 / 1.0;
//ieta = 1.0 / eta;
return eta;
}

// We have a coat top layer: change the base fresnel0 accordingdly:
bsdfData.fresnel0 = ConvertF0ForAirInterfaceToF0ForNewTopIor(bsdfData.fresnel0, bsdfData.coatIor);
// We just dont clamp the roughnesses for now, but after the ComputeAdding() which will use those:
// Dont clamp the roughnesses for now, ComputeAdding() will use those directly:
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughnessA, bsdfData.anisotropy, bsdfData.roughnessAT, bsdfData.roughnessAB);
ConvertAnisotropyToRoughness(bsdfData.perceptualRoughnessB, bsdfData.anisotropy, bsdfData.roughnessBT, bsdfData.roughnessBB);
bsdfData.coatRoughness = PerceptualRoughnessToRoughness(bsdfData.coatPerceptualRoughness);

{
// 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.
// TODO: add ui inputs, +tangent map for anisotropy;
// TODO: add tangent map for anisotropy;
ConvertAnisotropyToClampRoughness(bsdfData.perceptualRoughnessA, bsdfData.anisotropy, bsdfData.roughnessAT, bsdfData.roughnessAB);
ConvertAnisotropyToClampRoughness(bsdfData.perceptualRoughnessB, bsdfData.anisotropy, bsdfData.roughnessBT, bsdfData.roughnessBB);
}

struct PreLightData
{
float NdotV; // Could be negative due to normal mapping, use ClampNdotV()
float TdotV; // Stored only when VLAYERED_RECOMPUTE_PERLIGHT
float BdotV;
// IBL: we calculate and prefetch the pre-integrated split sum data for
// all needed lobes

// For IBLs (and analytical lights if approximation is used)
float3 vLayerEnergyCoeff[NB_VLAYERS];
float vLayerPerceptualRoughness[NB_VLAYERS];
//float vLayerPerceptualRoughness[NB_VLAYERS];
// We will duplicate one entry to simplify the IBL loop
// (In general it's either that or we add branches (if lobe from bottom interface or
// top inteface) in the loop and make sure the compiler [unroll] - should be automatic
// on a static loop - as then the compiler will remove these known branches as it unrolls.
// All our loops for lobes are static so either way it should unroll and remove either
// duplicated storage or the branch.)
//float energyCompensation[TOTAL_NB_LOBES];
// terms during ComputeAdding (ie FGD becomes FGDinf) (but the approximation depends on f0,
// our FGD is scalar, not rgb, see GetEnergyCompensationFactor.)
// terms during ComputeAdding (ie FGD becomes FGDinf) (but the approximation depends on
// f0, our FGD is scalar, not rgb, see GetEnergyCompensationFactor.)
// Same thing for the F0:
// So we will compute float3 energy factors per lobe:
float3 energyFactor[TOTAL_NB_LOBES];
// We will compute float3 energy factors per lobe.
// We will duplicate one entry to simplify the IBL loop (In general it's either that or
// we add branches (if lobe from bottom interface or top inteface)
// (All our loops for lobes are static so either way the compiler should unroll and remove
// either duplicated storage or the branch.)
float3 energyCompensationFactor[TOTAL_NB_LOBES];
//See VLAYERED_DIFFUSE_ENERGY_HACKED_TERM
float3 diffuseEnergy;

out float ctt,
out float3 R12, out float3 T12, out float3 R21, out float3 T21,
out float s_r12, out float s_t12, out float j12,
//
//out float s_r12_lobeB,
//
if(i==0) {
if( i == 0 )
{
// Update energy
float R0, n12;

// Update mean
float sti = sqrt(1.0 - Sq(cti));
float stt = sti / n12;
if(stt <= 1.0f) {
if( stt <= 1.0f )
{
// Hack: as roughness -> 1, remove the effect of changing angle also note: we never track means per se
// because of symmetry, we have no azimuth, and don't consider offspecular effect as well as never
// outputting final downward lobes anyway.

//http://www.wolframalpha.com/input/?i=f(alpha)+%3D+(1.0-alpha)*(sqrt(1.0-alpha)+%2B+alpha)+alpha+%3D+0+to+1
stt = scale*stt + (1.0-scale)*sti;
ctt = sqrt(1.0 - stt*stt);
} else {
// TER, flip sign: directions either reflected or transmitted always leave
}
else
{
// TIR, flip sign: directions either reflected or transmitted always leave
// the surface. So here we have ctt instead of cti, we reverse dir by flipping sign.
// Not accounted for though check implications of ctt = -1.0
// TODO

j21 = 1.0/j12;
// Case of the media layer
} else if(i ==1) {
}
else if(i == 1)
{
// Update energy
R12 = float3(0.0, 0.0, 0.0);
T12 = exp(- bsdfData.coatThickness * bsdfData.coatExtinction / cti);

j21 = 1.0;
// Case of the dielectric / conductor base
} else {
}
else
{
// Update energy
R12 = F_Schlick(bsdfData.fresnel0, cti);
T12 = 0.0;

// Iterate over the layers
for(int i = 0; i < NB_VLAYERS; ++i)
{
// Variables for the adding step
float3 R12, T12, R21, T21;
s_r12=0.0;

_s_ri0 = (e_ri0 > 0.0) ? _s_ri0/e_ri0 : 0.0;
// Store the coefficient and variance
if(m_r0i > 0.0) {
// TODO: cleanup and check if unroll works, then need only top layer, can use an if
// and the rest of the array is not needed
preLightData.vLayerEnergyCoeff[i] = m_R0i; // TODO: don't forget to lerp with lobeMix for bottom coefficient
preLightData.vLayerPerceptualRoughness[i] = LinearVarianceToPerceptualRoughness(_s_r0m);
} else {
if(m_r0i > 0.0)
{
preLightData.vLayerEnergyCoeff[i] = m_R0i;
//preLightData.vLayerPerceptualRoughness[i] = LinearVarianceToPerceptualRoughness(_s_r0m);
}
else
{
preLightData.vLayerPerceptualRoughness[i] = 0.0;
//preLightData.vLayerPerceptualRoughness[i] = 0.0;
}
// Update energy

}
//-------------------------------------------------------------
// Post compute: TODO also VLAYERED_RECOMPUTE_PERLIGHT
// Post compute:
//-------------------------------------------------------------
// TODO: dual lobe feature option
//

// We have 6 roughnesses to process;
// TODO: We could probably optimize this a bit.
//
// We need both bottom lobes
// perceptualRoughnessA and perceptualRoughnessB
// for IBLs (because anisotropy will use a hack)
// We need both bottom lobes perceptualRoughnessA and
// perceptualRoughnessB for IBLs (because anisotropy will use a hack)
//
// Then we need anisotropic roughness updates again for the 2
// bottom lobes, for analytical lights.

// but not vLayer*[1] - this is the media "layer")
// Obviously coat roughness is given without ComputeAdding calculations (nothing on top)
//
//preLightData.iblPerceptualRoughness[COAT_LOBE_IDX] = preLightData.vLayerPerceptualRoughness[TOP_VLAYER_IDX];
// ( preLightData.iblPerceptualRoughness[COAT_LOBE_IDX] = preLightData.vLayerPerceptualRoughness[TOP_VLAYER_IDX]; )
#ifdef VLAYERED_RECOMPUTE_PERLIGHT
bool perLightOption = true;

bool haveAnisotropy = HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_ANISOTROPY);
if( !calledPerLight )
{
// First, if we're not called per light, always (regardless of perLightOption) calculate
// roughness for top lobe: no adding( ) modifications, just conversion + clamping for
// analytical lights. For these we also don't need to recompute these, but only the Fresnel
// or FGD term are necessary in ComputeAdding, see BSDF().
preLightData.iblPerceptualRoughness[COAT_LOBE_IDX] = bsdfData.coatPerceptualRoughness;
preLightData.layeredCoatRoughness = ClampRoughnessForAnalyticalLights(bsdfData.coatRoughness);
}
// calledPerLight and all of the bools above are static time known.
// What we have to calculate is a bit messy here.
// Basically, If we're in a mode where we will compute the vlayer stats per analytical light,

// Otherwise, depending on if we have anisotropy or not, we might still have to deal with
// the T and B terms to have isotropic modulation by the above layer and re-infer back a
// a corrected anisotropy and scalar roughness for use with the IBL hack.
// That hack adds complexity because IBLs they can't use the T and B roughnesses but at the
// That hack adds complexity because IBLs can't use the T and B roughnesses but at the
// roughness in the anisotropic direction.
// roughness in the anisotropic direction, which is incorrect and with the hack, will appear
// even more so.
if( !calledPerLight && !haveAnisotropy)
{

//s_r12 = RoughnessToLinearVariance(PerceptualRoughnessToRoughness(bsdfData.perceptualRoughnessA));
s_r12 = RoughnessToLinearVariance(bsdfData.roughnessAT);
_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
float tmpA = _s_r0m;
float varianceLobeA = _s_r0m;
float tmpB = _s_r0m;
float varianceLobeB = _s_r0m;
preLightData.iblPerceptualRoughness[BASE_LOBEB_IDX] = LinearVarianceToPerceptualRoughness(_s_r0m);
if( !perLightOption )

preLightData.layeredRoughnessT[0] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(tmpA));
preLightData.layeredRoughnessT[1] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(tmpB));
preLightData.layeredRoughnessT[0] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(varianceLobeA));
preLightData.layeredRoughnessT[1] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(varianceLobeB));
}
}

_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
float roughnessB = LinearVarianceToRoughness(_s_r0m);
//ConvertRoughnessToAnisotropy(roughnessT, roughnessB, preLightData.iblAnisotropy[0]);
#ifdef VLAYERED_IBL_DESTRETCH_COMPENSATION_FOR_ANISOTROPY
ConvertRoughnessToAnisotropy(roughnessT, roughnessB, preLightData.iblAnisotropy[0]);
#else
#endif
// We're not going to get called again per analytical light so store the result needed and used by them:
// LOBEA T and B part:
preLightData.layeredRoughnessT[0] = ClampRoughnessForAnalyticalLights(roughnessT);

// We do the same for LOBEB:
// -------------------------
// LOBEB roughness for analytical lights (T part)
s_r12 = RoughnessToLinearVariance(bsdfData.roughnessBT);

_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
roughnessB = LinearVarianceToRoughness(_s_r0m);
// ConvertRoughnessToAnisotropy(roughnessT, roughnessB, preLightData.iblAnisotropy[1]);
#ifdef VLAYERED_IBL_DESTRETCH_COMPENSATION_FOR_ANISOTROPY
ConvertRoughnessToAnisotropy(roughnessT, roughnessB, preLightData.iblAnisotropy[1]);
#else
preLightData.iblPerceptualRoughness[BASE_LOBEB_IDX] = RoughnessToPerceptualRoughness((roughnessT + roughnessB)/2.0);
#endif
preLightData.iblPerceptualRoughness[BASE_LOBEA_IDX] = RoughnessToPerceptualRoughness((roughnessT + roughnessB)/2.0);
// LOBEB T and B part:
// LOBEA T and B part:
}
} // if( !calledPerLight && haveAnisotropy)
if( calledPerLight )
{

_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
preLightData.layeredRoughnessT[0] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(_s_r0m));
// LOBEB roughness for analytical (T part)
// LOBEB roughness for analytical lights (T part)
s_r12 = RoughnessToLinearVariance(bsdfData.roughnessBT);
_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
preLightData.layeredRoughnessT[1] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(_s_r0m));

// LOBEA roughness for analytical (B part)
// LOBEA roughness for analytical lights (B part)
// LOBEB roughness for analytical (B part)
// LOBEB roughness for analytical lights (B part)
s_r12 = RoughnessToLinearVariance(bsdfData.roughnessBB);
_s_r0m = s_ti0 + j0i*(s_t0i + s_r12 + m_rr*(s_r12+s_ri0));
preLightData.layeredRoughnessB[1] = ClampRoughnessForAnalyticalLights(LinearVarianceToRoughness(_s_r0m));

#ifdef VLAYERED_DIFFUSE_ENERGY_HACKED_TERM
// TODO
// Obviously not correct since this is directional
// probably too much removed, but with a non FGD term, could
// actually balance out (as using FGD would lower this)
// diffuseEnergy = Max3( Ti0.r, Ti0.g, Ti0.b);
// Not correct since these stats are still directional probably too much
// removed, but with a non FGD term, could actually balance out (as using
// FGD would lower this)
#else
preLightData.diffuseEnergy = float3(1.0, 1.0, 1.0);
#endif

preLightData.NdotV = dot(N, V);
float NdotV = ClampNdotV(preLightData.NdotV);
preLightData.diffuseEnergy = float3(1.0, 1.0, 1.0);
// iblPerceptualRoughness for FGD, mip, etc
// iblR (fetch direction compensated dominant spec)
// specularFGD (coatIblF is now in there too)
// energyCompensation (for all light, apply everytime since with layering it becomes
// lobe specific)
// iblPerceptualRoughness (for FGD, mip, etc)
// iblR (fetch direction compensated dominant spec)
// specularFGD (coatIblF is now in there too)
// energyCompensation (to a apply for each light sample since with layering it becomes interface specific)
//
// We also need for analytical lights:
//

// The later are done here only if we're not using VLAYERED_RECOMPUTE_PERLIGHT.
//
// The later are done in ComputeAdding at GetPreLightData time only if we're not using
// VLAYERED_RECOMPUTE_PERLIGHT.
//
float3 iblN[TOTAL_NB_LOBES]; //ZERO_INITIALIZE(float3[TOTAL_NB_LOBES], iblN);
float3 iblR[TOTAL_NB_LOBES]; //ZERO_INITIALIZE(float3[TOTAL_NB_LOBES], iblR);
float3 iblN[TOTAL_NB_LOBES]; //ZERO_INITIALIZE(float3[TOTAL_NB_LOBES], iblN);
float3 iblR[TOTAL_NB_LOBES];
// See the struct PreLightData, to simplify the IBL loop, we will recopy these
//preLightData.fresnel0[BASE_LOBEA_IDX] = bsdfData.fresnel0;
//preLightData.fresnel0[BASE_LOBEB_IDX] = bsdfData.fresnel0;
//preLightData.fresnel0[COAT_LOBE_IDX] = IorToFresnel0(bsdfData.coatIor);
preLightData.coatIeta = 1.0 / GetCoatEta(bsdfData);
#ifdef _MATERIAL_FEATURE_COAT
// --------------------------------------------------------------------
// VLAYERING:
// --------------------------------------------------------------------
// Obviously coat roughness is given without ComputeAdding calculations (nothing on top)
preLightData.iblPerceptualRoughness[COAT_LOBE_IDX] = bsdfData.coatPerceptualRoughness;
preLightData.layeredCoatRoughness = ClampRoughnessForAnalyticalLights(bsdfData.coatRoughness);
preLightData.coatIeta = 1.0 / GetCoatEta(bsdfData);
// First thing we need is compute the energy coefficients and new roughnesses.
// Even if configured to do it also per analytical light, we need it for IBLs too.

float BdotV = dot(bsdfData.bitangentWS, V);
#ifndef VLAYERED_RECOMPUTE_PERLIGHT
// We can precalculate lambdaVs for all lights here since we're not doing ComputeAdding per light
#else
// Store those for eval analytical lights since we're going to
// recalculate lambdaV after each ComputeAdding for each light
preLightData.TdotV = TdotV;
preLightData.BdotV = BdotV;
#endif
// For GGX aniso and IBL we have done an empirical (eye balled) approximation compare to the reference.
// We use a single fetch, and we stretch the normal to use based on various criteria.

{
#ifndef VLAYERED_RECOMPUTE_PERLIGHT
// We can precalculate lambdaVs for all lights here since we're not doing ComputeAdding per light
preLightData.partLambdaV[COAT_LOBE_IDX] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredCoatRoughness);
preLightData.partLambdaV[BASE_LOBEA_IDX] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[0]);
preLightData.partLambdaV[BASE_LOBEB_IDX] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[1]);

float specularReflectivityBase = lerp(specularReflectivity[BASE_LOBEA_IDX], specularReflectivity[BASE_LOBEB_IDX], bsdfData.lobeMix);
//preLightData.energyCompensation[BASE_LOBEA_IDX] = CalculateEnergyCompensationFromSpecularReflectivity(specularReflectivityBase);
//preLightData.energyCompensation[BASE_LOBEB_IDX] = preLightData.energyCompensation[BASE_LOBEA_IDX];
//preLightData.energyCompensation[COAT_LOBE_IDX] = CalculateEnergyCompensationFromSpecularReflectivity(specularReflectivity[COAT_LOBE_IDX]);
preLightData.energyFactor[BASE_LOBEA_IDX] = GetEnergyCompensationFactor(specularReflectivityBase, bsdfData.fresnel0);
preLightData.energyFactor[BASE_LOBEB_IDX] = GetEnergyCompensationFactor(specularReflectivityBase, bsdfData.fresnel0);
preLightData.energyFactor[COAT_LOBE_IDX] = GetEnergyCompensationFactor(specularReflectivity[COAT_LOBE_IDX], IorToFresnel0(bsdfData.coatIor));
preLightData.energyCompensationFactor[BASE_LOBEA_IDX] = GetEnergyCompensationFactor(specularReflectivityBase, bsdfData.fresnel0);
preLightData.energyCompensationFactor[BASE_LOBEB_IDX] = GetEnergyCompensationFactor(specularReflectivityBase, bsdfData.fresnel0);
preLightData.energyCompensationFactor[COAT_LOBE_IDX] = GetEnergyCompensationFactor(specularReflectivity[COAT_LOBE_IDX], IorToFresnel0(bsdfData.coatIor));
//preLightData.energyCompensation[COAT_LOBE_IDX] =
//preLightData.energyCompensation[BASE_LOBEA_IDX] =
//preLightData.energyCompensation[BASE_LOBEB_IDX] = 0.0;
preLightData.energyFactor[BASE_LOBEA_IDX] =
preLightData.energyFactor[BASE_LOBEB_IDX] =
preLightData.energyFactor[COAT_LOBE_IDX] = 1.0;
preLightData.energyCompensationFactor[BASE_LOBEA_IDX] =
preLightData.energyCompensationFactor[BASE_LOBEB_IDX] =
preLightData.energyCompensationFactor[COAT_LOBE_IDX] = 1.0;
#endif //ifdef _MATERIAL_FEATURE_COAT
// --------------------------------------------------------------------
// --------------------------------------------------------------------
// To make BSDF( ) evaluation more generic, even if we're not vlayered,
// we will use these:
// See ConvertSurfaceDataToBSDFData : The later are already clamped if
// vlayering is disabled, so could be used directly, but for later
// refactoring (instead of BSDFdata A and B values, we should really
// permit array definitions in the shader include attributes TODOTODO)
preLightData.layeredRoughnessT[0] = bsdfData.roughnessAT; // the later are already clamped
preLightData.layeredRoughnessT[0] = bsdfData.roughnessAT;
preLightData.layeredRoughnessB[0] = bsdfData.roughnessAB;
preLightData.layeredRoughnessT[1] = bsdfData.roughnessBT;
preLightData.layeredRoughnessB[1] = bsdfData.roughnessBB;

}
else
{
preLightData.partLambdaV[0] = GetSmithJointGGXPartLambdaV(NdotV, bsdfData.roughnessAT);
preLightData.partLambdaV[1] = GetSmithJointGGXPartLambdaV(NdotV, bsdfData.roughnessBT);
preLightData.partLambdaV[0] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[0]);
preLightData.partLambdaV[1] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[1]);
iblN[0] = iblN[1] = N;
} // ...no anisotropy

// BSDF share between directional light, punctual light and area light (reference)
//-----------------------------------------------------------------------------
void CalculateAnisoAngles(out float TdotH, out float TdotL, out float BdotH, out float BdotL)
void CalculateAnisoAngles(BSDFData bsdfData, float3 L, float3 V, float invLenLV, out float TdotH, out float TdotL, out float BdotH, out float BdotL)
{
float3 H = (L + V) * invLenLV;

{
float3 N = bsdfData.normalWS;
// Optimized math. Ref: PBR Diffuse Lighting for GGX + Smith Microsurfaces (slide 114).
float LdotV = dot(L, V);
float invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS)); // invLenLV = rcp(length(L + V)), clamp to avoid rsqrt(0) = NaN

// TODO: Proper Fresnel
float3 F = F_Schlick(bsdfData.fresnel0, LdotH);
// TODO: with iridescence, will be per light sample.
// TODO: with iridescence, will be optionally per light sample
#if 0
#if 0
// If we're going to do heavy calculations anyways, maybe we can even do that:
// Use the proper angle at the bottom interface for BSDF calculations:
// (tricky which one to choose, as we have the energy terms already, which are
// a bit like FGD, while at the same time, we have multiple-scattering, but
// if we have anisotropy, we know the later isn't accounted for in ComputeAdding.
// In the IBL case, we don't have a specific incoming light direction but we
// don't handle anisotropy correctly either anyway (ComputeAdding) in both case
// we need to work around it.
float3 H = (L + V) * invLenLV;
L = CoatRefract(L, H, preLightData.coatIeta); // H stays the same
// --------------------------------------------------------------------
// VLAYERING:
// --------------------------------------------------------------------
#ifdef _MATERIAL_FEATURE_COAT
float topLdotH = LdotH; // == VdotH)
float topNdotH = NdotH;
float topNdotL = NdotL;
float topNdotV = NdotV;
#if VLAYERED_USE_REFRACTED_ANGLES_FOR_BASE
// Use the refracted angle at the bottom interface for BSDF calculations:
// Seems like the more correct ones to use, but not obvious as we have the energy
// coefficients already (vLayerEnergyCoeff), which are like FGD (but no deferred
// FGD fetch to do here for analytical lights), so normally, we should use
// an output lobe parametrization, but multiple-scattering is not accounted fully
// byt ComputeAdding (by deriving azimuth dependent covariance operators).
// In the IBL case, we don't have a specific incoming light direction so the light
// representation matches more the correct context of a split sum approximation,
// though we don't handle anisotropy correctly either anyway.
// In both cases we need to work around it, so just to test:
float3 H = (L + V) * invLenLV;
// H stays the same so calculate it one time
V = CoatRefract(V, H, preLightData.coatIeta);
L = reflect(-V, H);
LdotV = dot(L, V);
invLenLV = rsqrt(max(2.0 * LdotV + 2.0, FLT_EPS)); // invLenLV = rcp(length(L + V)), clamp to avoid rsqrt(0) = NaN
NdotH = saturate((NdotL + dot(N, V)) * invLenLV); // Do not clamp NdotV here
LdotH = saturate(invLenLV * LdotV + invLenLV);
NdotV = ClampNdotV(dot(N, V));
#ifndef VLAYERED_RECOMPUTE_PERLIGHT
#ifdef VLAYERED_RECOMPUTE_PERLIGHT
ComputeAdding(LdotH, bsdfData, preLightData, true); // Notice LdotH as interface angle for energy calculations and roughness modifications
ComputeAdding(topLdotH, bsdfData, preLightData, true);
// Notice topLdotH as interface angle, symmetric model parametrization (see sec. 6 and comments
// on ComputeAdding)
// layered*Roughness* and vLayerEnergyCoeff are now updated for the proper light direction.
preLightData.partLambdaV[BASE_LOBEA_IDX] = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, NdotV, preLightData.layeredRoughnessT[0], preLightData.layeredRoughnessB[0]);
preLightData.partLambdaV[BASE_LOBEB_IDX] = GetSmithJointGGXAnisoPartLambdaV(TdotV, BdotV, NdotV, preLightData.layeredRoughnessT[1], preLightData.layeredRoughnessB[1]);
// for our LdotH, too bad, along with the "wrongly" calculated energy.
// for our LdotH, too bad (similar to what we do with iridescence), along with the "wrongly" calculated energy.
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_ANISOTROPY))
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_ANISOTROPY))
#ifdef VLAYERED_RECOMPUTE_PERLIGHT
#ifdef VLAYERED_USE_REFRACTED_ANGLES_FOR_BASE
// we just changed V, update those:
preLightData.TdotV = dot(bsdfData.tangentWS, V);
preLightData.BdotV = dot(bsdfData.bitangentWS, V);
#endif
preLightData.partLambdaV[BASE_LOBEA_IDX] = GetSmithJointGGXAnisoPartLambdaV(preLightData.TdotV, preLightData.BdotV, NdotV,
preLightData.layeredRoughnessT[0], preLightData.layeredRoughnessB[0]);
preLightData.partLambdaV[BASE_LOBEB_IDX] = GetSmithJointGGXAnisoPartLambdaV(preLightData.TdotV, preLightData.BdotV, NdotV,
preLightData.layeredRoughnessT[1], preLightData.layeredRoughnessB[1]);
#endif
CalculateAnisoAngles(TdotH, TdotL, BdotH, BdotL);
CalculateAnisoAngles(bsdfData, L, V, invLenLV, TdotH, TdotL, BdotH, BdotL);
DV[BASE_LOBEA_IDX] = DV_SmithJointGGXAniso(TdotH, BdotH, NdotH, NdotV, TdotL, BdotL, NdotL,
preLightData.layeredRoughnessT[0], preLightData.layeredRoughnessB[0],

preLightData.layeredRoughnessT[1], preLightData.layeredRoughnessB[1],
preLightData.partLambdaV[BASE_LOBEB_IDX]);
DV[COAT_LOBE_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredCoatRoughness, preLightData.partLambdaV[COAT_LOBE_IDX]);
DV[COAT_LOBE_IDX] = DV_SmithJointGGX(topNdotH, topNdotL, topNdotV, preLightData.layeredCoatRoughness, preLightData.partLambdaV[COAT_LOBE_IDX]);
DV[COAT_LOBE_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredCoatRoughness, preLightData.partLambdaV[COAT_LOBE_IDX]);
DV[BASE_LOBEA_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredRoughnessT[0], preLightData.partLambdaV[0]);
DV[BASE_LOBEB_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredRoughnessT[1], preLightData.partLambdaV[1]);
#ifdef VLAYERED_RECOMPUTE_PERLIGHT
preLightData.partLambdaV[BASE_LOBEA_IDX] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[0]);
preLightData.partLambdaV[BASE_LOBEB_IDX] = GetSmithJointGGXPartLambdaV(NdotV, preLightData.layeredRoughnessT[1]);
#endif
DV[COAT_LOBE_IDX] = DV_SmithJointGGX(topNdotH, topNdotL, topNdotV, preLightData.layeredCoatRoughness, preLightData.partLambdaV[COAT_LOBE_IDX]);
DV[BASE_LOBEA_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredRoughnessT[0], preLightData.partLambdaV[BASE_LOBEA_IDX]);
DV[BASE_LOBEB_IDX] = DV_SmithJointGGX(NdotH, NdotL, NdotV, preLightData.layeredRoughnessT[1], preLightData.partLambdaV[BASE_LOBEB_IDX]);
specularLighting = preLightData.vLayerEnergyCoeff[TOP_VLAYER_IDX]
* preLightData.energyFactor[BASE_LOBEA_IDX] // either energyFactor index will do, same one.
specularLighting = preLightData.vLayerEnergyCoeff[BOTTOM_VLAYER_IDX]
* preLightData.energyCompensationFactor[BASE_LOBEA_IDX] // either energyCompensationFactor index will do, same one.
specularLighting += preLightData.vLayerEnergyCoeff[BOTTOM_VLAYER_IDX]
* preLightData.energyFactor[COAT_LOBE_IDX]
specularLighting += preLightData.vLayerEnergyCoeff[TOP_VLAYER_IDX]
* preLightData.energyCompensationFactor[COAT_LOBE_IDX]
}
#endif // ..._MATERIAL_FEATURE_COAT
} // if( IsVLayeredEnabled(bsdfData) )
#endif
// No vlayering :
// ------------------------------------
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_LIT_ANISOTROPY))
// --------------------------------------------------------------------
// NO VLAYERING:
// --------------------------------------------------------------------
if (HasFeatureFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_ANISOTROPY))
CalculateAnisoAngles(TdotH, TdotL, BdotH, BdotL);
CalculateAnisoAngles(bsdfData, L, V, invLenLV, TdotH, TdotL, BdotH, BdotL);
bsdfData.roughnessAT, bsdfData.roughnessAB, preLightData.partLambdaV[0]);
bsdfData.roughnessAT, bsdfData.roughnessAB,
preLightData.partLambdaV[0]);
bsdfData.roughnessBT, bsdfData.roughnessBB, preLightData.partLambdaV[1]);
bsdfData.roughnessBT, bsdfData.roughnessBB,
preLightData.partLambdaV[1]);
}
else
{

specularLighting = F * lerp(DV[0], DV[1], bsdfData.lobeMix);
//...and energy compensation is applied at PostEvaluateBSDF when no vlayering.
// TODO: config option + diffuse GGX
float3 diffuseTerm = Lambert();
#ifdef VLAYERED_DIFFUSE_ENERGY_HACKED_TERM
// TODO: config option + diffuse GGX
float diffuseTerm = Lambert();
if( IsVLayeredEnabled(bsdfData) )
{
diffuseTerm *= preLightData.diffuseEnergy;
}
#endif
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF().
diffuseLighting = diffuseTerm;

{
IndirectLighting lighting;
ZERO_INITIALIZE(IndirectLighting, lighting);
//return lighting;
// TODO: Refraction
// There is no coat handling in Lit for refractions.
// Here handle one lobe instead of all the others basically, or we could want it all.

// Note that when we're not vlayered, we apply it not at each light sample but at the end,
// at PostEvaluateBSDF.
// Incorrect, but just for now:
//L = ApplyEnergyCompensationToSpecularLighting(L, preLightData.fresnel0[i], preLightData.energyCompensation[i]);
L *= preLightData.energyFactor[i];
L *= preLightData.energyCompensationFactor[i];
}
envLighting += L;
}

4
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLit.shader


_Anisotropy("Anisotropy", Range(-1.0, 1.0)) = 0.0
[ToggleUI] _CoatEnable("Coat Enable", Float) = 0.0 // UI only
_CoatSmoothness("Coat Smoothness", Range(0.0, 1.0)) = 1.0
_CoatIor("Coat IOR", Range(1.0, 2.0)) = 1.5
_CoatIor("Coat IOR", Range(1.0001, 2.0)) = 1.5
_CoatExtinction("Coat Extinction Coefficient", Color) = (1,1,1,1) // in thickness^-1 units
_CoatExtinction("Coat Extinction Coefficient", Color) = (1,1,1) // in thickness^-1 units
[HideInInspector] _NormalMapShow("NormalMap Show", Float) = 0.0
_NormalMap("NormalMap", 2D) = "bump" {} // Tangent space normal map

2
ScriptableRenderPipeline/HDRenderPipeline/HDRP/Material/StackLit/StackLitProperties.hlsl


float _CoatSmoothness;
float _CoatIor;
float _CoatThickness;
float4 _CoatExtinction;
float3 _CoatExtinction;
float _NormalScale;
float _NormalMapUV;

正在加载...
取消
保存