Browse Source

Merge pull request #6428 from santipaprika/thin-transmission

Improve and refactor Atom's light transmission in thin object mode
Jeremy Ong 3 years ago
parent
commit
adb598445f
22 changed files with 298 additions and 85 deletions
  1. 33 3
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype
  2. 1 1
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli
  3. 12 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl
  4. 21 10
      Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_SubsurfaceState.lua
  5. 12 0
      Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl
  6. 33 3
      Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype
  7. 5 1
      Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli
  8. 44 19
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli
  9. 9 0
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli
  10. 9 2
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/CapsuleLight.azsli
  11. 29 14
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli
  12. 15 10
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli
  13. 11 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli
  14. 15 10
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli
  15. 13 5
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli
  16. 3 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli
  17. 4 1
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli
  18. 3 2
      Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli
  19. 9 1
      Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.cpp
  20. 3 0
      Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h
  21. 11 1
      Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.cpp
  22. 3 0
      Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h

+ 33 - 3
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR.materialtype

@@ -1154,7 +1154,7 @@
                 {
                 {
                     "name": "thickness",
                     "name": "thickness",
                     "displayName": "    Thickness",
                     "displayName": "    Thickness",
-                    "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel",
+                    "description": "In thick transmission mode: Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel\n\nIn thin transmission mode: This value modulates the distance traversed by light inside an object.",
                     "type": "float",
                     "type": "float",
                     "defaultValue": 0.5,
                     "defaultValue": 0.5,
                     "min": 0.0,
                     "min": 0.0,
@@ -1223,14 +1223,41 @@
                     "min": 0.0,
                     "min": 0.0,
                     "softMax": 20.0
                     "softMax": 20.0
                 },
                 },
+                {
+                    "name": "shrinkFactor",
+                    "displayName": "    Shrink Factor",
+                    "description": "Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection",
+                    "type": "float",
+                    "defaultValue": 0.005,
+                    "min": 0.0,
+                    "softMax": 0.05
+                },
+                {
+                    "name": "transmissionNdLBias",
+                    "displayName": "    Angle Bias",
+                    "description": "cosine of angle to extend below (N . L = 0) in scattering through thin objects",
+                    "type": "float",
+                    "defaultValue": 0.1,
+                    "min": -1.0,
+                    "softMax": 1.0
+                },
+                {
+                    "name": "distanceAttenuation",
+                    "displayName": "    Distance Attenuation",
+                    "description": "Attenuation of the transmission effect, used to hide artifacts due to low-res shadow maps\nFor directional lights: attenuation proportional to the distance from the object to the camera.\nFor other light sources: attenuation proportional to the distance from the object to the light source.",
+                    "type": "float",
+                    "defaultValue": 0.5,
+                    "min": 0.0,
+                    "softMax": 4.0
+                },
                 {
                 {
                     "name": "transmissionScale",
                     "name": "transmissionScale",
                     "displayName": "    Scale",
                     "displayName": "    Scale",
                     "description": "Strength of transmission",
                     "description": "Strength of transmission",
                     "type": "float",
                     "type": "float",
-                    "defaultValue": 3.0,
+                    "defaultValue": 1.0,
                     "min": 0.0,
                     "min": 0.0,
-                    "softMax": 20.0
+                    "softMax": 5.0
                 }
                 }
             ],
             ],
             "detailLayerGroup": [
             "detailLayerGroup": [
@@ -1555,6 +1582,9 @@
                 "power": "subsurfaceScattering.transmissionPower",
                 "power": "subsurfaceScattering.transmissionPower",
                 "distortion": "subsurfaceScattering.transmissionDistortion",
                 "distortion": "subsurfaceScattering.transmissionDistortion",
                 "attenuation": "subsurfaceScattering.transmissionAttenuation",
                 "attenuation": "subsurfaceScattering.transmissionAttenuation",
+                "shrinkFactor": "subsurfaceScattering.shrinkFactor",
+                "transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
+                "distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
                 "tintColor": "subsurfaceScattering.transmissionTint",
                 "tintColor": "subsurfaceScattering.transmissionTint",
                 "thickness": "subsurfaceScattering.thickness",
                 "thickness": "subsurfaceScattering.thickness",
                 "enabled": "subsurfaceScattering.enableSubsurfaceScattering",
                 "enabled": "subsurfaceScattering.enableSubsurfaceScattering",

+ 1 - 1
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_Common.azsli

@@ -93,7 +93,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
     
     
     // Elements of m_transmissionParams:
     // Elements of m_transmissionParams:
     // Thick object mode: (attenuation coefficient, power, distortion, scale)
     // Thick object mode: (attenuation coefficient, power, distortion, scale)
-    // Thin  object mode:  (float3 scatter distance, scale)
+    // Thin  object mode:  (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
     float4 m_transmissionParams;
     float4 m_transmissionParams;
     
     
     // (float3 TintColor, thickness)
     // (float3 TintColor, thickness)

+ 12 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_ForwardPass.azsl

@@ -243,6 +243,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
     surface.transmission.tint = transmissionTintThickness.rgb;
     surface.transmission.tint = transmissionTintThickness.rgb;
     surface.transmission.thickness = transmissionTintThickness.w;
     surface.transmission.thickness = transmissionTintThickness.w;
     surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
     surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
+    surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
 
 
     // ------- Anisotropy -------
     // ------- Anisotropy -------
 
 
@@ -275,6 +276,17 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
     lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);
     lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);
     lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture);
     lightingData.specularOcclusion = GetOcclusionInput(MaterialSrg::m_specularOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_specularOcclusionMapUvIndex], MaterialSrg::m_specularOcclusionFactor, o_specularOcclusion_useTexture);
 
 
+    // ------- Thin Object Light Transmission -------
+
+    // Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
+    lightingData.shrinkFactor = surface.transmission.transmissionParams.x;
+
+    // Angle offset for subsurface scattering through thin objects
+    lightingData.transmissionNdLBias = surface.transmission.transmissionParams.y;
+
+    // Attenuation applied to hide artifacts due to low-res shadow maps 
+    lightingData.distanceAttenuation = surface.transmission.transmissionParams.z;
+
     // ------- Clearcoat -------
     // ------- Clearcoat -------
     
     
     // [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags
     // [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags

