From 5c4736e3ebd204980b35f36767fd2ed4bc4cf7c4 Mon Sep 17 00:00:00 2001 From: Stephane Laroche Date: Mon, 30 Mar 2020 17:06:21 -0400 Subject: [PATCH 1/2] Added support for rasterized area light shadows in StackLit. Also refactor code to avoid early return (compiler bug) and fix lux meter debug mode when anisotropy for area lights is enabled. --- .../CHANGELOG.md | 1 + .../Runtime/Material/StackLit/StackLit.hlsl | 677 ++++++++++-------- 2 files changed, 364 insertions(+), 314 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 008c249fcc1..17cf4914656 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -89,6 +89,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added a new debug panel for volumes - Added XR setting to control camera jitter for temporal effects - Added an error message in the DrawRenderers custom pass when rendering opaque objects with an HDRP asset in DeferredOnly mode. +- Added support for rasterized area light shadows in StackLit ### Fixed - Fix when rescale probe all direction below zero (1219246) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/StackLit/StackLit.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/StackLit/StackLit.hlsl index ac030a088d9..f1a75c350a6 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/StackLit/StackLit.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/StackLit/StackLit.hlsl @@ -31,6 +31,12 @@ // #define STACK_LIT_DISPLAY_REFERENCE_IBL #endif +#ifndef SKIP_RASTERIZED_SHADOWS +#define RASTERIZED_AREA_LIGHT_SHADOWS 1 +#else +#define RASTERIZED_AREA_LIGHT_SHADOWS 0 +#endif + //----------------------------------------------------------------------------- // Texture and constant buffer declaration //----------------------------------------------------------------------------- @@ -3743,147 +3749,154 @@ DirectLighting EvaluateBSDF_Line( LightLoopContext lightLoopContext, // Terminate if the shaded point is too far away. if (intensity == 0.0) - return lighting; - - lightData.diffuseDimmer *= intensity; - lightData.specularDimmer *= intensity; - - // Translate the light s.t. the shaded point is at the origin of the coordinate system. - lightData.positionRWS -= positionWS; + { + lightData.diffuseDimmer *= intensity; + lightData.specularDimmer *= intensity; - // TODO: some of this could be precomputed. - float3 P1 = lightData.positionRWS - T * (0.5 * len); - float3 P2 = lightData.positionRWS + T * (0.5 * len); + // Translate the light s.t. the shaded point is at the origin of the coordinate system. + lightData.positionRWS -= positionWS; - // Setup the default local canonical frame with X-Y aligned to the reflection plane - // using orthoBasisViewNormal: without the anisotropic hack, this is only dependent on - // if we have dual normal maps or not: + // TODO: some of this could be precomputed. + float3 P1 = lightData.positionRWS - T * (0.5 * len); + float3 P2 = lightData.positionRWS + T * (0.5 * len); - // Rotate the endpoints into the local coordinate system. - float3 localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); - float3 localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); - // Compute the binormal in the local coordinate system. - float3 B = normalize(cross(localP1, localP2)); + // Setup the default local canonical frame with X-Y aligned to the reflection plane + // using orthoBasisViewNormal: without the anisotropic hack, this is only dependent on + // if we have dual normal maps or not: - if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above - { - // Since we proceed with calculating diffuse and transmission irradiance, we setup - // the points for the diffuse frame. - // There's no anisotropy on the diffuse component and this is oriented considering - // the proper base layer normal: - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormalDiffuse)); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormalDiffuse)); - B = normalize(cross(localP1, localP2)); - } + // Rotate the endpoints into the local coordinate system. + float3 localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + float3 localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + // Compute the binormal in the local coordinate system. + float3 B = normalize(cross(localP1, localP2)); - // Calculate the L irradiance (ltcValue) first for the diffuse part and transmission, - // then for the specular base layer and finishing with the coat. - float ltcValue; + if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above + { + // Since we proceed with calculating diffuse and transmission irradiance, we setup + // the points for the diffuse frame. + // There's no anisotropy on the diffuse component and this is oriented considering + // the proper base layer normal: + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormalDiffuse)); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormalDiffuse)); + B = normalize(cross(localP1, localP2)); + } - // Evaluate the diffuse part - ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformDiffuse); - ltcValue *= lightData.diffuseDimmer; - // We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF(). - lighting.diffuse = preLightData.diffuseFGD * preLightData.diffuseEnergy * ltcValue; + // Calculate the L irradiance (ltcValue) first for the diffuse part and transmission, + // then for the specular base layer and finishing with the coat. + float ltcValue; - UNITY_BRANCH if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_TRANSMISSION)) - { - // Flip the view vector and the normal. The bitangent stays the same. - float3x3 flipMatrix = float3x3(-1, 0, 0, - 0, 1, 0, - 0, 0, -1); - - // Use the Lambertian approximation for performance reasons. - // The matrix multiplication should not generate any extra ALU on GCN. - // TODO: double evaluation is very inefficient! This is a temporary solution. - ltcValue = LTCEvaluate(localP1, localP2, B, mul(flipMatrix, k_identity3x3)); + // Evaluate the diffuse part + ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformDiffuse); ltcValue *= lightData.diffuseDimmer; - - // VLAYERED_DIFFUSE_ENERGY_HACKED_TERM: - // In Lit with Lambert, there's no diffuseFGD, it is one. In our case, we also - // need a diffuse energy term when vlayered. - - // 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(). - lighting.diffuse += bsdfData.transmittance * ltcValue * preLightData.diffuseEnergy; - } + lighting.diffuse = preLightData.diffuseFGD * preLightData.diffuseEnergy * ltcValue; - // Evaluate the specular lobes for the stack - IF_DEBUG( if ( _DebugLobeMask.y != 0.0) ) - { - if (AREA_LIGHTS_ANISOTROPY_ENABLED) + UNITY_BRANCH if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_TRANSMISSION)) { - // In that case, instead of only considering possibly dual normal maps and thus two - // local canonical frames we have lobe specific frames because of the anisotropic hack: - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); - B = normalize(cross(localP1, localP2)); + // Flip the view vector and the normal. The bitangent stays the same. + float3x3 flipMatrix = float3x3(-1, 0, 0, + 0, 1, 0, + 0, 0, -1); + + // Use the Lambertian approximation for performance reasons. + // The matrix multiplication should not generate any extra ALU on GCN. + // TODO: double evaluation is very inefficient! This is a temporary solution. + ltcValue = LTCEvaluate(localP1, localP2, B, mul(flipMatrix, k_identity3x3)); + ltcValue *= lightData.diffuseDimmer; + + // VLAYERED_DIFFUSE_ENERGY_HACKED_TERM: + // In Lit with Lambert, there's no diffuseFGD, it is one. In our case, we also + // need a diffuse energy term when vlayered. + + // 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(). + lighting.diffuse += bsdfData.transmittance * ltcValue * preLightData.diffuseEnergy; } - ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[BASE_LOBEA_IDX]); - // See EvaluateBSDF_Env TODOENERGY: - lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEA_IDX] * preLightData.specularFGD[BASE_LOBEA_IDX] * ltcValue; - } - IF_DEBUG( if ( _DebugLobeMask.z != 0.0) ) - { - if (AREA_LIGHTS_ANISOTROPY_ENABLED) - { - // In that case, instead of only considering possibly dual normal maps and thus two - // local canonical frames we have lobe specific frames because of the anisotropic hack: - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); - B = normalize(cross(localP1, localP2)); - } - ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[BASE_LOBEB_IDX]); - lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEB_IDX] * preLightData.specularFGD[BASE_LOBEB_IDX] * ltcValue; - } - if (IsVLayeredEnabled(bsdfData)) - { - IF_DEBUG( if ( _DebugLobeMask.x != 0.0) ) + // Evaluate the specular lobes for the stack + IF_DEBUG( if ( _DebugLobeMask.y != 0.0) ) { - if (IsCoatNormalMapEnabled(bsdfData)) + if (AREA_LIGHTS_ANISOTROPY_ENABLED) { - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); - B = normalize(cross(localP1, localP2)); + // In that case, instead of only considering possibly dual normal maps and thus two + // local canonical frames we have lobe specific frames because of the anisotropic hack: + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); + B = normalize(cross(localP1, localP2)); } - if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above + ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[BASE_LOBEA_IDX]); + // See EvaluateBSDF_Env TODOENERGY: + lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEA_IDX] * preLightData.specularFGD[BASE_LOBEA_IDX] * ltcValue; + } + IF_DEBUG( if ( _DebugLobeMask.z != 0.0) ) + { + if (AREA_LIGHTS_ANISOTROPY_ENABLED) { - // No need to check if we have dual normal maps here: alread taken care via iblN[COAT_LOBE_IDX] - // in GetPreLightData and setup in preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX]. - - // we have lobe specific frames because of the anisotropic hack (there's no anisotropy for the - // coat, but the index of the ortho basis is lobe-based still because of the base layer lobes which - // can have anisotropy). - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); + // In that case, instead of only considering possibly dual normal maps and thus two + // local canonical frames we have lobe specific frames because of the anisotropic hack: + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); B = normalize(cross(localP1, localP2)); } - ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[COAT_LOBE_IDX]); - lighting.specular += preLightData.energyCompensationFactor[COAT_LOBE_IDX] * preLightData.specularFGD[COAT_LOBE_IDX] * ltcValue; + ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[BASE_LOBEB_IDX]); + lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEB_IDX] * preLightData.specularFGD[BASE_LOBEB_IDX] * ltcValue; } - } - lighting.specular *= lightData.specularDimmer; + if (IsVLayeredEnabled(bsdfData)) + { + IF_DEBUG( if ( _DebugLobeMask.x != 0.0) ) + { + if (IsCoatNormalMapEnabled(bsdfData)) + { + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); + B = normalize(cross(localP1, localP2)); + } + if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above + { + // No need to check if we have dual normal maps here: alread taken care via iblN[COAT_LOBE_IDX] + // in GetPreLightData and setup in preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX]. + + // we have lobe specific frames because of the anisotropic hack (there's no anisotropy for the + // coat, but the index of the ortho basis is lobe-based still because of the base layer lobes which + // can have anisotropy). + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); + B = normalize(cross(localP1, localP2)); + } + ltcValue = LTCEvaluate(localP1, localP2, B, preLightData.ltcTransformSpecular[COAT_LOBE_IDX]); + lighting.specular += preLightData.energyCompensationFactor[COAT_LOBE_IDX] * preLightData.specularFGD[COAT_LOBE_IDX] * ltcValue; + } + } + lighting.specular *= lightData.specularDimmer; - // Save ALU by applying 'lightData.color' only once. - lighting.diffuse *= lightData.color; - lighting.specular *= lightData.color; -#ifdef DEBUG_DISPLAY - if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER) - { - // Make sure we're using the base layer frame: - localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); - localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); - B = normalize(cross(localP1, localP2)); - - // Only lighting, not BSDF - // Apply area light on lambert then multiply by PI to cancel Lambert - lighting.diffuse = LTCEvaluate(localP1, localP2, B, k_identity3x3); - lighting.diffuse *= PI * lightData.diffuseDimmer; + // Save ALU by applying 'lightData.color' only once. + lighting.diffuse *= lightData.color; + lighting.specular *= lightData.color; + + #ifdef DEBUG_DISPLAY + if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER) + { + // Make sure we're using the base layer frame: + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + if (AREA_LIGHTS_ANISOTROPY_ENABLED) + { + // In that case orthoBasisViewNormal[] is per lobe due to anistropic hack, + // use orthoBasisViewNormalDiffuse: + localP1 = mul(P1, transpose(preLightData.orthoBasisViewNormalDiffuse)); + localP2 = mul(P2, transpose(preLightData.orthoBasisViewNormalDiffuse)); + } + B = normalize(cross(localP1, localP2)); + + // Only lighting, not BSDF + // Apply area light on lambert then multiply by PI to cancel Lambert + lighting.diffuse = LTCEvaluate(localP1, localP2, B, k_identity3x3); + lighting.diffuse *= PI * lightData.diffuseDimmer; + } + #endif } -#endif #endif // STACK_LIT_DISPLAY_REFERENCE_AREA @@ -3916,219 +3929,255 @@ DirectLighting EvaluateBSDF_Rect( LightLoopContext lightLoopContext, #else float3 unL = lightData.positionRWS - positionWS; - if (dot(lightData.forward, unL) >= 0.0001) - { - // The light is back-facing. - return lighting; - } - - // Rotate the light direction into the light space. - float3x3 lightToWorld = float3x3(lightData.right, lightData.up, -lightData.forward); - unL = mul(unL, transpose(lightToWorld)); - - // TODO: This could be precomputed. - float halfWidth = lightData.size.x * 0.5; - float halfHeight = lightData.size.y * 0.5; - - // Define the dimensions of the attenuation volume. - // TODO: This could be precomputed. - float range = lightData.range; - float3 invHalfDim = rcp(float3(range + halfWidth, - range + halfHeight, - range)); - - // Compute the light attenuation. -#ifdef ELLIPSOIDAL_ATTENUATION - // The attenuation volume is an axis-aligned ellipsoid s.t. - // r1 = (r + w / 2), r2 = (r + h / 2), r3 = r. - float intensity = EllipsoidalDistanceAttenuation(unL, invHalfDim, - lightData.rangeAttenuationScale, - lightData.rangeAttenuationBias); -#else - // The attenuation volume is an axis-aligned box s.t. - // hX = (r + w / 2), hY = (r + h / 2), hZ = r. - float intensity = BoxDistanceAttenuation(unL, invHalfDim, - lightData.rangeAttenuationScale, - lightData.rangeAttenuationBias); -#endif - - // Terminate if the shaded point is too far away. - if (intensity == 0.0) - return lighting; - - lightData.diffuseDimmer *= intensity; - lightData.specularDimmer *= intensity; - - // Translate the light s.t. the shaded point is at the origin of the coordinate system. - lightData.positionRWS -= positionWS; - - float4x3 lightVerts; - - // TODO: some of this could be precomputed. - lightVerts[0] = lightData.positionRWS + lightData.right * -halfWidth + lightData.up * -halfHeight; // LL - lightVerts[1] = lightData.positionRWS + lightData.right * -halfWidth + lightData.up * halfHeight; // UL - lightVerts[2] = lightData.positionRWS + lightData.right * halfWidth + lightData.up * halfHeight; // UR - lightVerts[3] = lightData.positionRWS + lightData.right * halfWidth + lightData.up * -halfHeight; // LR - - // Rotate the endpoints into the local coordinate system. - float4x3 localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); - - if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above - { - // Since we proceed with calculating diffuse and transmission irradiance, we setup - // the points for the diffuse frame. - // There's no anisotropy on the diffuse component and this is oriented considering - // the proper base layer normal: - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormalDiffuse)); - } - - // Calculate the L irradiance (ltcValue) first for the diffuse part and transmission, - // then for the specular base layer and finishing with the coat. - float3 ltcValue; - - // Evaluate the diffuse part - // Polygon irradiance in the transformed configuration. - float4x3 LD = mul(localLightVerts, preLightData.ltcTransformDiffuse); - ltcValue = PolygonIrradiance(LD); - ltcValue *= lightData.diffuseDimmer; - // Only apply cookie if there is one - if ( lightData.cookieMode != COOKIEMODE_NONE ) - { - // Compute the cookie data for the diffuse term - float3 formFactorD = PolygonFormFactor(LD); - ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LD, formFactorD); - } - // We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF(). - lighting.diffuse = preLightData.diffuseFGD * preLightData.diffuseEnergy * ltcValue; - - UNITY_BRANCH if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_TRANSMISSION)) - { - // Flip the view vector and the normal. The bitangent stays the same. - float3x3 flipMatrix = float3x3(-1, 0, 0, - 0, 1, 0, - 0, 0, -1); - - // Use the Lambertian approximation for performance reasons. - // The matrix multiplication should not generate any extra ALU on GCN. - float3x3 ltcTransform = mul(flipMatrix, k_identity3x3); - - // Polygon irradiance in the transformed configuration. - // TODO: double evaluation is very inefficient! This is a temporary solution. - float4x3 LTD = mul(localLightVerts, ltcTransform); - ltcValue = PolygonIrradiance(LTD); - ltcValue *= lightData.diffuseDimmer; - // Only apply cookie if there is one - if ( lightData.cookieMode != COOKIEMODE_NONE ) - { - // Compute the cookie data for the transmission diffuse term - float3 formFactorTD = PolygonFormFactor(LTD); - ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LTD, formFactorTD); - } - // VLAYERED_DIFFUSE_ENERGY_HACKED_TERM: - // In Lit with Lambert, there's no diffuseFGD, it is one. In our case, we also - // need a diffuse energy term when vlayered. - - // 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(). - lighting.diffuse += bsdfData.transmittance * ltcValue * preLightData.diffuseEnergy; - } - - // Evaluate the specular lobes for the stack - IF_DEBUG( if ( _DebugLobeMask.y != 0.0) ) + // if (dot(lightData.forward, unL) >= eps), all points on the light are back-facing: + // (Dont early return to guard against compiler bug for if / quick early return constructs) + if (dot(lightData.forward, unL) < FLT_EPS) { - if (AREA_LIGHTS_ANISOTROPY_ENABLED) - { - // In that case, instead of only considering possibly dual normal maps and thus two - // local canonical frames we have lobe specific frames because of the anisotropic hack: - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); - } - // Polygon irradiance in the transformed configuration. - float4x3 LAS = mul(localLightVerts, preLightData.ltcTransformSpecular[BASE_LOBEA_IDX]); - ltcValue = PolygonIrradiance(LAS); - // Only apply cookie if there is one - if ( lightData.cookieMode != COOKIEMODE_NONE ) - { - // Compute the cookie data for the specular term - float3 formFactorAS = PolygonFormFactor(LAS); - ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LAS, formFactorAS); - } - - // See EvaluateBSDF_Env TODOENERGY: - lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEA_IDX] * preLightData.specularFGD[BASE_LOBEA_IDX] * ltcValue; - } - IF_DEBUG( if ( _DebugLobeMask.z != 0.0) ) - { - if (AREA_LIGHTS_ANISOTROPY_ENABLED) - { - // In that case, instead of only considering possibly dual normal maps and thus two - // local canonical frames we have lobe specific frames because of the anisotropic hack: - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); - } - float4x3 LS = mul(localLightVerts, preLightData.ltcTransformSpecular[BASE_LOBEB_IDX]); - ltcValue = PolygonIrradiance(LS); - // Only apply cookie if there is one - if ( lightData.cookieMode != COOKIEMODE_NONE ) - { - // Compute the cookie data for the specular term - float3 formFactorS = PolygonFormFactor(LS); - ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LS, formFactorS); - } - - lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEB_IDX] * preLightData.specularFGD[BASE_LOBEB_IDX] * ltcValue; - } - if (IsVLayeredEnabled(bsdfData)) - { - if (IsCoatNormalMapEnabled(bsdfData)) - { - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); - } - if (AREA_LIGHTS_ANISOTROPY_ENABLED) + // Rotate the light direction into the light space. + float3x3 lightToWorld = float3x3(lightData.right, lightData.up, -lightData.forward); + unL = mul(unL, transpose(lightToWorld)); + + // TODO: This could be precomputed. + float halfWidth = lightData.size.x * 0.5; + float halfHeight = lightData.size.y * 0.5; + + // Define the dimensions of the attenuation volume. + // TODO: This could be precomputed. + float range = lightData.range; + float3 invHalfDim = rcp(float3(range + halfWidth, + range + halfHeight, + range)); + + // Compute the light attenuation. + #ifdef ELLIPSOIDAL_ATTENUATION + // The attenuation volume is an axis-aligned ellipsoid s.t. + // r1 = (r + w / 2), r2 = (r + h / 2), r3 = r. + float intensity = EllipsoidalDistanceAttenuation(unL, invHalfDim, + lightData.rangeAttenuationScale, + lightData.rangeAttenuationBias); + #else + // The attenuation volume is an axis-aligned box s.t. + // hX = (r + w / 2), hY = (r + h / 2), hZ = r. + float intensity = BoxDistanceAttenuation(unL, invHalfDim, + lightData.rangeAttenuationScale, + lightData.rangeAttenuationBias); + #endif + + // If the shaded point is too far away we avoid shading. + // (guard against compiler bug for if / quick early return constructs) + if (intensity != 0.0) { - // In that case, instead of only considering possibly dual normal maps and thus two - // local canonical frames we have lobe specific frames because of the anisotropic hack: - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); - } - IF_DEBUG( if ( _DebugLobeMask.x != 0.0) ) - { - float4x3 LSCC = mul(localLightVerts, preLightData.ltcTransformSpecular[COAT_LOBE_IDX]); - ltcValue = PolygonIrradiance(LSCC); + lightData.diffuseDimmer *= intensity; + lightData.specularDimmer *= intensity; + + // Translate the light s.t. the shaded point is at the origin of the coordinate system. + lightData.positionRWS -= positionWS; + + float4x3 lightVerts; + + // TODO: some of this could be precomputed. + lightVerts[0] = lightData.positionRWS + lightData.right * -halfWidth + lightData.up * -halfHeight; // LL + lightVerts[1] = lightData.positionRWS + lightData.right * -halfWidth + lightData.up * halfHeight; // UL + lightVerts[2] = lightData.positionRWS + lightData.right * halfWidth + lightData.up * halfHeight; // UR + lightVerts[3] = lightData.positionRWS + lightData.right * halfWidth + lightData.up * -halfHeight; // LR + + // Rotate the endpoints into the local coordinate system. + float4x3 localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + + if (AREA_LIGHTS_ANISOTROPY_ENABLED) // statically known, so no need for if else, just overwrite the above + { + // Since we proceed with calculating diffuse and transmission irradiance, we setup + // the points for the diffuse frame. + // There's no anisotropy on the diffuse component and this is oriented considering + // the proper base layer normal: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormalDiffuse)); + } + + // Calculate the L irradiance (ltcValue) first for the diffuse part and transmission, + // then for the specular base layer and finishing with the coat. + float3 ltcValue; + + // Evaluate the diffuse part + // Polygon irradiance in the transformed configuration. + float4x3 LD = mul(localLightVerts, preLightData.ltcTransformDiffuse); + ltcValue = PolygonIrradiance(LD); + ltcValue *= lightData.diffuseDimmer; // Only apply cookie if there is one if ( lightData.cookieMode != COOKIEMODE_NONE ) { - // Compute the cookie data for the specular term - float3 formFactorS = PolygonFormFactor(LSCC); - ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LSCC, formFactorS); + // Compute the cookie data for the diffuse term + float3 formFactorD = PolygonFormFactor(LD); + ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LD, formFactorD); } - lighting.specular += preLightData.energyCompensationFactor[COAT_LOBE_IDX] * preLightData.specularFGD[COAT_LOBE_IDX] * ltcValue; - } - } - lighting.specular *= lightData.specularDimmer; + // We don't multiply by 'bsdfData.diffuseColor' here. It's done only once in PostEvaluateBSDF(). + lighting.diffuse = preLightData.diffuseFGD * preLightData.diffuseEnergy * ltcValue; + + UNITY_BRANCH if (HasFlag(bsdfData.materialFeatures, MATERIALFEATUREFLAGS_STACK_LIT_TRANSMISSION)) + { + // Flip the view vector and the normal. The bitangent stays the same. + float3x3 flipMatrix = float3x3(-1, 0, 0, + 0, 1, 0, + 0, 0, -1); + + // Use the Lambertian approximation for performance reasons. + // The matrix multiplication should not generate any extra ALU on GCN. + float3x3 ltcTransform = mul(flipMatrix, k_identity3x3); + + // Polygon irradiance in the transformed configuration. + // TODO: double evaluation is very inefficient! This is a temporary solution. + float4x3 LTD = mul(localLightVerts, ltcTransform); + ltcValue = PolygonIrradiance(LTD); + ltcValue *= lightData.diffuseDimmer; + // Only apply cookie if there is one + if ( lightData.cookieMode != COOKIEMODE_NONE ) + { + // Compute the cookie data for the transmission diffuse term + float3 formFactorTD = PolygonFormFactor(LTD); + ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LTD, formFactorTD); + } + // VLAYERED_DIFFUSE_ENERGY_HACKED_TERM: + // In Lit with Lambert, there's no diffuseFGD, it is one. In our case, we also + // need a diffuse energy term when vlayered. + + // 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(). + lighting.diffuse += bsdfData.transmittance * ltcValue * preLightData.diffuseEnergy; + } + + // Evaluate the specular lobes for the stack + IF_DEBUG( if ( _DebugLobeMask.y != 0.0) ) + { + if (AREA_LIGHTS_ANISOTROPY_ENABLED) + { + // In that case, instead of only considering possibly dual normal maps and thus two + // local canonical frames we have lobe specific frames because of the anisotropic hack: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEA_IDX])); + } + // Polygon irradiance in the transformed configuration. + float4x3 LAS = mul(localLightVerts, preLightData.ltcTransformSpecular[BASE_LOBEA_IDX]); + ltcValue = PolygonIrradiance(LAS); + // Only apply cookie if there is one + if ( lightData.cookieMode != COOKIEMODE_NONE ) + { + // Compute the cookie data for the specular term + float3 formFactorAS = PolygonFormFactor(LAS); + ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LAS, formFactorAS); + } + + // See EvaluateBSDF_Env TODOENERGY: + lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEA_IDX] * preLightData.specularFGD[BASE_LOBEA_IDX] * ltcValue; + } + IF_DEBUG( if ( _DebugLobeMask.z != 0.0) ) + { + if (AREA_LIGHTS_ANISOTROPY_ENABLED) + { + // In that case, instead of only considering possibly dual normal maps and thus two + // local canonical frames we have lobe specific frames because of the anisotropic hack: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_BASE_LOBEB_IDX])); + } + float4x3 LS = mul(localLightVerts, preLightData.ltcTransformSpecular[BASE_LOBEB_IDX]); + ltcValue = PolygonIrradiance(LS); + // Only apply cookie if there is one + if ( lightData.cookieMode != COOKIEMODE_NONE ) + { + // Compute the cookie data for the specular term + float3 formFactorS = PolygonFormFactor(LS); + ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LS, formFactorS); + } + + lighting.specular += preLightData.energyCompensationFactor[BASE_LOBEB_IDX] * preLightData.specularFGD[BASE_LOBEB_IDX] * ltcValue; + } + + if (IsVLayeredEnabled(bsdfData)) + { + if (IsCoatNormalMapEnabled(bsdfData)) + { + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[COAT_NORMAL_IDX])); + } + if (AREA_LIGHTS_ANISOTROPY_ENABLED) + { + // In that case, instead of only considering possibly dual normal maps and thus two + // local canonical frames we have lobe specific frames because of the anisotropic hack: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[ORTHOBASIS_VN_COAT_LOBE_IDX])); + } + IF_DEBUG( if ( _DebugLobeMask.x != 0.0) ) + { + float4x3 LSCC = mul(localLightVerts, preLightData.ltcTransformSpecular[COAT_LOBE_IDX]); + ltcValue = PolygonIrradiance(LSCC); + // Only apply cookie if there is one + if ( lightData.cookieMode != COOKIEMODE_NONE ) + { + // Compute the cookie data for the specular term + float3 formFactorS = PolygonFormFactor(LSCC); + ltcValue *= SampleAreaLightCookie(lightData.cookieScaleOffset, LSCC, formFactorS); + } + lighting.specular += preLightData.energyCompensationFactor[COAT_LOBE_IDX] * preLightData.specularFGD[COAT_LOBE_IDX] * ltcValue; + } + } + lighting.specular *= lightData.specularDimmer; + + + // Save ALU by applying 'lightData.color' only once. + lighting.diffuse *= lightData.color; + lighting.specular *= lightData.color; + + #ifdef DEBUG_DISPLAY + if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER) + { + // Make sure we're using the base layer frame: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); + if (AREA_LIGHTS_ANISOTROPY_ENABLED) + { + // In that case orthoBasisViewNormal[] is per lobe due to anistropic hack, + // use orthoBasisViewNormalDiffuse: + localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormalDiffuse)); + } + + // Only lighting, not BSDF + // Apply area light on lambert then multiply by PI to cancel Lambert + lighting.diffuse = PolygonIrradiance(mul(localLightVerts, k_identity3x3)); + lighting.diffuse *= PI * lightData.diffuseDimmer; + } + #endif + } // if light not too far - // Save ALU by applying 'lightData.color' only once. - lighting.diffuse *= lightData.color; - lighting.specular *= lightData.color; + } // if light not back-facing + + float shadow = 1.0; + float shadowMask = 1.0; +#ifdef SHADOWS_SHADOWMASK + // shadowMaskSelector.x is -1 if there is no shadow mask + // Note that we override shadow value (in case we don't have any dynamic shadow) + shadow = shadowMask = (lightData.shadowMaskSelector.x >= 0.0) ? dot(BUILTIN_DATA_SHADOW_MASK, lightData.shadowMaskSelector) : 1.0; +#endif #if defined(SCREEN_SPACE_SHADOWS) && !defined(_SURFACE_TYPE_TRANSPARENT) - float shadow = 1.0; if ((lightData.screenSpaceShadowIndex & SCREEN_SPACE_SHADOW_INDEX_MASK) != INVALID_SCREEN_SPACE_SHADOW) + { shadow = GetScreenSpaceShadow(posInput, lightData.screenSpaceShadowIndex); - lighting.diffuse *= shadow; - lighting.specular *= shadow; -#endif // defined(SCREEN_SPACE_SHADOWS) && !defined(_SURFACE_TYPE_TRANSPARENT) - -#ifdef DEBUG_DISPLAY - if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER) + } + else +#endif // ENABLE_RAYTRACING + if (lightData.shadowIndex != -1) { - // Make sure we're using the base layer frame: - localLightVerts = mul(lightVerts, transpose(preLightData.orthoBasisViewNormal[BASE_NORMAL_IDX])); +#if RASTERIZED_AREA_LIGHT_SHADOWS + // lightData.positionRWS now contains the Light vector. + shadow = GetAreaLightAttenuation(lightLoopContext.shadowContext, posInput.positionSS, posInput.positionWS, bsdfData.normalWS, lightData.shadowIndex, normalize(lightData.positionRWS), length(lightData.positionRWS)); +#ifdef SHADOWS_SHADOWMASK + // See comment for punctual light shadow mask + shadow = lightData.nonLightMappedOnly ? min(shadowMask, shadow) : shadow; +#endif + shadow = lerp(shadowMask, shadow, lightData.shadowDimmer); - // Only lighting, not BSDF - // Apply area light on lambert then multiply by PI to cancel Lambert - lighting.diffuse = PolygonIrradiance(mul(localLightVerts, k_identity3x3)); - lighting.diffuse *= PI * lightData.diffuseDimmer; +#endif } + +#if RASTERIZED_AREA_LIGHT_SHADOWS || SUPPORTS_RAYTRACED_AREA_SHADOWS + float3 shadowColor = ComputeShadowColor(shadow, lightData.shadowTint, lightData.penumbraTint); + lighting.diffuse *= shadowColor; + lighting.specular *= shadowColor; #endif #endif // STACK_LIT_DISPLAY_REFERENCE_AREA From fa98b123c0ec1e1f0b7f1dce98c7951705e9814e Mon Sep 17 00:00:00 2001 From: Stephane Laroche Date: Wed, 8 Apr 2020 14:12:05 -0400 Subject: [PATCH 2/2] Doc: remove note about area light shadows for stacklit. [skipci] --- .../Documentation~/Light-Component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md b/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md index f5029e94aaa..fd537c504a4 100644 --- a/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md +++ b/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md @@ -184,7 +184,7 @@ These settings define the volumetric behavior of this Light. Alter these setting ### **Shadows** -Use this section to adjust the Shadows cast by this Light. Note that Area Lights can't currently cast shadows for GameObjects that use a **StackLit** Material. +Use this section to adjust the Shadows cast by this Light. Unity exposes extra properties in this section depending on the **Mode** you set in the [General](#GeneralProperties) section. Unity also exposes extra properties depending on the **Filtering Quality** set in your Unity Project’s [HDRP Asset](HDRP-Asset.html).