shadow fixes (#5890)
* shadow fixes Signed-off-by: Michael Riegger <mriegger@amazon.com> * Adding missing line Signed-off-by: Michael Riegger <mriegger@amazon.com> * Adding point sampler Signed-off-by: Michael Riegger <mriegger@amazon.com> * feedback from pr Signed-off-by: mrieggeramzn <mriegger@amazon.com> * better variable name Signed-off-by: mrieggeramzn <mriegger@amazon.com> * Fix compile error Signed-off-by: mrieggeramzn <mriegger@amazon.com>
This commit is contained in:
@@ -41,97 +41,3 @@ bool IsInsideOfImageSize(
|
||||
return IsInsideOfImageSize(coord, inputImageSize) &&
|
||||
IsInsideOfImageSize(coord, outputImageSize);
|
||||
}
|
||||
|
||||
//! This returns filtered value of "source" with weights in "filterTable" in 1 direction.
|
||||
//! @param coord the center coordinate (in Texture2DArray) of the filtered area.
|
||||
//! the xy coordinate is in pixel, and z is array slice index.
|
||||
//! @param source image resource which is used as the source of the filtering.
|
||||
//! Note that it contains entire of the shadowmap atlas, not a single shadowmap.
|
||||
//! @param direction either (1,0) or (0,1).
|
||||
//! If (1,0), the filtering direction is horizontal,
|
||||
//! and if (0,1), it is vertical.
|
||||
//! @param sourceMin the minimum (left/top most) index of the shadowmap.
|
||||
//! @param sourceMax the maximum (right/bottom most) index of the shadowmap.
|
||||
//! @param filterTable the weight table for this table.
|
||||
//! Since the weight table of a Gaussian filter is left-right symmetry,
|
||||
//! the right half is omitted in this filterTable.
|
||||
//! @param filterOffset the offset of the filtering parameter in filterTable.
|
||||
//! @param filterCount the element count of filtering parameter in filterTable.
|
||||
//! For example, the weight table has size 11 in the original meaning
|
||||
//! of Gaussian filter, filterCount == 6 by omitting the right half.
|
||||
float FilteredFloat(
|
||||
uint3 coord,
|
||||
Texture2DArray<float> source,
|
||||
int2 direction,
|
||||
int sourceMin,
|
||||
int sourceMax,
|
||||
Buffer<float> filterTable,
|
||||
uint filterOffset,
|
||||
uint filterCount)
|
||||
{
|
||||
if (filterCount == 0)
|
||||
{
|
||||
return 0.; // if no filtering info, early return.
|
||||
}
|
||||
|
||||
const int centerIndex = (int)dot(coord.xy, direction);
|
||||
float result = 0.;
|
||||
int index = 0;
|
||||
|
||||
// This function summarizes the values stored in "source"
|
||||
// from minIndex to maxIndex with weight in "filterTable".
|
||||
// In the case that some point in [minIndex, maxIndex] go outside of
|
||||
// the shadowmap (indicated by sourceMin and sourceMax),
|
||||
// the edge value of the shadowmap is used.
|
||||
|
||||
// 1. littler index side (left/up side)
|
||||
const int minIndex = centerIndex - ((int)filterCount - 1);
|
||||
|
||||
// 1-1. outside of shadowmap (littler)
|
||||
// Assuming outside values are equal to the edge value,
|
||||
// it first summarize the weights for outside of shadowmap
|
||||
// then multiply it by the edge value.
|
||||
float weight = 0.; // summation of weights of outside of shadowmap
|
||||
for (index = minIndex; index < sourceMin; ++index)
|
||||
{
|
||||
weight += filterTable[filterOffset + index - minIndex];
|
||||
}
|
||||
int2 edgeOffset = direction * (sourceMin - centerIndex);
|
||||
int3 edgeCoord = coord + int3(edgeOffset, 0);
|
||||
result += weight * source[edgeCoord];
|
||||
|
||||
// 1-2. inside of shadowmap (littler)
|
||||
for (index = max(sourceMin, minIndex); index < centerIndex; ++index)
|
||||
{
|
||||
const int2 offset = direction * (index - centerIndex);
|
||||
result += filterTable[filterOffset + index - minIndex] *
|
||||
source[coord + int3(offset, 0)];
|
||||
}
|
||||
|
||||
// 2. greater index side (right/down side)
|
||||
const int maxIndex = centerIndex + ((int)filterCount - 1);
|
||||
|
||||
// 2-1. outside of shadowmap (greater)
|
||||
// This is similar to 1-1 above.
|
||||
weight = 0.; // summation of weights of outside of shadowmap
|
||||
for (index = maxIndex; index > sourceMax; --index)
|
||||
{
|
||||
weight += filterTable[filterOffset + maxIndex - index];
|
||||
}
|
||||
edgeOffset = direction * (sourceMax - centerIndex);
|
||||
edgeCoord = coord + int3(edgeOffset, 0);
|
||||
result += weight * source[edgeCoord];
|
||||
|
||||
// 2-2. inside of shadowmap (greater)
|
||||
for (index = min(sourceMax, maxIndex); index > centerIndex; --index)
|
||||
{
|
||||
const int2 offset = direction * (index - centerIndex);
|
||||
result += filterTable[filterOffset + maxIndex - index] *
|
||||
source[coord + int3(offset, 0)];
|
||||
}
|
||||
|
||||
// 3. center
|
||||
result += filterTable[filterOffset + filterCount - 1] * source[coord];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+15
-3
@@ -50,6 +50,20 @@ int UnpackPointLightShadowIndex(const ViewSrg::PointLight light, const int face)
|
||||
return (light.m_shadowIndices[index] >> shiftAmount) & 0xFFFF;
|
||||
}
|
||||
|
||||
uint ComputeShadowIndex(const ViewSrg::PointLight light, const Surface surface)
|
||||
{
|
||||
// shadow map size and bias are the same across all shadowmaps used by a specific point light, so just grab the first one
|
||||
const uint lightIndex0 = UnpackPointLightShadowIndex(light, 0);
|
||||
const float shadowmapSize = ViewSrg::m_projectedFilterParams[lightIndex0].m_shadowmapSize;
|
||||
|
||||
// Note that the normal bias offset could potentially move the shadowed position from one map to another map inside the same point light shadow.
|
||||
const float normalBias = ViewSrg::m_projectedShadows[lightIndex0].m_normalShadowBias;
|
||||
const float3 biasedPosition = surface.position + ComputeNormalShadowOffset(normalBias, surface.vertexNormal, shadowmapSize);
|
||||
|
||||
const int shadowCubemapFace = GetPointLightShadowCubemapFace(biasedPosition, light.m_position);
|
||||
return UnpackPointLightShadowIndex(light, shadowCubemapFace);
|
||||
}
|
||||
|
||||
void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingData lightingData)
|
||||
{
|
||||
float3 posToLight = light.m_position - surface.position;
|
||||
@@ -74,10 +88,8 @@ void ApplyPointLight(ViewSrg::PointLight light, Surface surface, inout LightingD
|
||||
float backShadowRatio = 0.0;
|
||||
if (o_enableShadows)
|
||||
{
|
||||
const int shadowCubemapFace = GetPointLightShadowCubemapFace(surface.position, light.m_position);
|
||||
const int shadowIndex = UnpackPointLightShadowIndex(light, shadowCubemapFace);
|
||||
const float3 lightDir = normalize(light.m_position - surface.position);
|
||||
|
||||
const uint shadowIndex = ComputeShadowIndex(light, surface);
|
||||
litRatio *= ProjectedShadow::GetVisibility(
|
||||
shadowIndex,
|
||||
light.m_position,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
float SampleESM(const Texture2DArray<float> shadowMap, const SamplerState samp, const float3 uv, const float zReceiver, const float esmExponent)
|
||||
{
|
||||
const float mipmaplevel = 0;
|
||||
const float occluder = shadowMap.SampleLevel(samp,uv, mipmaplevel).r;
|
||||
const float lit = exp((occluder - zReceiver) * esmExponent);
|
||||
return lit;
|
||||
}
|
||||
|
||||
float PCFFallbackForESM(const Texture2DArray<float> shadowMap, const float3 uv, const float zReceiver, const float esmExponent)
|
||||
{
|
||||
const float result = SampleESM(shadowMap, PassSrg::LinearSampler, uv, zReceiver, esmExponent);
|
||||
return saturate(result);
|
||||
}
|
||||
+10
-14
@@ -15,6 +15,7 @@
|
||||
#include "BicubicPcfFilters.azsli"
|
||||
#include "Shadow.azsli"
|
||||
#include "NormalOffsetShadows.azsli"
|
||||
#include "ESM.azsli"
|
||||
|
||||
// ProjectedShadow calculates shadowed area projected from a light.
|
||||
class ProjectedShadow
|
||||
@@ -190,13 +191,11 @@ float ProjectedShadow::GetVisibilityEsm()
|
||||
const float depth = PerspectiveDepthToLinear(
|
||||
m_shadowPosition.z - m_bias,
|
||||
coefficients);
|
||||
const float occluder = shadowmap.SampleLevel(
|
||||
PassSrg::LinearSampler,
|
||||
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z),
|
||||
/*LOD=*/0).r;
|
||||
|
||||
const float3 uv = float3(atlasPosition.xy * invAtlasSize, atlasPosition.z);
|
||||
const float esmExponent = ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent;
|
||||
const float ratio = SampleESM(shadowmap, PassSrg::LinearSampler, uv, depth, esmExponent);
|
||||
|
||||
const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder);
|
||||
const float ratio = exp(exponent);
|
||||
// pow() mitigates light bleeding to shadows from near shadow casters.
|
||||
return saturate( pow(ratio, 8) );
|
||||
}
|
||||
@@ -229,21 +228,18 @@ float ProjectedShadow::GetVisibilityEsmPcf()
|
||||
return 1.;
|
||||
}
|
||||
const float3 atlasPosition = GetAtlasPosition(m_shadowPosition.xy);
|
||||
const float3 uv = float3(atlasPosition.xy * invAtlasSize, atlasPosition.z);
|
||||
const float depth = PerspectiveDepthToLinear(
|
||||
m_shadowPosition.z - m_bias,
|
||||
coefficients);
|
||||
const float occluder = shadowmap.SampleLevel(
|
||||
PassSrg::LinearSampler,
|
||||
float3(atlasPosition.xy * invAtlasSize, atlasPosition.z),
|
||||
/*LOD=*/0).r;
|
||||
|
||||
const float exponent = -ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent * (depth - occluder);
|
||||
float ratio = exp(exponent);
|
||||
|
||||
const float esmExponent = ViewSrg::m_projectedShadows[m_shadowIndex].m_esmExponent;
|
||||
float ratio = SampleESM(shadowmap, PassSrg::LinearSampler, uv, depth, esmExponent);
|
||||
|
||||
static const float pcfFallbackThreshold = 1.04;
|
||||
if (ratio > pcfFallbackThreshold)
|
||||
{
|
||||
ratio = GetVisibilityPcf();
|
||||
ratio = PCFFallbackForESM(shadowmap, uv, depth, esmExponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user