+ 21 - 10
Gems/Atom/Feature/Common/Assets/Materials/Types/EnhancedPBR_SubsurfaceState.lua

@@ -92,23 +92,34 @@ function ProcessEditor(context)
     
     
     -- Update visibility for transmission...
     -- Update visibility for transmission...
 
 
-    local transmissionEnabled = TransmissionMode_None ~= context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
-    
-    local commonTrasmissionVisibility
-    if(transmissionEnabled) then
+    local thickTransmissionEnabled = TransmissionMode_ThickObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
+    local thinTransmissionEnabled = TransmissionMode_ThinObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
+
+    local commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
+    local thickTransmissionVisibility = MaterialPropertyVisibility_Hidden
+    local thinTransmissionVisibility = MaterialPropertyVisibility_Hidden
+    if (thickTransmissionEnabled or thinTransmissionEnabled) then
         commonTrasmissionVisibility = MaterialPropertyVisibility_Enabled
         commonTrasmissionVisibility = MaterialPropertyVisibility_Enabled
-    else
-        commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
+
+            if(thickTransmissionEnabled) then
+                thickTransmissionVisibility = MaterialPropertyVisibility_Enabled
+            else -- thin transmission enabled
+                thinTransmissionVisibility = MaterialPropertyVisibility_Enabled
+            end
     end
     end
     
     
     context:SetMaterialPropertyVisibility("subsurfaceScattering.thickness", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.thickness", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.thicknessMap", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.thicknessMap", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionTint", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionTint", commonTrasmissionVisibility)
-    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionPower", commonTrasmissionVisibility)
-    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionDistortion", commonTrasmissionVisibility)
-    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionAttenuation", commonTrasmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionPower", thickTransmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionDistortion", thickTransmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionAttenuation", thickTransmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.shrinkFactor", thinTransmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionNdLBias", thinTransmissionVisibility)
+    context:SetMaterialPropertyVisibility("subsurfaceScattering.distanceAttenuation", thinTransmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionScale", commonTrasmissionVisibility)
     context:SetMaterialPropertyVisibility("subsurfaceScattering.transmissionScale", commonTrasmissionVisibility)
     
     
-    UpdateTextureDependentPropertyVisibility(context, transmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
+    
+    UpdateTextureDependentPropertyVisibility(context, thickTransmissionEnabled or thinTransmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
 
 
 end
 end

+ 12 - 0
Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.azsl

@@ -325,6 +325,7 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
     surface.transmission.tint = transmissionTintThickness.rgb;
     surface.transmission.tint = transmissionTintThickness.rgb;
     surface.transmission.thickness = transmissionTintThickness.w;
     surface.transmission.thickness = transmissionTintThickness.w;
     surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
     surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
+    surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
 
 
     // ------- Lighting Data -------
     // ------- Lighting Data -------
 
 
@@ -341,6 +342,17 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
     lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
     lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
     lightingData.diffuseResponse = 1.0 - lightingData.specularResponse;
     lightingData.diffuseResponse = 1.0 - lightingData.specularResponse;
 
 
+    // ------- Thin Object Light Transmission -------
+
+    // Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
+    lightingData.shrinkFactor = surface.transmission.transmissionParams.x;
+
+    // Angle offset for subsurface scattering through thin objects
+    lightingData.transmissionNdLBias = surface.transmission.transmissionParams.y;
+
+    // Attenuation applied to hide artifacts due to low-res shadow maps 
+    lightingData.distanceAttenuation = surface.transmission.transmissionParams.z;
+
     // ------- Occlusion -------
     // ------- Occlusion -------
     
     
     lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);
     lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);

+ 33 - 3
Gems/Atom/Feature/Common/Assets/Materials/Types/Skin.materialtype

@@ -568,7 +568,7 @@
                 {
                 {
                     "name": "thickness",
                     "name": "thickness",
                     "displayName": "    Thickness",
                     "displayName": "    Thickness",
-                    "description": "Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel",
+                    "description": "In thick transmission mode: Normalized global thickness, the maxima between this value (multiplied by thickness map if enabled) and thickness from shadow map (if applicable) will be used as final thickness of pixel\n\nIn thin transmission mode: This value modulates the distance traversed by light inside an object.",
                     "type": "float",
                     "type": "float",
                     "defaultValue": 0.5,
                     "defaultValue": 0.5,
                     "min": 0.0,
                     "min": 0.0,
@@ -637,14 +637,41 @@
                     "min": 0.0,
                     "min": 0.0,
                     "softMax": 20.0
                     "softMax": 20.0
                 },
                 },
+                {
+                    "name": "shrinkFactor",
+                    "displayName": "    Shrink Factor",
+                    "description": "Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection",
+                    "type": "float",
+                    "defaultValue": 0.005,
+                    "min": 0.0,
+                    "softMax": 0.05
+                },
+                {
+                    "name": "transmissionNdLBias",
+                    "displayName": "    Angle Bias",
+                    "description": "cosine of angle to extend below (N . L = 0) in scattering through thin objects",
+                    "type": "float",
+                    "defaultValue": 0.1,
+                    "min": -1.0,
+                    "softMax": 1.0
+                },
+                {
+                    "name": "distanceAttenuation",
+                    "displayName": "    Distance Attenuation",
+                    "description": "Attenuation of the transmission effect, used to hide artifacts due to low-res shadow maps\nFor directional lights: attenuation proportional to the distance from the object to the camera.\nFor other light sources: attenuation proportional to the distance from the object to the light source.",
+                    "type": "float",
+                    "defaultValue": 0.5,
+                    "min": 0.0,
+                    "softMax": 4.0
+                },
                 {
                 {
                     "name": "transmissionScale",
                     "name": "transmissionScale",
                     "displayName": "    Scale",
                     "displayName": "    Scale",
                     "description": "Strength of transmission",
                     "description": "Strength of transmission",
                     "type": "float",
                     "type": "float",
-                    "defaultValue": 3.0,
+                    "defaultValue": 1.0,
                     "min": 0.0,
                     "min": 0.0,
-                    "softMax": 20.0
+                    "softMax": 5.0
                 }
                 }
             ],
             ],
             "wrinkleLayers": [
             "wrinkleLayers": [
@@ -1011,6 +1038,9 @@
                 "power": "subsurfaceScattering.transmissionPower",
                 "power": "subsurfaceScattering.transmissionPower",
                 "distortion": "subsurfaceScattering.transmissionDistortion",
                 "distortion": "subsurfaceScattering.transmissionDistortion",
                 "attenuation": "subsurfaceScattering.transmissionAttenuation",
                 "attenuation": "subsurfaceScattering.transmissionAttenuation",
+                "shrinkFactor": "subsurfaceScattering.shrinkFactor",
+                "transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
+                "distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
                 "tintColor": "subsurfaceScattering.transmissionTint",
                 "tintColor": "subsurfaceScattering.transmissionTint",
                 "thickness": "subsurfaceScattering.thickness",
                 "thickness": "subsurfaceScattering.thickness",
                 "enabled": "subsurfaceScattering.enableSubsurfaceScattering",
                 "enabled": "subsurfaceScattering.enableSubsurfaceScattering",

+ 5 - 1
Gems/Atom/Feature/Common/Assets/Materials/Types/Skin_Common.azsli

@@ -22,6 +22,10 @@
 #include "MaterialInputs/UvSetCount.azsli"
 #include "MaterialInputs/UvSetCount.azsli"
 #include "MaterialInputs/DetailMapsInput.azsli"
 #include "MaterialInputs/DetailMapsInput.azsli"
 
 
+// Use human skin profile for thin object transmission (if enabled)
+// Remove this line to use a custom profile based on the scatter color
+#define USE_HUMAN_SKIN_PROFILE
+
 ShaderResourceGroup MaterialSrg : SRG_PerMaterial
 ShaderResourceGroup MaterialSrg : SRG_PerMaterial
 {
 {
     // Auto-generate material SRG fields for common inputs
     // Auto-generate material SRG fields for common inputs
@@ -64,7 +68,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
     
     
     // Elements of m_transmissionParams:
     // Elements of m_transmissionParams:
     // Thick object mode: (attenuation coefficient, power, distortion, scale)
     // Thick object mode: (attenuation coefficient, power, distortion, scale)
-    // Thin  object mode:  (float3 scatter distance, scale)
+    // Thin  object mode:  (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
     float4 m_transmissionParams;
     float4 m_transmissionParams;
     
     
     // (float3 TintColor, thickness)
     // (float3 TintColor, thickness)

+ 44 - 19
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/BackLighting.azsli

@@ -16,24 +16,28 @@
 #include <Atom/Features/PBR/LightingOptions.azsli>
 #include <Atom/Features/PBR/LightingOptions.azsli>
 
 
 // Analytical integation (approximation) of diffusion profile over radius, could be replaced by other pre integrated kernels
 // Analytical integation (approximation) of diffusion profile over radius, could be replaced by other pre integrated kernels
-// such as sum of Gaussian
+// such as sum of Gaussian (see T(s))
 float3 TransmissionKernel(float t, float3 s)
 float3 TransmissionKernel(float t, float3 s)
 {
 {
     float3 exponent = s * t;
     float3 exponent = s * t;
     return 0.25 * (1.0 / exp(exponent) + 3.0 / exp(exponent / 3.0));
     return 0.25 * (1.0 / exp(exponent) + 3.0 / exp(exponent / 3.0));
 }
 }
 
 
-float ThinObjectFalloff(const float3 surfaceNormal, const float3 dirToLight)
+// [specific profile for human skin]
+// Analytical integation (approximation) of diffusion profile over radius, could be precomputed in a LUT
+// From d'Eon and Luebke (https://developer.nvidia.com/gpugems/gpugems3/part-iii-rendering/chapter-14-advanced-techniques-realistic-real-time-skin, section 14.4.7)
+float3 T(float s) 
 {
 {
-    const float ndl = saturate(dot(-surfaceNormal, dirToLight));
-    
-    // ndl works decently well but it can produce a harsh discontinuity in the area just before 
-    // the shadow starts appearing on objects like cylinder and tubes.
-    // Smoothing out ndl does a decent enough job of removing this artifact.
-    return smoothstep(0, 1, ndl * ndl);
+    // dipoles and multipoles are approximated with sums of a small number of Gaussians with variable weights and variances
+    return float3(0.233, 0.455, 0.649) * exp(-s*s/0.0064) +
+    float3(0.1, 0.336, 0.344) * exp(-s*s/0.0484) +
+    float3(0.118, 0.198, 0.0) * exp(-s*s/0.187) +
+    float3(0.113, 0.007, 0.007) * exp(-s*s/0.567) +
+    float3(0.358, 0.004, 0.0) * exp(-s*s/1.99) +
+    float3(0.078, 0.0, 0.0) * exp(-s*s/7.41);
 }
 }
 
 
-float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight, float shadowRatio)
+float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightIntensity, float3 dirToLight, float transmissionDistance, float attenuationDistance)
 {
 {
     float3 result = float3(0.0, 0.0, 0.0);
     float3 result = float3(0.0, 0.0, 0.0);
 
 
@@ -52,7 +56,7 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
             // https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
             // https://colinbarrebrisebois.com/2011/03/07/gdc-2011-approximating-translucency-for-a-fast-cheap-and-convincing-subsurface-scattering-look/
 
 
             {
             {
-                thickness = max(shadowRatio, surface.transmission.thickness);
+                thickness = max(transmissionDistance, surface.transmission.thickness);
                 float transmittance = pow( saturate( dot( lightingData.dirToCamera, -normalize( dirToLight + surface.normal * transmissionParams.z ) ) ), transmissionParams.y ) * transmissionParams.w;
                 float transmittance = pow( saturate( dot( lightingData.dirToCamera, -normalize( dirToLight + surface.normal * transmissionParams.z ) ) ), transmissionParams.y ) * transmissionParams.w;
                 float lamberAttenuation = exp(-thickness * transmissionParams.x) * saturate(1.0 - thickness);
                 float lamberAttenuation = exp(-thickness * transmissionParams.x) * saturate(1.0 - thickness);
                 result = transmittance * lamberAttenuation * lightIntensity;
                 result = transmittance * lamberAttenuation * lightIntensity;
@@ -60,18 +64,39 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
             break;
             break;
 
 
         case TransmissionMode::ThinObject:
         case TransmissionMode::ThinObject:
-            // Thin object mode, using thin-film assumption proposed by Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
+            // Thin object mode, based on Jimenez J. et al, 2010, "Real-Time Realistic Skin Translucency"
             // http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
             // http://www.iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf
-
-            float litRatio = 1.0 - shadowRatio;
-            if (litRatio)
+            
             {
             {
-                const float thickness = surface.transmission.thickness * transmissionParams.w;
-                const float3 invScattering = rcp(transmissionParams.xyz);
-                const float falloff = ThinObjectFalloff(surface.normal, dirToLight);
-                result = TransmissionKernel(thickness, invScattering) * falloff * lightIntensity * litRatio;
-            }
+                // transmissionDistance < 0.0f means shadows are not enabled --> avoid unnecessary computation
+                if (transmissionDistance < 0.0f)
+                {
+                    break;
+                }
+
+                // Irradiance arround surface point. 
+                // Albedo at front (surface point) is used to approximate irradiance at the back of the object (observation 4 in [Jimenez J. et al, 2010])
+                // Increase angle of influence to smooth transition regions
+                float3 E = surface.albedo * saturate(lightingData.transmissionNdLBias + dot(-surface.normal, dirToLight));
+
+                // Transmission distance modulated by hardcoded constant C and the thickness exposed parameter (in this case modulating the distance traversed by the light inside the object)
+                const float C = 300.0f;
+                float s = transmissionDistance * C * surface.transmission.thickness;
+                
+                // Use scattering color to weight thin object transmission color
+                const float3 invScattering = rcp(max(surface.transmission.scatterDistance, float(0.00001)));
+                
+#ifndef USE_HUMAN_SKIN_PROFILE
+                // Generic profile based on scatter color
+                result = TransmissionKernel(s, invScattering) * lightIntensity * E * transmissionParams.w;
+#else // USE_HUMAN_SKIN_PROFILE
+                // Profile specific to human skin
+                result = T(s) * lightIntensity * E * transmissionParams.w;
+#endif
 
 
+                // Distance attenuation applied to hide artifacts due to low-res projected areas onto shadowmaps (might need some work in the future)
+                result /= max(1.0, attenuationDistance * attenuationDistance * lightingData.distanceAttenuation);
+            }
             break;
             break;
     }
     }
 
 

+ 9 - 0
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli

@@ -28,6 +28,15 @@ class LightingData
     // Direction light shadow coordinates
     // Direction light shadow coordinates
     float3 shadowCoords[ViewSrg::MaxCascadeCount];
     float3 shadowCoords[ViewSrg::MaxCascadeCount];
 
 
+    // (N . L) to accept below (N . L = 0) in scattering through thin objects
+    float transmissionNdLBias; 
+    
+    // Shrink (absolute) offset towards the normal opposite direction to ensure correct shadow map projection
+    float shrinkFactor;
+
+    // Attenuation applied to hide artifacts due to low-res shadow maps 
+    float distanceAttenuation;
+
     // Normalized direction from surface to camera
     // Normalized direction from surface to camera
     float3 dirToCamera;
     float3 dirToCamera;
     
     

+ 9 - 2
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/CapsuleLight.azsli

@@ -135,8 +135,15 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, Surface surface, inout Light
         float3 closestIntersectionPoint = startPoint + closestT * startToEnd;
         float3 closestIntersectionPoint = startPoint + closestT * startToEnd;
         float3 posToLight = closestIntersectionPoint - surface.position;
         float3 posToLight = closestIntersectionPoint - surface.position;
 
 
-        // Tranmission contribution
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), 0.0);
+        // Transmission contribution
+        // We cannot compute the actual transmission distance so we want to:
+        // - If transmission mode is thick object -> use transmission thickness parameter instead
+        // - If transmission mode is thin object -> ignore back lighting
+        // To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance 
+        const float transmissionDistance = -1.0f;
+        // If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
+        const float attenuationDistance = 0.0f;
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), transmissionDistance, attenuationDistance);
 
 
         // Calculate the offset from the nearest point on the reflection vector to the nearest point on the capsule light
         // Calculate the offset from the nearest point on the reflection vector to the nearest point on the capsule light
         float3 posToClosestPointAlongReflection = dot(posToLight, reflectionDir) * reflectionDir;
         float3 posToClosestPointAlongReflection = dot(posToLight, reflectionDir) * reflectionDir;

