|
|
|
|
|
|
struct PreLightData |
|
|
|
{ |
|
|
|
// General |
|
|
|
float NdotV; // Geometric version (not clamped) |
|
|
|
float NdotV; // Geometric version (could be negative) |
|
|
|
|
|
|
|
// GGX iso |
|
|
|
float ggxLambdaV; |
|
|
|
|
|
|
float3 specularFGD; // Store preconvoled BRDF for both specular and diffuse |
|
|
|
float diffuseFGD; |
|
|
|
|
|
|
|
// area light |
|
|
|
float3x3 orthoBasisVN; // Right-handed view-dependent orthogonal basis around the normal |
|
|
|
float3x3 ltcXformGGX; // Sparse: should only use 4x VGPRs. Could be scalarized |
|
|
|
float3x3 ltcXformDisneyDiffuse; // Sparse: should only use 4x VGPRs. Could be scalarized |
|
|
|
float ltcGGXFresnelMagnitudeDiff; // The difference of magnitudes of GGX and Fresnel |
|
|
|
float ltcGGXFresnelMagnitude; |
|
|
|
float ltcDisneyDiffuseMagnitude; |
|
|
|
// Area lights (17 VGPRs). Scalarize? |
|
|
|
float3x3 orthoBasisViewNormal; // Right-handed view-dependent orthogonal basis around the normal (6x VGPRs) |
|
|
|
float3x3 ltcTransformDiffuse; // Inverse transformation for Lambertian or Disney Diffuse (4x VGPRs) |
|
|
|
float3x3 ltcTransformSpecular; // Inverse transformation for GGX (4x VGPRs) |
|
|
|
float ltcMagnitudeDiffuse; |
|
|
|
float3 ltcMagnitudeFresnel; |
|
|
|
}; |
|
|
|
|
|
|
|
PreLightData GetPreLightData(float3 V, PositionInputs posInput, BSDFData bsdfData) |
|
|
|
|
|
|
preLightData.iblMipLevel = PerceptualRoughnessToMipmapLevel(bsdfData.perceptualRoughness); |
|
|
|
|
|
|
|
// Area light |
|
|
|
preLightData.orthoBasisVN[0] = normalize(V - bsdfData.normalWS * preLightData.NdotV); |
|
|
|
preLightData.orthoBasisVN[1] = normalize(cross(bsdfData.normalWS, preLightData.orthoBasisVN[0])); |
|
|
|
preLightData.orthoBasisVN[2] = bsdfData.normalWS; |
|
|
|
|
|
|
|
// Note we load the matrix transpose (avoid to have to transpose it in shader) |
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
preLightData.ltcTransformDiffuse = k_identity3x3; |
|
|
|
#else |
|
|
|
// Get the inverse LTC matrix for Disney Diffuse |
|
|
|
preLightData.ltcTransformDiffuse = 0.0; |
|
|
|
preLightData.ltcTransformDiffuse._m22 = 1.0; |
|
|
|
preLightData.ltcTransformDiffuse._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, ltc_linear_clamp_sampler, uv, LTC_DISNEY_DIFFUSE_MATRIX_INDEX, 0); |
|
|
|
#endif |
|
|
|
|
|
|
|
preLightData.ltcXformGGX = 0.0; |
|
|
|
preLightData.ltcXformGGX._m22 = 1.0; |
|
|
|
preLightData.ltcXformGGX._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, ltc_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0); |
|
|
|
preLightData.ltcTransformSpecular = 0.0; |
|
|
|
preLightData.ltcTransformSpecular._m22 = 1.0; |
|
|
|
preLightData.ltcTransformSpecular._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, ltc_linear_clamp_sampler, uv, LTC_GGX_MATRIX_INDEX, 0); |
|
|
|
// Get the inverse LTC matrix for Disney Diffuse |
|
|
|
// Note we load the matrix transpose (avoid to have to transpose it in shader) |
|
|
|
preLightData.ltcXformDisneyDiffuse = 0.0; |
|
|
|
preLightData.ltcXformDisneyDiffuse._m22 = 1.0; |
|
|
|
preLightData.ltcXformDisneyDiffuse._m00_m02_m11_m20 = SAMPLE_TEXTURE2D_ARRAY_LOD(_LtcData, ltc_linear_clamp_sampler, uv, LTC_DISNEY_DIFFUSE_MATRIX_INDEX, 0); |
|
|
|
// Construct a right-handed view-dependent orthogonal basis around the normal |
|
|
|
preLightData.orthoBasisViewNormal[0] = normalize(V - bsdfData.normalWS * preLightData.NdotV); |
|
|
|
preLightData.orthoBasisViewNormal[2] = bsdfData.normalWS; |
|
|
|
preLightData.orthoBasisViewNormal[1] = normalize(cross(preLightData.orthoBasisViewNormal[2], preLightData.orthoBasisViewNormal[0])); |
|
|
|
preLightData.ltcGGXFresnelMagnitudeDiff = ltcMagnitude.r; |
|
|
|
preLightData.ltcGGXFresnelMagnitude = ltcMagnitude.g; |
|
|
|
preLightData.ltcDisneyDiffuseMagnitude = ltcMagnitude.b; |
|
|
|
float ltcGGXFresnelMagnitudeDiff = ltcMagnitude.r; |
|
|
|
float ltcGGXFresnelMagnitude = ltcMagnitude.g; |
|
|
|
float ltcDisneyDiffuseMagnitude = ltcMagnitude.b; |
|
|
|
|
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
preLightData.ltcMagnitudeDiffuse = 1; |
|
|
|
#else |
|
|
|
preLightData.ltcMagnitudeDiffuse = ltcDisneyDiffuseMagnitude; |
|
|
|
#endif |
|
|
|
|
|
|
|
// 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 + ltcGGXFresnelMagnitude; |
|
|
|
|
|
|
|
return preLightData; |
|
|
|
} |
|
|
|
|
|
|
// Currently, we only model diffuse transmission. Specular transmission is not yet supported. |
|
|
|
// We assume that the back side of the object is a uniformly illuminated infinite plane |
|
|
|
// with the reversed normal (and the view vector) of the current sample. |
|
|
|
float3 EvaluateTransmission(BSDFData bsdfData, float intensity, float diffuseScale, float shadow) |
|
|
|
float3 EvaluateTransmission(BSDFData bsdfData, float intensity, float shadow) |
|
|
|
float backLight = intensity * shadow * diffuseScale; |
|
|
|
float backLight = intensity * shadow; |
|
|
|
|
|
|
|
return backLight * bsdfData.transmittance; // Premultiplied with the diffuse color |
|
|
|
} |
|
|
|
|
|
|
float illuminance = Lambert() * ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
|
|
|
|
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass. |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, illuminance, lightData.diffuseScale, shadow); |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, illuminance * lightData.diffuseScale, shadow); |
|
|
|
} |
|
|
|
|
|
|
|
// Save ALU by applying 'lightData.color' only once. |
|
|
|
|
|
|
float illuminance = Lambert() * ComputeWrappedDiffuseLighting(-NdotL, SSS_WRAP_LIGHT); |
|
|
|
|
|
|
|
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass. |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, illuminance, lightData.diffuseScale, shadow); |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, illuminance * lightData.diffuseScale, shadow); |
|
|
|
} |
|
|
|
|
|
|
|
// Save ALU by applying 'lightData.color' only once. |
|
|
|
|
|
|
float3 P2 = lightData.positionWS + T * (0.5 * len); |
|
|
|
|
|
|
|
// Rotate the endpoints into the local coordinate system. |
|
|
|
P1 = mul(P1, transpose(preLightData.orthoBasisVN)); |
|
|
|
P2 = mul(P2, transpose(preLightData.orthoBasisVN)); |
|
|
|
P1 = mul(P1, transpose(preLightData.orthoBasisViewNormal)); |
|
|
|
P2 = mul(P2, transpose(preLightData.orthoBasisViewNormal)); |
|
|
|
// Compute the binormal. |
|
|
|
// Compute the binormal in the local coordinate system. |
|
|
|
float3 B = normalize(cross(P1, P2)); |
|
|
|
|
|
|
|
float ltcValue; |
|
|
|
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, k_identity3x3); |
|
|
|
#else |
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcXformDisneyDiffuse); |
|
|
|
#endif |
|
|
|
|
|
|
|
#ifndef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
ltcValue *= preLightData.ltcDisneyDiffuseMagnitude; |
|
|
|
#endif |
|
|
|
|
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformDiffuse); |
|
|
|
diffuseLighting = bsdfData.diffuseColor * ltcValue; |
|
|
|
diffuseLighting = bsdfData.diffuseColor * (preLightData.ltcMagnitudeDiffuse * ltcValue); |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmission) |
|
|
|
|
|
|
|
|
|
|
// Use the Lambertian approximation for performance reasons. |
|
|
|
// The matrix multiplication should not generate any extra ALU on GCN. |
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, mul(flipMatrix, k_identity3x3)); |
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, mul(flipMatrix, k_identity3x3)); |
|
|
|
ltcValue *= lightData.diffuseScale; |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, ltcValue, lightData.diffuseScale, 1); |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, ltcValue, 1); |
|
|
|
// 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. |
|
|
|
float3 fresnelTerm = bsdfData.fresnel0 * preLightData.ltcGGXFresnelMagnitudeDiff |
|
|
|
+ (float3)preLightData.ltcGGXFresnelMagnitude; |
|
|
|
|
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcXformGGX); |
|
|
|
ltcValue = LTCEvaluate(P1, P2, B, preLightData.ltcTransformSpecular); |
|
|
|
specularLighting = fresnelTerm * ltcValue; |
|
|
|
specularLighting = preLightData.ltcMagnitudeFresnel * ltcValue; |
|
|
|
} |
|
|
|
|
|
|
|
// Save ALU by applying 'lightData.color' only once. |
|
|
|
|
|
|
lightVerts[3] = lightData.positionWS + lightData.right * -halfWidth + lightData.up * halfHeight; |
|
|
|
|
|
|
|
// Rotate the endpoints into the local coordinate system. |
|
|
|
lightVerts = mul(lightVerts, transpose(preLightData.orthoBasisVN)); |
|
|
|
lightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal)); |
|
|
|
float3x3 ltcMatrix; |
|
|
|
float ltcValue; |
|
|
|
float ltcValue; |
|
|
|
#ifdef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
ltcMatrix = k_identity3x3; |
|
|
|
#else |
|
|
|
ltcMatrix = preLightData.ltcXformDisneyDiffuse; |
|
|
|
#endif |
|
|
|
|
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, ltcMatrix)); |
|
|
|
|
|
|
|
#ifndef LIT_DIFFUSE_LAMBERT_BRDF |
|
|
|
ltcValue *= preLightData.ltcDisneyDiffuseMagnitude; |
|
|
|
#endif |
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformDiffuse)); |
|
|
|
|
|
|
|
diffuseLighting = bsdfData.diffuseColor * ltcValue; |
|
|
|
diffuseLighting = bsdfData.diffuseColor * (preLightData.ltcMagnitudeDiffuse * ltcValue); |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmission) |
|
|
|
|
|
|
|
|
|
|
// Use the Lambertian approximation for performance reasons. |
|
|
|
// The matrix multiplication should not generate any extra ALU on GCN. |
|
|
|
ltcMatrix = mul(flipMatrix, k_identity3x3); |
|
|
|
float3x3 ltcTransform = mul(flipMatrix, k_identity3x3); |
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, ltcMatrix)); |
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, ltcTransform)); |
|
|
|
ltcValue *= lightData.diffuseScale; |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, ltcValue, lightData.diffuseScale, 1); |
|
|
|
diffuseLighting += EvaluateTransmission(bsdfData, ltcValue, 1); |
|
|
|
ltcMatrix = preLightData.ltcXformGGX; |
|
|
|
|
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, ltcMatrix)); |
|
|
|
|
|
|
|
ltcValue = PolygonIrradiance(mul(lightVerts, preLightData.ltcTransformSpecular)); |
|
|
|
|
|
|
|
// 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. |
|
|
|
float3 fresnelTerm = bsdfData.fresnel0 * preLightData.ltcGGXFresnelMagnitudeDiff |
|
|
|
+ (float3)preLightData.ltcGGXFresnelMagnitude; |
|
|
|
|
|
|
|
specularLighting = fresnelTerm * ltcValue; |
|
|
|
specularLighting = preLightData.ltcMagnitudeFresnel * ltcValue; |
|
|
|
} |
|
|
|
|
|
|
|
// Save ALU by applying 'lightData.color' only once. |
|
|
|