Merge pull request #6428 from santipaprika/thin-transmission

Improve and refactor Atom's light transmission in thin object mode
monroegm-disable-blank-issue-2
Jeremy Ong 4 years ago committed by GitHub
commit adb598445f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1154,7 +1154,7 @@
{
"name": "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",
"defaultValue": 0.5,
"min": 0.0,
@ -1223,14 +1223,41 @@
"min": 0.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",
"displayName": " Scale",
"description": "Strength of transmission",
"type": "float",
"defaultValue": 3.0,
"defaultValue": 1.0,
"min": 0.0,
"softMax": 20.0
"softMax": 5.0
}
],
"detailLayerGroup": [
@ -1555,6 +1582,9 @@
"power": "subsurfaceScattering.transmissionPower",
"distortion": "subsurfaceScattering.transmissionDistortion",
"attenuation": "subsurfaceScattering.transmissionAttenuation",
"shrinkFactor": "subsurfaceScattering.shrinkFactor",
"transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
"distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
"tintColor": "subsurfaceScattering.transmissionTint",
"thickness": "subsurfaceScattering.thickness",
"enabled": "subsurfaceScattering.enableSubsurfaceScattering",

@ -93,7 +93,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
// Elements of m_transmissionParams:
// 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;
// (float3 TintColor, thickness)

@ -243,6 +243,7 @@ PbrLightingOutput ForwardPassPS_Common(VSOutput IN, bool isFrontFace, out float
surface.transmission.tint = transmissionTintThickness.rgb;
surface.transmission.thickness = transmissionTintThickness.w;
surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
// ------- 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.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 -------
// [GFX TODO][ATOM-14603]: Clean up the double uses of these clear coat flags

@ -92,23 +92,34 @@ function ProcessEditor(context)
-- Update visibility for transmission...
local transmissionEnabled = TransmissionMode_None ~= context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
local thickTransmissionEnabled = TransmissionMode_ThickObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
local thinTransmissionEnabled = TransmissionMode_ThinObject == context:GetMaterialPropertyValue_enum("subsurfaceScattering.transmissionMode")
local commonTrasmissionVisibility
if(transmissionEnabled) then
local commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
local thickTransmissionVisibility = MaterialPropertyVisibility_Hidden
local thinTransmissionVisibility = MaterialPropertyVisibility_Hidden
if (thickTransmissionEnabled or thinTransmissionEnabled) then
commonTrasmissionVisibility = MaterialPropertyVisibility_Enabled
else
commonTrasmissionVisibility = MaterialPropertyVisibility_Hidden
if(thickTransmissionEnabled) then
thickTransmissionVisibility = MaterialPropertyVisibility_Enabled
else -- thin transmission enabled
thinTransmissionVisibility = MaterialPropertyVisibility_Enabled
end
end
context:SetMaterialPropertyVisibility("subsurfaceScattering.thickness", commonTrasmissionVisibility)
context:SetMaterialPropertyVisibility("subsurfaceScattering.thicknessMap", 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)
UpdateTextureDependentPropertyVisibility(context, transmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
UpdateTextureDependentPropertyVisibility(context, thickTransmissionEnabled or thinTransmissionEnabled, "subsurfaceScattering.thicknessMap", "subsurfaceScattering.useThicknessMap", "subsurfaceScattering.thicknessMapUv")
end

@ -325,6 +325,7 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
surface.transmission.tint = transmissionTintThickness.rgb;
surface.transmission.thickness = transmissionTintThickness.w;
surface.transmission.transmissionParams = MaterialSrg::m_transmissionParams;
surface.transmission.scatterDistance = MaterialSrg::m_scatterDistance;
// ------- Lighting Data -------
@ -341,6 +342,17 @@ PbrLightingOutput SkinPS_Common(VSOutput IN)
lightingData.specularResponse = FresnelSchlickWithRoughness(lightingData.NdotV, surface.specularF0, surface.roughnessLinear);
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 -------
lightingData.diffuseAmbientOcclusion = GetOcclusionInput(MaterialSrg::m_diffuseOcclusionMap, MaterialSrg::m_sampler, IN.m_uv[MaterialSrg::m_diffuseOcclusionMapUvIndex], MaterialSrg::m_diffuseOcclusionFactor, o_diffuseOcclusion_useTexture);

@ -568,7 +568,7 @@
{
"name": "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",
"defaultValue": 0.5,
"min": 0.0,
@ -637,14 +637,41 @@
"min": 0.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",
"displayName": " Scale",
"description": "Strength of transmission",
"type": "float",
"defaultValue": 3.0,
"defaultValue": 1.0,
"min": 0.0,
"softMax": 20.0
"softMax": 5.0
}
],
"wrinkleLayers": [
@ -1011,6 +1038,9 @@
"power": "subsurfaceScattering.transmissionPower",
"distortion": "subsurfaceScattering.transmissionDistortion",
"attenuation": "subsurfaceScattering.transmissionAttenuation",
"shrinkFactor": "subsurfaceScattering.shrinkFactor",
"transmissionNdLBias": "subsurfaceScattering.transmissionNdLBias",
"distanceAttenuation": "subsurfaceScattering.distanceAttenuation",
"tintColor": "subsurfaceScattering.transmissionTint",
"thickness": "subsurfaceScattering.thickness",
"enabled": "subsurfaceScattering.enableSubsurfaceScattering",

@ -22,6 +22,10 @@
#include "MaterialInputs/UvSetCount.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
{
// Auto-generate material SRG fields for common inputs
@ -64,7 +68,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial
// Elements of m_transmissionParams:
// 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;
// (float3 TintColor, thickness)

@ -16,24 +16,28 @@
#include <Atom/Features/PBR/LightingOptions.azsli>
// 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 exponent = s * t;
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);
@ -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/
{
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 lamberAttenuation = exp(-thickness * transmissionParams.x) * saturate(1.0 - thickness);
result = transmittance * lamberAttenuation * lightIntensity;
@ -60,18 +64,39 @@ float3 GetBackLighting(Surface surface, LightingData lightingData, float3 lightI
break;
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
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;
}

@ -28,6 +28,15 @@ class LightingData
// Direction light shadow coordinates
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
float3 dirToCamera;

@ -135,8 +135,15 @@ void ApplyCapsuleLight(ViewSrg::CapsuleLight light, Surface surface, inout Light
float3 closestIntersectionPoint = startPoint + closestT * startToEnd;
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
float3 posToClosestPointAlongReflection = dot(posToLight, reflectionDir) * reflectionDir;

@ -18,7 +18,13 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
// Shadowed check
const uint shadowIndex = ViewSrg::m_shadowIndexDirectionalLight;
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)
{
litRatio = DirectionalLightShadow::GetVisibility(
@ -30,7 +36,19 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
#if ENABLE_TRANSMISSION
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
}
@ -52,23 +70,20 @@ void ApplyDirectionalLights(Surface surface, inout LightingData lightingData)
// [GFX TODO][ATOM-2012] care of multiple directional light
// Currently shadow check is done only for index == shadowIndex.
float currentLitRatio = 1.0f;
float currentBackShadowRatio = 1.0f;
if (o_enableShadows)
{
currentLitRatio = (index == shadowIndex) ? litRatio : 1.;
float currentTransmissionDistance = -1.0f;
currentBackShadowRatio = 1.0 - currentLitRatio;
#if ENABLE_TRANSMISSION
if (o_transmission_mode == TransmissionMode::ThickObject)
if (o_enableShadows && index == shadowIndex)
{
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.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

@ -77,8 +77,11 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
// shadow
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)
{
litRatio = ProjectedShadow::GetVisibility(
@ -88,14 +91,16 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
-dirToConeTip,
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 (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
}
@ -115,7 +120,7 @@ void ApplyDiskLight(ViewSrg::DiskLight light, Surface surface, inout LightingDat
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, posToLightDir) * litRatio;
// 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

@ -46,6 +46,16 @@ void AddSampleContribution(
float3 intensityRgb = float3(intensity, intensity, intensity);
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);
}

@ -67,6 +67,7 @@ uint ComputeShadowIndex(const ViewSrg::PointLight light, const Surface surface)
void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
{
float3 posToLight = light.m_position - surface.position;
float posToLightDist = length(posToLight);
float d2 = dot(posToLight, posToLight); // light distance squared
float falloff = d2 * light.m_invAttenuationRadiusSquared;
@ -84,8 +85,11 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
// shadow
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)
{
const float3 lightDir = normalize(light.m_position - surface.position);
@ -97,14 +101,15 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
lightDir,
surface.vertexNormal);
// Use backShadowRatio to carry thickness from shadow map for thick mode
backShadowRatio = 1.0 - litRatio;
#if ENABLE_TRANSMISSION
// o_transmission_mode == NONE is not taken into account because GetBackLighting already ignores this case
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
}
@ -113,7 +118,7 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
lightingData.diffuseLighting += GetDiffuseLighting(surface, lightingData, lightIntensity, normalize(posToLight)) * litRatio;
// 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

@ -148,13 +148,21 @@ void ApplyQuadLight(ViewSrg::QuadLight light, Surface surface, inout LightingDat
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 +=
(
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

@ -14,7 +14,8 @@ class TransmissionSurfaceData
{
float3 tint;
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();
};
@ -24,6 +25,7 @@ void TransmissionSurfaceData::InitializeToZero()
tint = float3(0.0f, 0.0f, 0.0f);
thickness = 0.0f;
transmissionParams = float4(0.0f, 0.0f, 0.0f, 0.0f);
scatterDistance = float3(0.0f, 0.0f, 0.0f);
}
#endif

@ -161,7 +161,10 @@ float DirectionalLightShadow::GetThickness(uint lightIndex, float3 shadowCoords[
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 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;
return viewSpaceThickness;
}

@ -275,7 +275,8 @@ float ProjectedShadow::GetThickness()
/*LOD=*/0
).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;
}

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

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

@ -25,6 +25,9 @@ namespace AZ
->Field("power", &SubsurfaceTransmissionParameterFunctorSourceData::m_power)
->Field("distortion", &SubsurfaceTransmissionParameterFunctorSourceData::m_distortion)
->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("thickness", &SubsurfaceTransmissionParameterFunctorSourceData::m_thickness)
->Field("enabled", &SubsurfaceTransmissionParameterFunctorSourceData::m_enabled)
@ -48,6 +51,9 @@ namespace AZ
functor->m_power = context.FindMaterialPropertyIndex(Name{ m_power });
functor->m_distortion = context.FindMaterialPropertyIndex(Name{ m_distortion });
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_thickness = context.FindMaterialPropertyIndex(Name{ m_thickness });
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() ||
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();
}
@ -66,6 +73,9 @@ namespace AZ
AddMaterialPropertyDependency(functor, functor->m_power);
AddMaterialPropertyDependency(functor, functor->m_distortion);
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_thickness);
AddMaterialPropertyDependency(functor, functor->m_enabled);

@ -36,6 +36,9 @@ namespace AZ
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_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_thickness; //!< material property for normalized object thickness
AZStd::string m_enabled; //!< material property for subsurface scattering feature switch (enabled or disabled)

Loading…
Cancel
Save