+ 29 - 14
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DirectionalLight.azsli

@@ -18,7 +18,13 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
     // Shadowed check
     // Shadowed check
     const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
     const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
     float litRatio = 1.0f;
     float litRatio = 1.0f;
-    float backShadowRatio = 0.0f;
+    float camToSurfDist = distance(ViewSrg::m_worldPosition, surface.position);
+
+    // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+    // - If transmission mode is thick object -> use transmission thickness parameter instead
+    // - If transmission mode is thin object -> ignore back lighting
+    float transmissionDistance = -1.0f;
+
     if (o_enableShadows && shadowIndex <  SceneSrg::m_directionalLightCount)
     if (o_enableShadows && shadowIndex <  SceneSrg::m_directionalLightCount)
     {
     {
         litRatio = DirectionalLightShadow::GetVisibility(
         litRatio = DirectionalLightShadow::GetVisibility(
@@ -30,7 +36,19 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
 #if ENABLE_TRANSMISSION
 #if ENABLE_TRANSMISSION
         if (o_transmission_mode == TransmissionMode::ThickObject)
         if (o_transmission_mode == TransmissionMode::ThickObject)
         {
         {
-            backShadowRatio = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
+            transmissionDistance = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
+        } 
+        else if (o_transmission_mode == TransmissionMode::ThinObject) 
+        {
+            // Fetch and use shrinked positions for thin object transmission to ensure they fall onto the object when querying
+            DirectionalLightShadow::GetShadowCoords(
+                shadowIndex,
+                surface.position - lightingData.shrinkFactor * surface.vertexNormal,
+                surface.normal,
+                lightingData.shadowCoords);
+
+            // the depth from the shadow map
+            transmissionDistance = DirectionalLightShadow::GetThickness(shadowIndex, lightingData.shadowCoords);
         }
         }
 #endif
 #endif
     }
     }
@@ -52,23 +70,20 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
         // [GFX TODO][ATOM-2012] care of multiple directional light
         // [GFX TODO][ATOM-2012] care of multiple directional light
         // Currently shadow check is done only for index == shadowIndex.
         // Currently shadow check is done only for index == shadowIndex.
         float currentLitRatio = 1.0f;
         float currentLitRatio = 1.0f;
-        float currentBackShadowRatio = 1.0f;
-        if (o_enableShadows)
+        float currentTransmissionDistance = -1.0f;
+
+        if (o_enableShadows && index == shadowIndex)
         {
         {
-            currentLitRatio = (index == shadowIndex) ? litRatio : 1.;
-            
-            currentBackShadowRatio = 1.0 - currentLitRatio;
-#if ENABLE_TRANSMISSION
-            if (o_transmission_mode == TransmissionMode::ThickObject)
-            {
-                currentBackShadowRatio = (index == shadowIndex) ?  backShadowRatio : 0.;
-            }
-#endif
+            // Add contribution only if current directional light is the active one for shadows
+            currentLitRatio = litRatio;
+            currentTransmissionDistance = transmissionDistance;
         }
         }
+
+        // Transmission contribution
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight, currentTransmissionDistance, camToSurfDist);
         
         
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
         lightingData.specularLighting += GetSpecularLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
         lightingData.specularLighting += GetSpecularLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight) * currentLitRatio;
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, light.m_rgbIntensityLux, dirToLight, currentBackShadowRatio);
     }
     }
     
     
     // Add debug coloring for directional light shadow
     // Add debug coloring for directional light shadow

