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:
mrieggeramzn
2021-12-02 09:26:34 -08:00
committed by GitHub
parent d89dcff7df
commit f4f7f07721
4 changed files with 49 additions and 111 deletions
@@ -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;
}
@@ -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);
}
@@ -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
{