|
|
|
|
|
|
#define LTC_DISNEY_DIFFUSE_MATRIX_INDEX 1 // RGBA |
|
|
|
#define LTC_MULTI_GGX_FRESNEL_DISNEY_DIFFUSE_INDEX 2 // RGB, A unused |
|
|
|
|
|
|
|
// SSS parameters |
|
|
|
#define N_PROFILES 8 |
|
|
|
uint _TransmittanceFlags; // One bit per profile; 1 = enabled |
|
|
|
float4 _HalfRcpVariancesAndLerpWeights[N_PROFILES][2]; // 2x Gaussians per color channel, A is the the associated interpolation weight |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// Helper functions/variable specific to this material |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
} |
|
|
|
|
|
|
|
// Evaluates transmittance for a linear combination of two normalized 2D Gaussians. |
|
|
|
// Computes results for each color channel separately. |
|
|
|
// Ref: Real-Time Realistic Skin Translucency (2010), equation 9 (modified). |
|
|
|
float3 ComputeTransmittance(float3 halfRcpVariance1, float lerpWeight1, |
|
|
|
float3 halfRcpVariance2, float lerpWeight2, |
|
|
|
float thickness, float radiusScale) |
|
|
|
{ |
|
|
|
// To be consistent with SSS, we modify the thickness instead of scaling the radius of the profile. |
|
|
|
thickness /= radiusScale; |
|
|
|
|
|
|
|
float t2 = thickness * thickness; |
|
|
|
|
|
|
|
// TODO: 6 exponentials is kind of expensive... Should we use a LUT instead? |
|
|
|
// lerp(exp(-t2 * halfRcpVariance1), exp(-t2 * halfRcpVariance2), lerpWeight2) |
|
|
|
return exp(-t2 * halfRcpVariance1) * lerpWeight1 + exp(-t2 * halfRcpVariance2) * lerpWeight2; |
|
|
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
// conversion function for forward |
|
|
|
//----------------------------------------------------------------------------- |
|
|
|
|
|
|
{ |
|
|
|
bsdfData.diffuseColor = surfaceData.baseColor; |
|
|
|
bsdfData.fresnel0 = 0.028; // TODO take from subsurfaceProfile |
|
|
|
bsdfData.subsurfaceRadius = surfaceData.subsurfaceRadius; |
|
|
|
bsdfData.thickness = surfaceData.thickness; |
|
|
|
bsdfData.subsurfaceRadius = surfaceData.subsurfaceRadius * 0.01; |
|
|
|
bsdfData.thickness = surfaceData.thickness * 0.01; |
|
|
|
bsdfData.enableTransmittance = (1 << bsdfData.subsurfaceProfile) & _TransmittanceFlags; |
|
|
|
if (bsdfData.enableTransmittance) |
|
|
|
{ |
|
|
|
bsdfData.transmittance = ComputeTransmittance(_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][0].xyz, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][0].w, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][1].xyz, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][1].w, |
|
|
|
bsdfData.thickness, bsdfData.subsurfaceRadius); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT) |
|
|
|
{ |
|
|
|
|
|
|
{ |
|
|
|
bsdfData.diffuseColor = baseColor; |
|
|
|
bsdfData.fresnel0 = 0.028; // TODO take from subsurfaceProfile |
|
|
|
bsdfData.subsurfaceRadius = inGBuffer2.r; |
|
|
|
bsdfData.thickness = inGBuffer2.g; |
|
|
|
bsdfData.subsurfaceRadius = inGBuffer2.r * 0.01; |
|
|
|
bsdfData.thickness = inGBuffer2.g * 0.01; |
|
|
|
bsdfData.enableTransmittance = (1 << bsdfData.subsurfaceProfile) & _TransmittanceFlags; |
|
|
|
if (bsdfData.enableTransmittance) |
|
|
|
{ |
|
|
|
bsdfData.transmittance = ComputeTransmittance(_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][0].xyz, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][0].w, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][1].xyz, |
|
|
|
_HalfRcpVariancesAndLerpWeights[bsdfData.subsurfaceProfile][1].w, |
|
|
|
bsdfData.thickness, bsdfData.subsurfaceRadius); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (bsdfData.materialId == MATERIALID_LIT_CLEAR_COAT) |
|
|
|
{ |
|
|
|
|
|
|
float3 L = -lightData.forward; // Lights are pointing backward in Unity |
|
|
|
float illuminance = saturate(dot(bsdfData.normalWS, L)); |
|
|
|
|
|
|
|
diffuseLighting = float3(0.0, 0.0, 0.0); |
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
float3 cookieColor = float3(1.0, 1.0, 1.0); |
|
|
|
diffuseLighting = float3(0.0, 0.0, 0.0); |
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
float4 cookie = float4(1.0, 1.0, 1.0, 1.0); |
|
|
|
float shadowAttenuation = GetDirectionalShadowAttenuation(lightLoopContext, positionWS, lightData.shadowIndex, L, posInput.unPositionSS); |
|
|
|
float shadow = GetDirectionalShadowAttenuation(lightLoopContext, positionWS, lightData.shadowIndex, L, posInput.unPositionSS, 0); |
|
|
|
illuminance *= shadowAttenuation; |
|
|
|
illuminance *= shadow; |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (lightData.cookieIndex >= 0 && illuminance > 0.0) |
|
|
|
|
|
|
coord = coord * 0.5 + 0.5; |
|
|
|
|
|
|
|
// Tile the texture if the 'repeat' wrap mode is enabled. |
|
|
|
if (lightData.tileCookie) |
|
|
|
if (lightData.tileCookie) |
|
|
|
float4 cookie = SampleCookie2D(lightLoopContext, coord, lightData.cookieIndex); |
|
|
|
cookie = SampleCookie2D(lightLoopContext, coord, lightData.cookieIndex); |
|
|
|
cookieColor = cookie.rgb; |
|
|
|
illuminance *= cookie.a; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
diffuseLighting *= (cookieColor * lightData.color) * (illuminance * lightData.diffuseScale); |
|
|
|
specularLighting *= (cookieColor * lightData.color) * (illuminance * lightData.specularScale); |
|
|
|
diffuseLighting *= (cookie.rgb * lightData.color) * (illuminance * lightData.diffuseScale); |
|
|
|
specularLighting *= (cookie.rgb * lightData.color) * (illuminance * lightData.specularScale); |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmittance) |
|
|
|
{ |
|
|
|
// Reverse the normal. |
|
|
|
illuminance = saturate(dot(-bsdfData.normalWS, L)); |
|
|
|
|
|
|
|
[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0) |
|
|
|
{ |
|
|
|
float shadow = GetDirectionalShadowAttenuation(lightLoopContext, positionWS, lightData.shadowIndex, L, posInput.unPositionSS, bsdfData.thickness); |
|
|
|
illuminance *= shadow; |
|
|
|
} |
|
|
|
|
|
|
|
illuminance *= cookie.a; |
|
|
|
|
|
|
|
// The difference between the Disney Diffuse and the Lambertian BRDF for transmittance is negligible. |
|
|
|
float3 backLight = (cookie.rgb * lightData.color) * (illuminance * lightData.diffuseScale * Lambert()); |
|
|
|
// TODO: multiplication by 'diffuseColor' and 'transmittance' is the same for each light. |
|
|
|
float3 transmittedLight = backLight * bsdfData.diffuseColor * bsdfData.transmittance; |
|
|
|
|
|
|
|
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass. |
|
|
|
diffuseLighting += transmittedLight; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
attenuation *= GetAngleAttenuation(L, -lightData.forward, lightData.angleScale, lightData.angleOffset); |
|
|
|
float illuminance = saturate(dot(bsdfData.normalWS, L)) * attenuation; |
|
|
|
|
|
|
|
diffuseLighting = float3(0.0, 0.0, 0.0); |
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
float3 cookieColor = float3(1.0, 1.0, 1.0); |
|
|
|
diffuseLighting = float3(0.0, 0.0, 0.0); |
|
|
|
specularLighting = float3(0.0, 0.0, 0.0); |
|
|
|
float4 cookie = float4(1.0, 1.0, 1.0, 1.0); |
|
|
|
|
|
|
|
// TODO: measure impact of having all these dynamic branch here and the gain (or not) of testing illuminace > 0 |
|
|
|
|
|
|
|
|
|
|
[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0) |
|
|
|
{ |
|
|
|
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal); |
|
|
|
float shadowAttenuation = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS); |
|
|
|
shadowAttenuation = lerp(1.0, shadowAttenuation, lightData.shadowDimmer); |
|
|
|
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS, 0); |
|
|
|
shadow = lerp(1.0, shadow, lightData.shadowDimmer); |
|
|
|
illuminance *= shadowAttenuation; |
|
|
|
illuminance *= shadow; |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (lightData.cookieIndex >= 0 && illuminance > 0.0) |
|
|
|
|
|
|
// Rotate 'L' into the light space. |
|
|
|
// We perform the negation because lights are oriented backwards (-Z). |
|
|
|
float3 coord = mul(-L, transpose(lightToWorld)); |
|
|
|
|
|
|
|
float4 cookie; |
|
|
|
|
|
|
|
[branch] if (lightData.lightType == GPULIGHTTYPE_SPOT) |
|
|
|
{ |
|
|
|
|
|
|
cookie = SampleCookieCube(lightLoopContext, coord, lightData.cookieIndex); |
|
|
|
} |
|
|
|
|
|
|
|
cookieColor = cookie.rgb; |
|
|
|
illuminance *= cookie.a; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
diffuseLighting *= (cookieColor * lightData.color) * (illuminance * lightData.diffuseScale); |
|
|
|
specularLighting *= (cookieColor * lightData.color) * (illuminance * lightData.specularScale); |
|
|
|
diffuseLighting *= (cookie.rgb * lightData.color) * (illuminance * lightData.diffuseScale); |
|
|
|
specularLighting *= (cookie.rgb * lightData.color) * (illuminance * lightData.specularScale); |
|
|
|
} |
|
|
|
|
|
|
|
[branch] if (bsdfData.enableTransmittance) |
|
|
|
{ |
|
|
|
// Reverse the normal. |
|
|
|
illuminance = saturate(dot(-bsdfData.normalWS, L)) * attenuation; |
|
|
|
|
|
|
|
[branch] if (lightData.shadowIndex >= 0 && illuminance > 0.0) |
|
|
|
{ |
|
|
|
float3 offset = float3(0.0, 0.0, 0.0); // GetShadowPosOffset(nDotL, normal); |
|
|
|
float shadow = GetPunctualShadowAttenuation(lightLoopContext, lightData.lightType, positionWS + offset, lightData.shadowIndex, L, posInput.unPositionSS, bsdfData.thickness); |
|
|
|
shadow = lerp(1.0, shadow, lightData.shadowDimmer); |
|
|
|
|
|
|
|
illuminance *= shadow; |
|
|
|
} |
|
|
|
|
|
|
|
illuminance *= cookie.a; |
|
|
|
|
|
|
|
// The difference between the Disney Diffuse and the Lambertian BRDF for transmittance is negligible. |
|
|
|
float3 backLight = (cookie.rgb * lightData.color) * (illuminance * lightData.diffuseScale * Lambert()); |
|
|
|
// TODO: multiplication by 'diffuseColor' and 'transmittance' is the same for each light. |
|
|
|
float3 transmittedLight = backLight * bsdfData.diffuseColor * bsdfData.transmittance; |
|
|
|
|
|
|
|
// We use diffuse lighting for accumulation since it is going to be blurred during the SSS pass. |
|
|
|
diffuseLighting += transmittedLight; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|