+ 15 - 10
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/DiskLight.azsli

@@ -76,9 +76,12 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
 
 
         // shadow
         // shadow
         float litRatio = 1.0;
         float litRatio = 1.0;
-        
-        // How much is back face shadowed, it's set to the reverse of litRatio to share the same default value with thickness, which should be 0 if no shadow map available
-        float backShadowRatio = 0.0;
+
+        // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+        // - If transmission mode is thick object -> use transmission thickness parameter instead
+        // - If transmission mode is thin object -> ignore back lighting
+        float transmissionDistance = -1.0f;
+
         if (o_enableShadows)
         if (o_enableShadows)
         {
         {
             litRatio = ProjectedShadow::GetVisibility(
             litRatio = ProjectedShadow::GetVisibility(
@@ -88,14 +91,16 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
                 -dirToConeTip,
                 -dirToConeTip,
                 surface.vertexNormal);
                 surface.vertexNormal);
              
              
-            // Use backShadowRatio to carry thickness from shadow map for thick mode
-            backShadowRatio = 1.0 - litRatio;
+
+            // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
 #if ENABLE_TRANSMISSION            
 #if ENABLE_TRANSMISSION            
             if (o_transmission_mode == TransmissionMode::ThickObject)
             if (o_transmission_mode == TransmissionMode::ThickObject)
             {
             {
-                backShadowRatio = ProjectedShadow::GetThickness(
-                    light.m_shadowIndex,
-                    surface.position);
+                transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position);
+            }
+            else if (o_transmission_mode == TransmissionMode::ThinObject)
+            {
+                transmissionDistance = ProjectedShadow::GetThickness(light.m_shadowIndex, surface.position - lightingData.shrinkFactor * surface.vertexNormal);
             }
             }
 #endif
 #endif
         }
         }
