|
|
|
|
|
|
#define LTC_LUT_OFFSET (0.5 * rcp(LTC_LUT_SIZE)) |
|
|
|
|
|
|
|
// Subsurface scattering constant |
|
|
|
#define SSS_WRAP_ANGLE (PI/12) // Used for wrap lighting |
|
|
|
#define SSS_WRAP_ANGLE (PI/12) // 15 degrees |
|
|
|
#define SSS_WRAP_LIGHT cos(PI/2 - SSS_WRAP_ANGLE) |
|
|
|
|
|
|
|
CBUFFER_START(UnitySSSParameters) |
|
|
|
|
|
|
#elif LIT_DIFFUSE_GGX_BRDF |
|
|
|
float3 diffuseTerm = DiffuseGGX(bsdfData.diffuseColor, NdotV, NdotL, NdotH, LdotV, bsdfData.roughness); |
|
|
|
#else |
|
|
|
float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotV, bsdfData.perceptualRoughness); |
|
|
|
// A note on subsurface scattering. |
|
|
|
// The correct way to handle SSS is to transmit light inside the surface, perform SSS, |
|
|
|
// and then transmit it outside towards the viewer. |
|
|
|
// Transmit(X) = F_Transm_Schlick(F0, F90, NdotX), where F0 = 0, F90 = 1. |
|
|
|
// Therefore, the diffuse BSDF should be decomposed as follows: |
|
|
|
// f_d = A / Pi * F_Transm_Schlick(0, 1, NdotL) * F_Transm_Schlick(0, 1, NdotV) + f_d_reflection, |
|
|
|
// with F_Transm_Schlick(0, 1, NdotV) applied after the SSS pass. |
|
|
|
// The alternative (artistic) formulation of Disney is to set F90 = 0.5: |
|
|
|
// f_d = A / Pi * F_Transm_Schlick(0, 0.5, NdotL) * F_Transm_Schlick(0, 0.5, NdotV) + f_retro_reflection. |
|
|
|
// That way, darkening at grading angles is reduced to 0.5. |
|
|
|
// In practice, applying F_Transm_Schlick(F0, F90, NdotV) after the SSS pass is expensive, |
|
|
|
// as it forces us to read the normal buffer at the end of the SSS pass. |
|
|
|
// Separating f_retro_reflection also has a small cost (mostly due to energy compensation |
|
|
|
// for multi-bounce GGX), and the visual difference is negligible. |
|
|
|
// Therefore, we choose not to separate diffuse lighting into reflected and transmitted. |
|
|
|
float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotV, bsdfData.perceptualRoughness); |
|
|
|
#endif |
|
|
|
|
|
|
|
// We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF(). |
|
|
|
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmission) |
|
|
|
{ |
|
|
|
// We apply wrapped lighting instead of the regular Lambertian diffuse |
|
|
|
// to compensate for approximations within EvaluateTransmission(). |
|
|
|
float illuminance = Lambert() * ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
// Apply wrapped lighting to better handle thin objects (cards) at grazing angles. |
|
|
|
float wrappedNdotL = ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
|
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
illuminance = Lambert() * wrappedNdotL; |
|
|
|
#else |
|
|
|
float tNdotL = saturate(-NdotL); |
|
|
|
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V); |
|
|
|
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL; |
|
|
|
#endif |
|
|
|
|
|
|
|
// 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(). |
|
|
|
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmission) |
|
|
|
{ |
|
|
|
// We apply wrapped lighting instead of the regular Lambertian diffuse |
|
|
|
// to compensate for approximations within EvaluateTransmission(). |
|
|
|
float illuminance = Lambert() * ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
// Apply wrapped lighting to better handle thin objects (cards) at grazing angles. |
|
|
|
float wrappedNdotL = ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
|
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
illuminance = Lambert() * wrappedNdotL; |
|
|
|
#else |
|
|
|
float tNdotL = saturate(-NdotL); |
|
|
|
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V); |
|
|
|
illuminance = INV_PI * F_Transm_Schlick(0, 0.5, NdotV) * F_Transm_Schlick(0, 0.5, tNdotL) * wrappedNdotL; |
|
|
|
#endif |
|
|
|
|
|
|
|
// 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(). |
|
|
|