}
// Precompute part of lambdaV
float GetSmithJointGGXPre LambdaV(float NdotV, float roughness)
float GetSmithJointGGXPart LambdaV(float NdotV, float roughness)
{
float a2 = roughness * roughness;
return sqrt((-NdotV * a2 + NdotV) * NdotV + a2);
// Ref: http://jcgt.org/published/0003/02/03/paper.pdf
float V_SmithJointGGX(float NdotL, float NdotV, float roughness, float pre LambdaV)
float V_SmithJointGGX(float NdotL, float NdotV, float roughness, float part LambdaV)
{
float a2 = roughness * roughness;
// G = 1 / (1 + lambda_v + lambda_l);
// Reorder code to be more optimal:
float lambdaV = NdotL * pre LambdaV;
float lambdaV = NdotL * part LambdaV;
float lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
// Simplify visibility term: (2.0 * NdotL * NdotV) / ((4.0 * NdotL * NdotV) * (lambda_v + lambda_l));
float V_SmithJointGGX(float NdotL, float NdotV, float roughness)
{
float preLambdaV = GetSmithJointGGXPre LambdaV(NdotV, roughness);
return V_SmithJointGGX(NdotL, NdotV, roughness, pre LambdaV);
float partLambdaV = GetSmithJointGGXPart LambdaV(NdotV, roughness);
return V_SmithJointGGX(NdotL, NdotV, roughness, part LambdaV);
float DV_SmithJointGGX(float NdotH, float NdotL, float NdotV, float roughness, float pre LambdaV)
float DV_SmithJointGGX(float NdotH, float NdotL, float NdotV, float roughness, float part LambdaV)
float lambdaV = NdotL * pre LambdaV;
float lambdaV = NdotL * part LambdaV;
float lambdaL = NdotV * sqrt((-NdotL * a2 + NdotL) * NdotL + a2);
float2 G = float2(1, lambdaV + lambdaL); // Fraction without the constant (0.5)
float DV_SmithJointGGX(float NdotH, float NdotL, float NdotV, float roughness)
{
float preLambdaV = GetSmithJointGGXPre LambdaV(NdotV, roughness);
return DV_SmithJointGGX(NdotH, NdotL, NdotV, roughness, pre LambdaV);
float partLambdaV = GetSmithJointGGXPart LambdaV(NdotV, roughness);
return DV_SmithJointGGX(NdotH, NdotL, NdotV, roughness, part LambdaV);
}
// Precompute a part of LambdaV.
// https://www.desmos.com/calculator/wtp8lnjutx
float GetSmithJointGGXPre LambdaVApprox(float NdotV, float roughness)
float GetSmithJointGGXPart LambdaVApprox(float NdotV, float roughness)
float V_SmithJointGGXApprox(float NdotL, float NdotV, float roughness, float pre LambdaV)
float V_SmithJointGGXApprox(float NdotL, float NdotV, float roughness, float part LambdaV)
float lambdaV = NdotL * pre LambdaV;
float lambdaV = NdotL * part LambdaV;
float lambdaL = NdotV * (NdotL * (1 - a) + a);
return 0.5 / (lambdaV + lambdaL);
{
float preLambdaV = GetSmithJointGGXPre LambdaVApprox(NdotV, roughness);
return V_SmithJointGGXApprox(NdotL, NdotV, roughness, pre LambdaV);
float partLambdaV = GetSmithJointGGXPart LambdaVApprox(NdotV, roughness);
return V_SmithJointGGXApprox(NdotL, NdotV, roughness, part LambdaV);
}
// roughnessT -> roughness in tangent direction
return INV_PI * D_GGXAnisoNoPI(TdotH, BdotH, NdotH, roughnessT, roughnessB);
}
float GetSmithJointGGXAnisoPre LambdaV(float TdotV, float BdotV, float NdotV, float roughnessT, float roughnessB)
float GetSmithJointGGXAnisoPart LambdaV(float TdotV, float BdotV, float NdotV, float roughnessT, float roughnessB)
{
float aT2 = roughnessT * roughnessT;
float aB2 = roughnessB * roughnessB;
// Note: V = G / (4 * NdotL * NdotV)
// Ref: https://cedec.cesa.or.jp/2015/session/ENG/14698.html The Rendering Materials of Far Cry 4
float V_SmithJointGGXAniso(float TdotV, float BdotV, float NdotV, float TdotL, float BdotL, float NdotL, float roughnessT, float roughnessB, float pre LambdaV)
float V_SmithJointGGXAniso(float TdotV, float BdotV, float NdotV, float TdotL, float BdotL, float NdotL, float roughnessT, float roughnessB, float part LambdaV)
float lambdaV = NdotL * pre LambdaV;
float lambdaV = NdotL * part LambdaV;
float lambdaL = NdotV * sqrt(aT2 * TdotL * TdotL + aB2 * BdotL * BdotL + NdotL * NdotL);
return 0.5 / (lambdaV + lambdaL);
{
float preLambdaV = GetSmithJointGGXAnisoPre LambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB);
return V_SmithJointGGXAniso(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB, pre LambdaV);
float partLambdaV = GetSmithJointGGXAnisoPart LambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB);
return V_SmithJointGGXAniso(TdotV, BdotV, NdotV, TdotL, BdotL, NdotL, roughnessT, roughnessB, part LambdaV);
}
// Inline D_GGXAniso() * V_SmithJointGGXAniso() together for better code generation.
float roughnessT, float roughnessB, float pre LambdaV)
float roughnessT, float roughnessB, float part LambdaV)
{
float aT2 = roughnessT * roughnessT;
float aB2 = roughnessB * roughnessB;
float lambdaV = NdotL * pre LambdaV;
float lambdaV = NdotL * part LambdaV;
float lambdaL = NdotV * sqrt(aT2 * TdotL * TdotL + aB2 * BdotL * BdotL + NdotL * NdotL);
float2 G = float2(1, lambdaV + lambdaL); // Fraction without the constant (0.5)
float TdotL, float BdotL, float NdotL,
float roughnessT, float roughnessB)
{
float preLambdaV = GetSmithJointGGXAnisoPre LambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB);
float partLambdaV = GetSmithJointGGXAnisoPart LambdaV(TdotV, BdotV, NdotV, roughnessT, roughnessB);
roughnessT, roughnessB, pre LambdaV);
roughnessT, roughnessB, part LambdaV);
}
//-----------------------------------------------------------------------------