@@ -115,8 +120,8 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
 
 
         // Transmission contribution
         // Transmission contribution
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, backShadowRatio);
-        
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, posToLightDir, transmissionDistance, distanceToLight2);
+
         // Adjust the light direction for specular based on disk size
         // Adjust the light direction for specular based on disk size
 
 
         // Calculate the reflection off the normal from the view direction
         // Calculate the reflection off the normal from the view direction

+ 11 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/LightTypesCommon.azsli

@@ -46,6 +46,16 @@ void AddSampleContribution(
     float3 intensityRgb = float3(intensity, intensity, intensity);
     float3 intensityRgb = float3(intensity, intensity, intensity);
 
 
     diffuseAcc += GetDiffuseLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
     diffuseAcc += GetDiffuseLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
-    translucentAcc += GetBackLighting(surface, lightingData, intensityRgb, posToLightSampleDir, 0.0);
+
+    // Transmission contribution
+    // We cannot compute the actual transmission distance so we want to:
+    // - If transmission mode is thick object -> use transmission thickness parameter instead
+    // - If transmission mode is thin object -> ignore back lighting
+    // To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance 
+    const float transmissionDistance = -1.0f;
+    // If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
+    const float attenuationDistance = 0.0f;
+    translucentAcc += GetBackLighting(surface, lightingData, intensityRgb, posToLightSampleDir, transmissionDistance, attenuationDistance);
+
     specularAcc += GetSpecularLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
     specularAcc += GetSpecularLighting(surface, lightingData, intensityRgb, posToLightSampleDir);
 }
 }

+ 15 - 10
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/PointLight.azsli

@@ -67,6 +67,7 @@ uint ComputeShadowIndex(const ViewSrg::PointLight light, const Surface surface)
 void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
 void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
 {
 {
     float3 posToLight = light.m_position - surface.position;
     float3 posToLight = light.m_position - surface.position;
+    float posToLightDist = length(posToLight);
     float d2 = dot(posToLight, posToLight); // light distance squared
     float d2 = dot(posToLight, posToLight); // light distance squared
     float falloff = d2 * light.m_invAttenuationRadiusSquared;
     float falloff = d2 * light.m_invAttenuationRadiusSquared;
     
     
@@ -84,8 +85,11 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
         // shadow
         // shadow
         float litRatio = 1.0;
         float litRatio = 1.0;
 
 
-        // How much is back face shadowed, it's set to the reverse of litRatio to share the same default value with thickness, which should be 0 if no shadow map available
-        float backShadowRatio = 0.0;
+        // Distance travelled by the light inside the object. If not redefined to a non-negative value, it will take the following behavior:
+        // - If transmission mode is thick object -> use transmission thickness parameter instead
+        // - If transmission mode is thin object -> ignore back lighting
+        float transmissionDistance = -1.0f;
+
         if (o_enableShadows)
         if (o_enableShadows)
         {
         {
             const float3 lightDir = normalize(light.m_position - surface.position);
             const float3 lightDir = normalize(light.m_position - surface.position);
@@ -96,16 +100,17 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
                     surface.position,
                     surface.position,
                     lightDir,
                     lightDir,
                     surface.vertexNormal);
                     surface.vertexNormal);
-                        
-            // Use backShadowRatio to carry thickness from shadow map for thick mode
-            backShadowRatio = 1.0 - litRatio;
+
 #if ENABLE_TRANSMISSION
 #if ENABLE_TRANSMISSION
+            // o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
             if (o_transmission_mode == TransmissionMode::ThickObject)
             if (o_transmission_mode == TransmissionMode::ThickObject)
             {
             {
-                backShadowRatio = ProjectedShadow::GetThickness(
-                    shadowIndex,
-                    surface.position);
-            }                   
+                transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position);
+            }
+            else if (o_transmission_mode == TransmissionMode::ThinObject)
+            {
+                transmissionDistance = ProjectedShadow::GetThickness(shadowIndex, surface.position - lightingData.shrinkFactor * surface.vertexNormal);
+            }
 #endif
 #endif
         }
         }
 
 
@@ -113,7 +118,7 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
         lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
 
 
         // Transmission contribution
         // Transmission contribution
-        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), backShadowRatio);
+        lightingData.translucentBackLighting += GetBackLighting(surface, lightingData, lightIntensity, normalize(posToLight), transmissionDistance, posToLightDist);
 
 
         // Adjust the light direcion for specular based on bulb size
         // Adjust the light direcion for specular based on bulb size
 
 

+ 13 - 5
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Lights/QuadLight.azsli

@@ -148,13 +148,21 @@ void ApplyQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingDat
                 GetDiffuseLighting(surface, lightingData, intensity, dirToLightCenter)
                 GetDiffuseLighting(surface, lightingData, intensity, dirToLightCenter)
             );
             );
             
             
+            // Transmission contribution
+            // We cannot compute the actual transmission distance so we want to:
+            // - If transmission mode is thick object -> use transmission thickness parameter instead
+            // - If transmission mode is thin object -> ignore back lighting
+            // To detect and apply this behavior in the GetBackLighting function, we need to use a negative transmissionDistance 
+            const float transmissionDistance = -1.0f;
+            // If the transmissionDistance is ignored then the attenuation distance (only used on thin objects) does not have any influence
+            const float attenuationDistance = 0.0f;
             lightingData.translucentBackLighting +=
             lightingData.translucentBackLighting +=
             (
             (
-                GetBackLighting(surface, lightingData, intensity, p0, 0.0) +
-                GetBackLighting(surface, lightingData, intensity, p1, 0.0) +
-                GetBackLighting(surface, lightingData, intensity, p2, 0.0) +
-                GetBackLighting(surface, lightingData, intensity, p3, 0.0) +
-                GetBackLighting(surface, lightingData, intensity, dirToLightCenter, 0.0)
+                GetBackLighting(surface, lightingData, intensity, p0, transmissionDistance, attenuationDistance) +
+                GetBackLighting(surface, lightingData, intensity, p1, transmissionDistance, attenuationDistance) +
+                GetBackLighting(surface, lightingData, intensity, p2, transmissionDistance, attenuationDistance) +
+                GetBackLighting(surface, lightingData, intensity, p3, transmissionDistance, attenuationDistance) +
+                GetBackLighting(surface, lightingData, intensity, dirToLightCenter, transmissionDistance, attenuationDistance)
             );
             );
             
             
             // Calculate specular by choosing a single representative point on the light's surface based on the reflection ray
             // Calculate specular by choosing a single representative point on the light's surface based on the reflection ray

+ 3 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/Surfaces/TransmissionSurfaceData.azsli

@@ -14,7 +14,8 @@ class TransmissionSurfaceData
 {
 {
     float3 tint;
     float3 tint;
     float thickness;                 //!< pre baked local thickness, used for transmission
     float thickness;                 //!< pre baked local thickness, used for transmission
-    float4 transmissionParams;       //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode:  (float3 scatter distance, scale)
+    float4 transmissionParams;       //!< parameters: thick mode->(attenuation coefficient, power, distortion, scale), thin mode:  (shrinkFactor, transmissionNdLBias, distanceAttenuation, scale)
+    float3 scatterDistance;          //!< scatter distance (same as in MaterialSrg) > 
 
 
     void InitializeToZero();
     void InitializeToZero();
 };
 };
@@ -24,6 +25,7 @@ void TransmissionSurfaceData::InitializeToZero()
     tint = float3(0.0f, 0.0f, 0.0f);
     tint = float3(0.0f, 0.0f, 0.0f);
     thickness = 0.0f;
     thickness = 0.0f;
     transmissionParams = float4(0.0f, 0.0f, 0.0f, 0.0f);
     transmissionParams = float4(0.0f, 0.0f, 0.0f, 0.0f);
+    scatterDistance = float3(0.0f, 0.0f, 0.0f);
 }
 }
 
 
 #endif
 #endif

+ 4 - 1
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/DirectionalLightShadow.azsli

@@ -161,7 +161,10 @@ float DirectionalLightShadow::GetThickness(uint lightIndex, float3 shadowCoords[
              shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin && shadowCoord.z < (1. - DepthMargin))
              shadowCoord.y >= 0. && shadowCoord.y * size < size - PixelMargin && shadowCoord.z < (1. - DepthMargin))
         {
         {
             const float depthBufferValue = shadowmap.Sample(PassSrg::LinearSampler, float3(shadowCoord.xy, indexOfCascade)).r;
             const float depthBufferValue = shadowmap.Sample(PassSrg::LinearSampler, float3(shadowCoord.xy, indexOfCascade)).r;
-            const float deltaDepth = abs(shadowCoord.z - depthBufferValue);
+            
+            // Normalized thickness (avoid negative values given by precision errors or shrinking offsets)
+            const float deltaDepth = max(shadowCoord.z - depthBufferValue,0.0);
+
             const float viewSpaceThickness = ViewSrg::m_directionalLightShadows[lightIndex].m_far_minus_near * deltaDepth;            
             const float viewSpaceThickness = ViewSrg::m_directionalLightShadows[lightIndex].m_far_minus_near * deltaDepth;            
             return viewSpaceThickness;
             return viewSpaceThickness;
         }
         }

+ 3 - 2
Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/Shadow/ProjectedShadow.azsli

@@ -274,8 +274,9 @@ float ProjectedShadow::GetThickness()
             float3(atlasPosition.xy * invAtlasSize, atlasPosition.z),
             float3(atlasPosition.xy * invAtlasSize, atlasPosition.z),
             /*LOD=*/0
             /*LOD=*/0
             ).r;
             ).r;
-            
-        const float viewSpaceThickness = abs(UnprojectDepth(m_shadowIndex, m_shadowPosition.z) - UnprojectDepth(m_shadowIndex, depthValue)); 
+        
+        // Denormalized thickness (avoid negative values given by precision errors or shrinking offsets)
+        const float viewSpaceThickness = max(UnprojectDepth(m_shadowIndex, depthValue) - UnprojectDepth(m_shadowIndex, m_shadowPosition.z), 0.0); 
         return viewSpaceThickness;    
         return viewSpaceThickness;    
     }
     }
 
 

+ 9 - 1
Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.cpp

@@ -26,6 +26,9 @@ namespace AZ
                     ->Field("m_power", &SubsurfaceTransmissionParameterFunctor::m_power)
                     ->Field("m_power", &SubsurfaceTransmissionParameterFunctor::m_power)
                     ->Field("m_distortion", &SubsurfaceTransmissionParameterFunctor::m_distortion)
                     ->Field("m_distortion", &SubsurfaceTransmissionParameterFunctor::m_distortion)
                     ->Field("m_attenuation", &SubsurfaceTransmissionParameterFunctor::m_attenuation)
                     ->Field("m_attenuation", &SubsurfaceTransmissionParameterFunctor::m_attenuation)
+                    ->Field("m_shrinkFactor", &SubsurfaceTransmissionParameterFunctor::m_shrinkFactor)
+                    ->Field("m_transmissionNdLBias", &SubsurfaceTransmissionParameterFunctor::m_transmissionNdLBias)
+                    ->Field("m_distanceAttenuation", &SubsurfaceTransmissionParameterFunctor::m_distanceAttenuation)
                     ->Field("m_tintColor", &SubsurfaceTransmissionParameterFunctor::m_tintColor)
                     ->Field("m_tintColor", &SubsurfaceTransmissionParameterFunctor::m_tintColor)
                     ->Field("m_thickness", &SubsurfaceTransmissionParameterFunctor::m_thickness)
                     ->Field("m_thickness", &SubsurfaceTransmissionParameterFunctor::m_thickness)
                     ->Field("m_enabled", &SubsurfaceTransmissionParameterFunctor::m_enabled)
                     ->Field("m_enabled", &SubsurfaceTransmissionParameterFunctor::m_enabled)
@@ -50,6 +53,9 @@ namespace AZ
             auto power = context.GetMaterialPropertyValue<float>(m_power);
             auto power = context.GetMaterialPropertyValue<float>(m_power);
             auto distortion = context.GetMaterialPropertyValue<float>(m_distortion);
             auto distortion = context.GetMaterialPropertyValue<float>(m_distortion);
             auto attenuation = context.GetMaterialPropertyValue<float>(m_attenuation);
             auto attenuation = context.GetMaterialPropertyValue<float>(m_attenuation);
+            auto shrinkFactor = context.GetMaterialPropertyValue<float>(m_shrinkFactor);
+            auto transmissionNdLBias = context.GetMaterialPropertyValue<float>(m_transmissionNdLBias);
+            auto distanceAttenuation = context.GetMaterialPropertyValue<float>(m_distanceAttenuation);
             auto tintColor = context.GetMaterialPropertyValue<Color>(m_tintColor);
             auto tintColor = context.GetMaterialPropertyValue<Color>(m_tintColor);
             auto thickness = context.GetMaterialPropertyValue<float>(m_thickness);
             auto thickness = context.GetMaterialPropertyValue<float>(m_thickness);
             auto scatterDistanceColor = context.GetMaterialPropertyValue<Color>(m_scatterDistanceColor);
             auto scatterDistanceColor = context.GetMaterialPropertyValue<Color>(m_scatterDistanceColor);
@@ -67,7 +73,9 @@ namespace AZ
             }
             }
             else
             else
             {
             {
-                transmissionParams.Set(scatterDistance);
+                transmissionParams.SetX(shrinkFactor);
+                transmissionParams.SetY(transmissionNdLBias);
+                transmissionParams.SetZ(distanceAttenuation);
                 transmissionParams.SetW(scale);
                 transmissionParams.SetW(scale);
             }
             }
 
 

+ 3 - 0
Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctor.h

@@ -37,6 +37,9 @@ namespace AZ
             RPI::MaterialPropertyIndex m_power;
             RPI::MaterialPropertyIndex m_power;
             RPI::MaterialPropertyIndex m_distortion;
             RPI::MaterialPropertyIndex m_distortion;
             RPI::MaterialPropertyIndex m_attenuation;
             RPI::MaterialPropertyIndex m_attenuation;
+            RPI::MaterialPropertyIndex m_shrinkFactor;
+            RPI::MaterialPropertyIndex m_transmissionNdLBias;
+            RPI::MaterialPropertyIndex m_distanceAttenuation;
             RPI::MaterialPropertyIndex m_tintColor;
             RPI::MaterialPropertyIndex m_tintColor;
             RPI::MaterialPropertyIndex m_thickness;
             RPI::MaterialPropertyIndex m_thickness;
             RPI::MaterialPropertyIndex m_enabled;
             RPI::MaterialPropertyIndex m_enabled;

+ 11 - 1
Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.cpp

@@ -25,6 +25,9 @@ namespace AZ
                     ->Field("power", &SubsurfaceTransmissionParameterFunctorSourceData::m_power)
                     ->Field("power", &SubsurfaceTransmissionParameterFunctorSourceData::m_power)
                     ->Field("distortion", &SubsurfaceTransmissionParameterFunctorSourceData::m_distortion)
                     ->Field("distortion", &SubsurfaceTransmissionParameterFunctorSourceData::m_distortion)
                     ->Field("attenuation", &SubsurfaceTransmissionParameterFunctorSourceData::m_attenuation)
                     ->Field("attenuation", &SubsurfaceTransmissionParameterFunctorSourceData::m_attenuation)
+                    ->Field("shrinkFactor", &SubsurfaceTransmissionParameterFunctorSourceData::m_shrinkFactor)
+                    ->Field("transmissionNdLBias", &SubsurfaceTransmissionParameterFunctorSourceData::m_transmissionNdLBias)
+                    ->Field("distanceAttenuation", &SubsurfaceTransmissionParameterFunctorSourceData::m_distanceAttenuation)
                     ->Field("tintColor", &SubsurfaceTransmissionParameterFunctorSourceData::m_tintColor)
                     ->Field("tintColor", &SubsurfaceTransmissionParameterFunctorSourceData::m_tintColor)
                     ->Field("thickness", &SubsurfaceTransmissionParameterFunctorSourceData::m_thickness)
                     ->Field("thickness", &SubsurfaceTransmissionParameterFunctorSourceData::m_thickness)
                     ->Field("enabled", &SubsurfaceTransmissionParameterFunctorSourceData::m_enabled)
                     ->Field("enabled", &SubsurfaceTransmissionParameterFunctorSourceData::m_enabled)
@@ -48,6 +51,9 @@ namespace AZ
             functor->m_power                    = context.FindMaterialPropertyIndex(Name{ m_power });
             functor->m_power                    = context.FindMaterialPropertyIndex(Name{ m_power });
             functor->m_distortion               = context.FindMaterialPropertyIndex(Name{ m_distortion });
             functor->m_distortion               = context.FindMaterialPropertyIndex(Name{ m_distortion });
             functor->m_attenuation              = context.FindMaterialPropertyIndex(Name{ m_attenuation });
             functor->m_attenuation              = context.FindMaterialPropertyIndex(Name{ m_attenuation });
+            functor->m_shrinkFactor             = context.FindMaterialPropertyIndex(Name{ m_shrinkFactor });
+            functor->m_transmissionNdLBias      = context.FindMaterialPropertyIndex(Name{ m_transmissionNdLBias });
+            functor->m_distanceAttenuation      = context.FindMaterialPropertyIndex(Name{ m_distanceAttenuation });
             functor->m_tintColor                = context.FindMaterialPropertyIndex(Name{ m_tintColor });
             functor->m_tintColor                = context.FindMaterialPropertyIndex(Name{ m_tintColor });
             functor->m_thickness                = context.FindMaterialPropertyIndex(Name{ m_thickness });
             functor->m_thickness                = context.FindMaterialPropertyIndex(Name{ m_thickness });
             functor->m_enabled                  = context.FindMaterialPropertyIndex(Name{ m_enabled });
             functor->m_enabled                  = context.FindMaterialPropertyIndex(Name{ m_enabled });
@@ -56,7 +62,8 @@ namespace AZ
 
 
             if (functor->m_mode.IsNull() || functor->m_scale.IsNull() || functor->m_power.IsNull() || functor->m_distortion.IsNull() ||
             if (functor->m_mode.IsNull() || functor->m_scale.IsNull() || functor->m_power.IsNull() || functor->m_distortion.IsNull() ||
                 functor->m_tintColor.IsNull() || functor->m_thickness.IsNull() || functor->m_enabled.IsNull() || functor->m_attenuation.IsNull() || functor->m_scatterDistanceColor.IsNull() ||
                 functor->m_tintColor.IsNull() || functor->m_thickness.IsNull() || functor->m_enabled.IsNull() || functor->m_attenuation.IsNull() || functor->m_scatterDistanceColor.IsNull() ||
-                functor->m_scatterDistanceIntensity.IsNull())
+                functor->m_scatterDistanceIntensity.IsNull() || functor->m_shrinkFactor.IsNull() || functor->m_transmissionNdLBias.IsNull() ||
+                functor->m_distanceAttenuation.IsNull())
             {
             {
                 return Failure();
                 return Failure();
             }
             }
@@ -66,6 +73,9 @@ namespace AZ
             AddMaterialPropertyDependency(functor, functor->m_power);
             AddMaterialPropertyDependency(functor, functor->m_power);
             AddMaterialPropertyDependency(functor, functor->m_distortion);
             AddMaterialPropertyDependency(functor, functor->m_distortion);
             AddMaterialPropertyDependency(functor, functor->m_attenuation);
             AddMaterialPropertyDependency(functor, functor->m_attenuation);
+            AddMaterialPropertyDependency(functor, functor->m_shrinkFactor);
+            AddMaterialPropertyDependency(functor, functor->m_transmissionNdLBias);
+            AddMaterialPropertyDependency(functor, functor->m_distanceAttenuation);
             AddMaterialPropertyDependency(functor, functor->m_tintColor);
             AddMaterialPropertyDependency(functor, functor->m_tintColor);
             AddMaterialPropertyDependency(functor, functor->m_thickness);
             AddMaterialPropertyDependency(functor, functor->m_thickness);
             AddMaterialPropertyDependency(functor, functor->m_enabled);
             AddMaterialPropertyDependency(functor, functor->m_enabled);

+ 3 - 0
Gems/Atom/Feature/Common/Code/Source/Material/SubsurfaceTransmissionParameterFunctorSourceData.h

@@ -36,6 +36,9 @@ namespace AZ
             AZStd::string m_power;                      //!< material property for thick transmission power
             AZStd::string m_power;                      //!< material property for thick transmission power
             AZStd::string m_distortion;                 //!< material property for thick transmission distortion towards surface normal
             AZStd::string m_distortion;                 //!< material property for thick transmission distortion towards surface normal
             AZStd::string m_attenuation;                //!< material property for thick transmission volume absorption
             AZStd::string m_attenuation;                //!< material property for thick transmission volume absorption
+            AZStd::string m_shrinkFactor;               //!< material property for thin transmission position shrink factor towards surface normal
+            AZStd::string m_transmissionNdLBias;        //!< material property for thin transmission bias of the NdL value
+            AZStd::string m_distanceAttenuation;        //!< material property for thin transmission attenuation with distance 
             AZStd::string m_tintColor;                  //!< material property for transmission tint
             AZStd::string m_tintColor;                  //!< material property for transmission tint
             AZStd::string m_thickness;                  //!< material property for normalized object thickness
             AZStd::string m_thickness;                  //!< material property for normalized object thickness
             AZStd::string m_enabled;                    //!< material property for subsurface scattering feature switch (enabled or disabled)
             AZStd::string m_enabled;                    //!< material property for subsurface scattering feature switch (enabled or disabled)