Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/CapsuleShadowRendering.cpp
tiago costa b62b074182 Modified FCapsuleShadowingCS and FCapsuleShadowingUpsamplePS to use FPermutationDomain instead of templates.
#rb krzysztof.narkowicz

[CL 16694202 by tiago costa in ue5-main branch]
2021-06-16 15:15:29 -04:00

1491 lines
65 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
CapsuleShadowRendering.cpp: Functionality for rendering shadows from capsules
=============================================================================*/
#include "CapsuleShadowRendering.h"
#include "Stats/Stats.h"
#include "HAL/IConsoleManager.h"
#include "RHI.h"
#include "RenderResource.h"
#include "ShaderParameters.h"
#include "RendererInterface.h"
#include "Shader.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "RHIStaticStates.h"
#include "PostProcess/SceneRenderTargets.h"
#include "GlobalShader.h"
#include "SceneRenderTargetParameters.h"
#include "ShadowRendering.h"
#include "DeferredShadingRenderer.h"
#include "MaterialShaderType.h"
#include "DistanceFieldAmbientOcclusion.h"
#include "DistanceFieldLightingPost.h"
#include "DistanceFieldLightingShared.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "RendererPrivateUtils.h"
DECLARE_GPU_STAT_NAMED(CapsuleShadows, TEXT("Capsule Shadows"));
int32 GCapsuleShadows = 1;
FAutoConsoleVariableRef CVarCapsuleShadows(
TEXT("r.CapsuleShadows"),
GCapsuleShadows,
TEXT("Whether to allow capsule shadowing on skinned components with bCastCapsuleDirectShadow or bCastCapsuleIndirectShadow enabled."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GCapsuleDirectShadows = 1;
FAutoConsoleVariableRef CVarCapsuleDirectShadows(
TEXT("r.CapsuleDirectShadows"),
GCapsuleDirectShadows,
TEXT("Whether to allow capsule direct shadowing on skinned components with bCastCapsuleDirectShadow enabled."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GCapsuleIndirectShadows = 1;
FAutoConsoleVariableRef CVarCapsuleIndirectShadows(
TEXT("r.CapsuleIndirectShadows"),
GCapsuleIndirectShadows,
TEXT("Whether to allow capsule indirect shadowing on skinned components with bCastCapsuleIndirectShadow enabled."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GCapsuleShadowsFullResolution = 0;
FAutoConsoleVariableRef CVarCapsuleShadowsFullResolution(
TEXT("r.CapsuleShadowsFullResolution"),
GCapsuleShadowsFullResolution,
TEXT("Whether to compute capsule shadows at full resolution."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleMaxDirectOcclusionDistance = 400;
FAutoConsoleVariableRef CVarCapsuleMaxDirectOcclusionDistance(
TEXT("r.CapsuleMaxDirectOcclusionDistance"),
GCapsuleMaxDirectOcclusionDistance,
TEXT("Maximum cast distance for direct shadows from capsules. This has a big impact on performance."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleMaxIndirectOcclusionDistance = 200;
FAutoConsoleVariableRef CVarCapsuleMaxIndirectOcclusionDistance(
TEXT("r.CapsuleMaxIndirectOcclusionDistance"),
GCapsuleMaxIndirectOcclusionDistance,
TEXT("Maximum cast distance for indirect shadows from capsules. This has a big impact on performance."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleShadowFadeAngleFromVertical = PI / 3;
FAutoConsoleVariableRef CVarCapsuleShadowFadeAngleFromVertical(
TEXT("r.CapsuleShadowFadeAngleFromVertical"),
GCapsuleShadowFadeAngleFromVertical,
TEXT("Angle from vertical up to start fading out the indirect shadow, to avoid self shadowing artifacts."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleIndirectConeAngle = PI / 8;
FAutoConsoleVariableRef CVarCapsuleIndirectConeAngle(
TEXT("r.CapsuleIndirectConeAngle"),
GCapsuleIndirectConeAngle,
TEXT("Light source angle used when the indirect shadow direction is derived from precomputed indirect lighting (no stationary skylight present)"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleSkyAngleScale = .6f;
FAutoConsoleVariableRef CVarCapsuleSkyAngleScale(
TEXT("r.CapsuleSkyAngleScale"),
GCapsuleSkyAngleScale,
TEXT("Scales the light source angle derived from the precomputed unoccluded sky vector (stationary skylight present)"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GCapsuleMinSkyAngle = 15;
FAutoConsoleVariableRef CVarCapsuleMinSkyAngle(
TEXT("r.CapsuleMinSkyAngle"),
GCapsuleMinSkyAngle,
TEXT("Minimum light source angle derived from the precomputed unoccluded sky vector (stationary skylight present)"),
ECVF_Scalability | ECVF_RenderThreadSafe
);
// Nvidia has lower vertex throughput when only processing a few verts per instance
// Disabled as it hasn't been tested
static const int32 NumTileQuadsInBuffer = 1;
TGlobalResource<FTileTexCoordVertexBuffer> GTileTexCoordVertexBuffer(NumTileQuadsInBuffer);
TGlobalResource<FTileIndexBuffer> GTileIndexBuffer(NumTileQuadsInBuffer);
const int32 GComputeLightDirectionFromVolumetricLightmapGroupSize = 64;
class FComputeLightDirectionFromVolumetricLightmapCS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FComputeLightDirectionFromVolumetricLightmapCS);
SHADER_USE_PARAMETER_STRUCT(FComputeLightDirectionFromVolumetricLightmapCS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(uint32, NumLightDirectionData)
SHADER_PARAMETER(uint32, SkyLightMode)
SHADER_PARAMETER(float, CapsuleIndirectConeAngle)
SHADER_PARAMETER(float, CapsuleSkyAngleScale)
SHADER_PARAMETER(float, CapsuleMinSkyAngle)
SHADER_PARAMETER_SRV(Buffer<float4>, LightDirectionData)
SHADER_PARAMETER_UAV(RWBuffer<float4>, RWComputedLightDirectionData)
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportCapsuleShadows(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GComputeLightDirectionFromVolumetricLightmapGroupSize);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), 1);
OutEnvironment.SetDefine(TEXT("LIGHT_SOURCE_MODE"), TEXT("LIGHT_SOURCE_FROM_CAPSULE"));
}
};
IMPLEMENT_GLOBAL_SHADER(FComputeLightDirectionFromVolumetricLightmapCS, "/Engine/Private/CapsuleShadowShaders.usf", "ComputeLightDirectionFromVolumetricLightmapCS", SF_Compute);
int32 GShadowShapeTileSize = 8;
int32 GetCapsuleShadowDownsampleFactor()
{
return GCapsuleShadowsFullResolution ? 1 : 2;
}
FIntPoint GetBufferSizeForCapsuleShadows()
{
return FIntPoint::DivideAndRoundDown(GetSceneTextureExtent(), GetCapsuleShadowDownsampleFactor());
}
enum class ECapsuleShadowingType
{
DirectionalLightTiledCulling,
PointLightTiledCulling,
IndirectTiledCulling,
MovableSkylightTiledCulling,
MovableSkylightTiledCullingGatherFromReceiverBentNormal,
MAX
};
enum class EIndirectShadowingPrimitiveTypes
{
CapsuleShapes,
MeshDistanceFields,
CapsuleShapesAndMeshDistanceFields,
MAX
};
enum class ELightSourceMode
{
// must match CapsuleShadowShaders.usf
Punctual = 0,
FromCapsule = 1,
FromReceiver = 2,
};
class FCapsuleShadowingCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCapsuleShadowingCS);
public:
class FShapeShadow : SHADER_PERMUTATION_ENUM_CLASS("SHAPE_SHADOW", ECapsuleShadowingType);
class FIndirectPrimitiveType : SHADER_PERMUTATION_ENUM_CLASS("INDIRECT_PRIMITIVE_TYPE", EIndirectShadowingPrimitiveTypes);
using FPermutationDomain = TShaderPermutationDomain<FShapeShadow, FIndirectPrimitiveType>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportCapsuleShadows(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization);
FPermutationDomain PermutationVector(Parameters.PermutationId);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GShadowShapeTileSize);
OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GShadowShapeTileSize);
ECapsuleShadowingType ShadowingType = PermutationVector.Get<FShapeShadow>();
OutEnvironment.SetDefine(TEXT("POINT_LIGHT"), ShadowingType == ECapsuleShadowingType::PointLightTiledCulling);
ELightSourceMode LightSourceMode = (ELightSourceMode)0;
if (ShadowingType == ECapsuleShadowingType::DirectionalLightTiledCulling || ShadowingType == ECapsuleShadowingType::PointLightTiledCulling)
{
LightSourceMode = ELightSourceMode::Punctual;
}
else if (ShadowingType == ECapsuleShadowingType::IndirectTiledCulling || ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCulling)
{
LightSourceMode = ELightSourceMode::FromCapsule;
}
else if (ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCullingGatherFromReceiverBentNormal)
{
LightSourceMode = ELightSourceMode::FromReceiver;
}
else
{
check(0);
}
OutEnvironment.SetDefine(TEXT("LIGHT_SOURCE_MODE"), (uint32)LightSourceMode);
const bool bApplyToBentNormal = ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCulling || ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCullingGatherFromReceiverBentNormal;
OutEnvironment.SetDefine(TEXT("APPLY_TO_BENT_NORMAL"), bApplyToBentNormal);
EIndirectShadowingPrimitiveTypes PrimitiveTypes = PermutationVector.Get<FIndirectPrimitiveType>();
if (PrimitiveTypes == EIndirectShadowingPrimitiveTypes::CapsuleShapes || PrimitiveTypes == EIndirectShadowingPrimitiveTypes::CapsuleShapesAndMeshDistanceFields)
{
OutEnvironment.SetDefine(TEXT("SUPPORT_CAPSULE_SHAPES"), 1);
}
if (PrimitiveTypes == EIndirectShadowingPrimitiveTypes::MeshDistanceFields || PrimitiveTypes == EIndirectShadowingPrimitiveTypes::CapsuleShapesAndMeshDistanceFields)
{
OutEnvironment.SetDefine(TEXT("SUPPORT_MESH_DISTANCE_FIELDS"), 1);
}
}
/** Default constructor. */
FCapsuleShadowingCS() {}
/** Initialization constructor. */
FCapsuleShadowingCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
ShadowFactors.Bind(Initializer.ParameterMap, TEXT("ShadowFactors"));
TileIntersectionCounts.Bind(Initializer.ParameterMap, TEXT("TileIntersectionCounts"));
TileDimensions.Bind(Initializer.ParameterMap, TEXT("TileDimensions"));
BentNormalTexture.Bind(Initializer.ParameterMap, TEXT("BentNormalTexture"));
ReceiverBentNormalTexture.Bind(Initializer.ParameterMap, TEXT("ReceiverBentNormalTexture"));
NumGroups.Bind(Initializer.ParameterMap, TEXT("NumGroups"));
LightDirection.Bind(Initializer.ParameterMap, TEXT("LightDirection"));
LightSourceRadius.Bind(Initializer.ParameterMap, TEXT("LightSourceRadius"));
RayStartOffsetDepthScale.Bind(Initializer.ParameterMap, TEXT("RayStartOffsetDepthScale"));
LightPositionAndInvRadius.Bind(Initializer.ParameterMap, TEXT("LightPositionAndInvRadius"));
LightAngleAndNormalThreshold.Bind(Initializer.ParameterMap, TEXT("LightAngleAndNormalThreshold"));
ScissorRectMinAndSize.Bind(Initializer.ParameterMap, TEXT("ScissorRectMinAndSize"));
DownsampleFactor.Bind(Initializer.ParameterMap, TEXT("DownsampleFactor"));
NumShadowCapsules.Bind(Initializer.ParameterMap, TEXT("NumShadowCapsules"));
ShadowCapsuleShapes.Bind(Initializer.ParameterMap, TEXT("ShadowCapsuleShapes"));
NumMeshDistanceFieldCasters.Bind(Initializer.ParameterMap, TEXT("NumMeshDistanceFieldCasters"));
MeshDistanceFieldCasterIndices.Bind(Initializer.ParameterMap, TEXT("MeshDistanceFieldCasterIndices"));
MaxOcclusionDistance.Bind(Initializer.ParameterMap, TEXT("MaxOcclusionDistance"));
CosFadeStartAngle.Bind(Initializer.ParameterMap, TEXT("CosFadeStartAngle"));
LightDirectionData.Bind(Initializer.ParameterMap, TEXT("LightDirectionData"));
IndirectCapsuleSelfShadowingIntensity.Bind(Initializer.ParameterMap, TEXT("IndirectCapsuleSelfShadowingIntensity"));
DistanceFieldObjectParameters.Bind(Initializer.ParameterMap);
DistanceFieldCulledObjectParameters.Bind(Initializer.ParameterMap);
}
void SetParameters(
FRHIComputeCommandList& RHICmdList,
FScene* Scene,
const FSceneView& View,
const FLightSceneInfo* LightSceneInfo,
FRHITexture* OutputTexture,
FRHIUnorderedAccessView* OutputTextureUAV,
FIntPoint TileDimensionsValue,
const FRWBuffer* TileIntersectionCountsBuffer,
FVector2D NumGroupsValue,
float MaxOcclusionDistanceValue,
const FIntRect& ScissorRect,
int32 DownsampleFactorValue,
int32 NumShadowCapsulesValue,
FRHIShaderResourceView* ShadowCapsuleShapesSRV,
int32 NumMeshDistanceFieldCastersValue,
FRHIShaderResourceView* MeshDistanceFieldCasterIndicesSRV,
FRHIShaderResourceView* LightDirectionDataSRV,
FRHITexture* ReceiverBentNormalTextureValue,
ECapsuleShadowingType ShadowingType)
{
FRHIComputeShader* ShaderRHI = RHICmdList.GetBoundComputeShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
if (TileIntersectionCountsBuffer)
{
RHICmdList.Transition(FRHITransitionInfo(TileIntersectionCountsBuffer->UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute));
}
if (ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCulling)
{
check(!ShadowFactors.IsBound());
BentNormalTexture.SetTexture(RHICmdList, ShaderRHI, OutputTexture, OutputTextureUAV);
}
else
{
check(!BentNormalTexture.IsBound());
ShadowFactors.SetTexture(RHICmdList, ShaderRHI, OutputTexture, OutputTextureUAV);
}
if (TileIntersectionCountsBuffer)
{
TileIntersectionCounts.SetBuffer(RHICmdList, ShaderRHI, *TileIntersectionCountsBuffer);
}
else
{
check(!TileIntersectionCounts.IsBound());
}
SetShaderValue(RHICmdList, ShaderRHI, TileDimensions, TileDimensionsValue);
if (ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCulling)
{
check(ReceiverBentNormalTextureValue);
SetTextureParameter(RHICmdList, ShaderRHI, ReceiverBentNormalTexture, ReceiverBentNormalTextureValue);
}
else
{
check(!ReceiverBentNormalTexture.IsBound());
}
SetShaderValue(RHICmdList, ShaderRHI, NumGroups, NumGroupsValue);
if (LightSceneInfo)
{
check(ShadowingType == ECapsuleShadowingType::DirectionalLightTiledCulling || ShadowingType == ECapsuleShadowingType::PointLightTiledCulling);
const FLightSceneProxy& LightProxy = *LightSceneInfo->Proxy;
FLightShaderParameters LightParameters;
LightProxy.GetLightShaderParameters(LightParameters);
SetShaderValue(RHICmdList, ShaderRHI, LightDirection, LightParameters.Direction);
FVector4 LightPositionAndInvRadiusValue(LightParameters.Position, LightParameters.InvRadius);
SetShaderValue(RHICmdList, ShaderRHI, LightPositionAndInvRadius, LightPositionAndInvRadiusValue);
// Default light source radius of 0 gives poor results
SetShaderValue(RHICmdList, ShaderRHI, LightSourceRadius, LightParameters.SourceRadius == 0 ? 20 : FMath::Clamp(LightParameters.SourceRadius, .001f, 1.0f / (4 * LightParameters.InvRadius)));
SetShaderValue(RHICmdList, ShaderRHI, RayStartOffsetDepthScale, LightProxy.GetRayStartOffsetDepthScale());
const float LightSourceAngle = FMath::Clamp(LightProxy.GetLightSourceAngle() * 5, 1.0f, 30.0f) * PI / 180.0f;
const FVector3f LightAngleAndNormalThresholdValue(LightSourceAngle, FMath::Cos(PI / 2 + LightSourceAngle), LightProxy.GetTraceDistance());
SetShaderValue(RHICmdList, ShaderRHI, LightAngleAndNormalThreshold, LightAngleAndNormalThresholdValue);
}
else
{
check(ShadowingType == ECapsuleShadowingType::IndirectTiledCulling || ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCulling || ShadowingType == ECapsuleShadowingType::MovableSkylightTiledCullingGatherFromReceiverBentNormal);
check(!LightDirection.IsBound() && !LightPositionAndInvRadius.IsBound());
}
SetShaderValue(RHICmdList, ShaderRHI, ScissorRectMinAndSize, FIntRect(ScissorRect.Min, ScissorRect.Size()));
SetShaderValue(RHICmdList, ShaderRHI, DownsampleFactor, DownsampleFactorValue);
SetShaderValue(RHICmdList, ShaderRHI, NumShadowCapsules, NumShadowCapsulesValue);
SetSRVParameter(RHICmdList, ShaderRHI, ShadowCapsuleShapes, ShadowCapsuleShapesSRV);
SetShaderValue(RHICmdList, ShaderRHI, NumMeshDistanceFieldCasters, NumMeshDistanceFieldCastersValue);
SetSRVParameter(RHICmdList, ShaderRHI, MeshDistanceFieldCasterIndices, MeshDistanceFieldCasterIndicesSRV);
SetShaderValue(RHICmdList, ShaderRHI, MaxOcclusionDistance, MaxOcclusionDistanceValue);
const float CosFadeStartAngleValue = FMath::Cos(GCapsuleShadowFadeAngleFromVertical);
const FVector2D CosFadeStartAngleVector(CosFadeStartAngleValue, 1.0f / (1.0f - CosFadeStartAngleValue));
SetShaderValue(RHICmdList, ShaderRHI, CosFadeStartAngle, CosFadeStartAngleVector);
SetSRVParameter(RHICmdList, ShaderRHI, LightDirectionData, LightDirectionDataSRV);
float IndirectCapsuleSelfShadowingIntensityValue = Scene->DynamicIndirectShadowsSelfShadowingIntensity;
SetShaderValue(RHICmdList, ShaderRHI, IndirectCapsuleSelfShadowingIntensity, IndirectCapsuleSelfShadowingIntensityValue);
if (Scene->DistanceFieldSceneData.GetCurrentObjectBuffers())
{
DistanceFieldObjectParameters.Set(
RHICmdList,
ShaderRHI,
*Scene->DistanceFieldSceneData.GetCurrentObjectBuffers(),
Scene->DistanceFieldSceneData.NumObjectsInBuffer);
TDistanceFieldCulledObjectBuffers<DFPT_SignedDistanceField> Dummy;
DistanceFieldCulledObjectParameters.Set(
RHICmdList,
ShaderRHI,
Dummy,
Scene->DistanceFieldSceneData);
}
else
{
check(!DistanceFieldObjectParameters.AnyBound());
}
}
void UnsetParameters(FRHIComputeCommandList& RHICmdList, const FRWBuffer* TileIntersectionCountsBuffer)
{
ShadowFactors.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
BentNormalTexture.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
TileIntersectionCounts.UnsetUAV(RHICmdList, RHICmdList.GetBoundComputeShader());
if (TileIntersectionCountsBuffer)
{
RHICmdList.Transition(FRHITransitionInfo(TileIntersectionCountsBuffer->UAV, ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
}
private:
LAYOUT_FIELD(FRWShaderParameter, ShadowFactors);
LAYOUT_FIELD(FRWShaderParameter, TileIntersectionCounts);
LAYOUT_FIELD(FShaderParameter, TileDimensions);
LAYOUT_FIELD(FRWShaderParameter, BentNormalTexture);
LAYOUT_FIELD(FShaderResourceParameter, ReceiverBentNormalTexture);
LAYOUT_FIELD(FShaderParameter, NumGroups);
LAYOUT_FIELD(FShaderParameter, LightDirection);
LAYOUT_FIELD(FShaderParameter, LightPositionAndInvRadius);
LAYOUT_FIELD(FShaderParameter, LightSourceRadius);
LAYOUT_FIELD(FShaderParameter, RayStartOffsetDepthScale);
LAYOUT_FIELD(FShaderParameter, LightAngleAndNormalThreshold);
LAYOUT_FIELD(FShaderParameter, ScissorRectMinAndSize);
LAYOUT_FIELD(FShaderParameter, DownsampleFactor);
LAYOUT_FIELD(FShaderParameter, NumShadowCapsules);
LAYOUT_FIELD(FShaderResourceParameter, ShadowCapsuleShapes);
LAYOUT_FIELD(FShaderParameter, NumMeshDistanceFieldCasters);
LAYOUT_FIELD(FShaderResourceParameter, MeshDistanceFieldCasterIndices);
LAYOUT_FIELD(FShaderParameter, MaxOcclusionDistance);
LAYOUT_FIELD(FShaderParameter, CosFadeStartAngle);
LAYOUT_FIELD(FShaderResourceParameter, LightDirectionData);
LAYOUT_FIELD(FShaderParameter, IndirectCapsuleSelfShadowingIntensity);
LAYOUT_FIELD((TDistanceFieldObjectBufferParameters<DFPT_SignedDistanceField>), DistanceFieldObjectParameters);
LAYOUT_FIELD((TDistanceFieldCulledObjectBufferParameters<DFPT_SignedDistanceField>), DistanceFieldCulledObjectParameters);
};
IMPLEMENT_GLOBAL_SHADER(FCapsuleShadowingCS, "/Engine/Private/CapsuleShadowShaders.usf", "CapsuleShadowingCS", SF_Compute);
class FCapsuleShadowingUpsampleVS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FCapsuleShadowingUpsampleVS, Global);
public:
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportCapsuleShadows(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("TILES_PER_INSTANCE"), NumTileQuadsInBuffer);
}
/** Default constructor. */
FCapsuleShadowingUpsampleVS() {}
/** Initialization constructor. */
FCapsuleShadowingUpsampleVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
TileDimensions.Bind(Initializer.ParameterMap,TEXT("TileDimensions"));
TileSize.Bind(Initializer.ParameterMap,TEXT("TileSize"));
ScissorRectMinAndSize.Bind(Initializer.ParameterMap,TEXT("ScissorRectMinAndSize"));
TileIntersectionCounts.Bind(Initializer.ParameterMap,TEXT("TileIntersectionCounts"));
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, FIntPoint TileDimensionsValue, const FIntRect& ScissorRect, const FRWBuffer& TileIntersectionCountsBuffer)
{
FRHIVertexShader* ShaderRHI = RHICmdList.GetBoundVertexShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
SetShaderValue(RHICmdList, ShaderRHI, TileDimensions, TileDimensionsValue);
SetShaderValue(RHICmdList, ShaderRHI, TileSize, FVector2D(
GShadowShapeTileSize * GetCapsuleShadowDownsampleFactor(),
GShadowShapeTileSize * GetCapsuleShadowDownsampleFactor()));
SetShaderValue(RHICmdList, ShaderRHI, ScissorRectMinAndSize, FIntRect(ScissorRect.Min, ScissorRect.Size()));
SetSRVParameter(RHICmdList, ShaderRHI, TileIntersectionCounts, TileIntersectionCountsBuffer.SRV);
}
private:
LAYOUT_FIELD(FShaderParameter, TileDimensions);
LAYOUT_FIELD(FShaderParameter, TileSize);
LAYOUT_FIELD(FShaderParameter, ScissorRectMinAndSize);
LAYOUT_FIELD(FShaderResourceParameter, TileIntersectionCounts);
};
IMPLEMENT_GLOBAL_SHADER(FCapsuleShadowingUpsampleVS, "/Engine/Private/CapsuleShadowShaders.usf", "CapsuleShadowingUpsampleVS", SF_Vertex);
class FCapsuleShadowingUpsamplePS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FCapsuleShadowingUpsamplePS);
public:
class FUpsampleRequired : SHADER_PERMUTATION_BOOL("UPSAMPLE_REQUIRED");
class FApplySSAO : SHADER_PERMUTATION_BOOL("APPLY_TO_SSAO");
using FPermutationDomain = TShaderPermutationDomain<FUpsampleRequired, FApplySSAO>;
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && DoesPlatformSupportCapsuleShadows(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("DOWNSAMPLE_FACTOR"), 2);
}
/** Default constructor. */
FCapsuleShadowingUpsamplePS() {}
/** Initialization constructor. */
FCapsuleShadowingUpsamplePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
ShadowFactorsTexture.Bind(Initializer.ParameterMap,TEXT("ShadowFactorsTexture"));
ShadowFactorsSampler.Bind(Initializer.ParameterMap,TEXT("ShadowFactorsSampler"));
ScissorRectMinAndSize.Bind(Initializer.ParameterMap,TEXT("ScissorRectMinAndSize"));
OutputtingToLightAttenuation.Bind(Initializer.ParameterMap,TEXT("OutputtingToLightAttenuation"));
}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FIntRect& ScissorRect, FRHITexture* ShadowFactorsTextureRHI, bool bOutputtingToLightAttenuation)
{
FRHIPixelShader* ShaderRHI = RHICmdList.GetBoundPixelShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
SetTextureParameter(RHICmdList, ShaderRHI, ShadowFactorsTexture, ShadowFactorsSampler, TStaticSamplerState<SF_Bilinear>::GetRHI(), ShadowFactorsTextureRHI);
SetShaderValue(RHICmdList, ShaderRHI, ScissorRectMinAndSize, FIntRect(ScissorRect.Min, ScissorRect.Size()));
SetShaderValue(RHICmdList, ShaderRHI, OutputtingToLightAttenuation, bOutputtingToLightAttenuation ? 1.0f : 0.0f);
}
private:
LAYOUT_FIELD(FShaderResourceParameter, ShadowFactorsTexture);
LAYOUT_FIELD(FShaderResourceParameter, ShadowFactorsSampler);
LAYOUT_FIELD(FShaderParameter, ScissorRectMinAndSize);
LAYOUT_FIELD(FShaderParameter, OutputtingToLightAttenuation);
};
IMPLEMENT_GLOBAL_SHADER(FCapsuleShadowingUpsamplePS, "/Engine/Private/CapsuleShadowShaders.usf", "CapsuleShadowingUpsamplePS", SF_Pixel);
void AllocateCapsuleTileIntersectionCountsBuffer(FIntPoint GroupSize, FSceneViewState* ViewState)
{
EPixelFormat CapsuleTileIntersectionCountsBufferFormat = PF_R32_UINT;
if (!IsValidRef(ViewState->CapsuleTileIntersectionCountsBuffer.Buffer)
|| (int32)ViewState->CapsuleTileIntersectionCountsBuffer.NumBytes < GroupSize.X * GroupSize.Y * GPixelFormats[CapsuleTileIntersectionCountsBufferFormat].BlockBytes)
{
ViewState->CapsuleTileIntersectionCountsBuffer.Release();
ViewState->CapsuleTileIntersectionCountsBuffer.Initialize(TEXT("CapsuleTileIntersectionCountsBuffer"), GPixelFormats[CapsuleTileIntersectionCountsBufferFormat].BlockBytes, GroupSize.X * GroupSize.Y, CapsuleTileIntersectionCountsBufferFormat);
}
}
// TODO(RDG) Move these into the shader FParameters.
BEGIN_SHADER_PARAMETER_STRUCT(FTiledCapsuleShadowParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RayTracedShadowsUAV)
RDG_TEXTURE_ACCESS(RayTracedShadows, ERHIAccess::UAVCompute)
END_SHADER_PARAMETER_STRUCT()
// TODO(RDG) Move these into the shader FParameters.
BEGIN_SHADER_PARAMETER_STRUCT(FUpsampleCapsuleShadowParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RDG_TEXTURE_ACCESS(RayTracedShadows, ERHIAccess::SRVGraphics)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
bool FDeferredShadingSceneRenderer::RenderCapsuleDirectShadows(
FRDGBuilder& GraphBuilder,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
const FLightSceneInfo& LightSceneInfo,
FRDGTextureRef ScreenShadowMaskTexture,
TArrayView<const FProjectedShadowInfo* const> CapsuleShadows,
bool bProjectingForForwardShading) const
{
bool bAllViewsHaveViewState = true;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (!View.ViewState)
{
bAllViewsHaveViewState = false;
}
}
if (!SupportsCapsuleDirectShadows(FeatureLevel, GShaderPlatformForFeatureLevel[FeatureLevel])
|| CapsuleShadows.Num() == 0
|| !ViewFamily.EngineShowFlags.CapsuleShadows
|| !bAllViewsHaveViewState)
{
return false;
}
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderCapsuleShadows);
FRDGTextureRef RayTracedShadowsRT = nullptr;
{
const FIntPoint BufferSize = GetBufferSizeForCapsuleShadows();
const FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(BufferSize, PF_G16R16F, FClearValueBinding::None, TexCreate_RenderTargetable | TexCreate_UAV));
RayTracedShadowsRT = GraphBuilder.CreateTexture(Desc, TEXT("RayTracedShadows"));
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_EVENT_SCOPE(GraphBuilder, "CapsuleShadows");
RDG_GPU_STAT_SCOPE(GraphBuilder, CapsuleShadows);
TArray<FCapsuleShape> CapsuleShapeData;
for (int32 ShadowIndex = 0; ShadowIndex < CapsuleShadows.Num(); ShadowIndex++)
{
const FProjectedShadowInfo* Shadow = CapsuleShadows[ShadowIndex];
int32 OriginalCapsuleIndex = CapsuleShapeData.Num();
TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> ShadowGroupPrimitives;
Shadow->GetParentSceneInfo()->GatherLightingAttachmentGroupPrimitives(ShadowGroupPrimitives);
for (int32 ChildIndex = 0; ChildIndex < ShadowGroupPrimitives.Num(); ChildIndex++)
{
const FPrimitiveSceneInfo* PrimitiveSceneInfo = ShadowGroupPrimitives[ChildIndex];
if (PrimitiveSceneInfo->Proxy->CastsDynamicShadow())
{
PrimitiveSceneInfo->Proxy->GetShadowShapes(CapsuleShapeData);
}
}
const float FadeRadiusScale = Shadow->FadeAlphas[ViewIndex];
for (int32 ShapeIndex = OriginalCapsuleIndex; ShapeIndex < CapsuleShapeData.Num(); ShapeIndex++)
{
CapsuleShapeData[ShapeIndex].Radius *= FadeRadiusScale;
}
}
if (CapsuleShapeData.Num() > 0)
{
const bool bDirectionalLight = LightSceneInfo.Proxy->GetLightType() == LightType_Directional;
FIntRect ScissorRect;
if (!LightSceneInfo.Proxy->GetScissorRect(ScissorRect, View, View.ViewRect))
{
ScissorRect = View.ViewRect;
}
const FIntPoint GroupSize(
FMath::DivideAndRoundUp(ScissorRect.Size().X / GetCapsuleShadowDownsampleFactor(), GShadowShapeTileSize),
FMath::DivideAndRoundUp(ScissorRect.Size().Y / GetCapsuleShadowDownsampleFactor(), GShadowShapeTileSize));
AllocateCapsuleTileIntersectionCountsBuffer(GroupSize, View.ViewState);
int32 NumCapsuleShapeData = CapsuleShapeData.Num();
AddPass(GraphBuilder, [&View, &LightSceneInfo, CapsuleShapeData = MoveTemp(CapsuleShapeData)](FRHICommandListImmediate& RHICmdList)
{
static_assert(sizeof(FCapsuleShape) == sizeof(FVector4) * 2, "FCapsuleShape has padding");
const int32 DataSize = CapsuleShapeData.Num() * CapsuleShapeData.GetTypeSize();
if (!IsValidRef(LightSceneInfo.ShadowCapsuleShapesVertexBuffer) || (int32)LightSceneInfo.ShadowCapsuleShapesVertexBuffer->GetSize() < DataSize)
{
LightSceneInfo.ShadowCapsuleShapesVertexBuffer.SafeRelease();
LightSceneInfo.ShadowCapsuleShapesSRV.SafeRelease();
FRHIResourceCreateInfo CreateInfo(TEXT("LightSceneInfo"));
LightSceneInfo.ShadowCapsuleShapesVertexBuffer = RHICreateVertexBuffer(DataSize, BUF_Volatile | BUF_ShaderResource, CreateInfo);
LightSceneInfo.ShadowCapsuleShapesSRV = RHICreateShaderResourceView(LightSceneInfo.ShadowCapsuleShapesVertexBuffer, sizeof(FVector4), PF_A32B32G32R32F);
}
void* CapsuleShapeLockedData = RHILockBuffer(LightSceneInfo.ShadowCapsuleShapesVertexBuffer, 0, DataSize, RLM_WriteOnly);
FPlatformMemory::Memcpy(CapsuleShapeLockedData, CapsuleShapeData.GetData(), DataSize);
RHIUnlockBuffer(LightSceneInfo.ShadowCapsuleShapesVertexBuffer);
RHICmdList.Transition(FRHITransitionInfo(View.ViewState->CapsuleTileIntersectionCountsBuffer.UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute));
RHICmdList.ClearUAVUint(View.ViewState->CapsuleTileIntersectionCountsBuffer.UAV, FUintVector4(0, 0, 0, 0));
});
{
FTiledCapsuleShadowParameters* PassParameters = GraphBuilder.AllocParameters<FTiledCapsuleShadowParameters>();
PassParameters->RayTracedShadows = RayTracedShadowsRT;
PassParameters->RayTracedShadowsUAV = GraphBuilder.CreateUAV(RayTracedShadowsRT);
PassParameters->SceneTextures = SceneTexturesUniformBuffer;
ECapsuleShadowingType ShadowingType = bDirectionalLight ? ECapsuleShadowingType::DirectionalLightTiledCulling : ECapsuleShadowingType::PointLightTiledCulling;
FCapsuleShadowingCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCapsuleShadowingCS::FShapeShadow>(ShadowingType);
PermutationVector.Set<FCapsuleShadowingCS::FIndirectPrimitiveType>(EIndirectShadowingPrimitiveTypes::CapsuleShapes);
auto ComputeShader = View.ShaderMap->GetShader<FCapsuleShadowingCS>(PermutationVector);
GraphBuilder.AddPass(
RDG_EVENT_NAME("TiledCapsuleShadowing"),
PassParameters,
ERDGPassFlags::Compute,
[ComputeShader, this, &View, &LightSceneInfo, RayTracedShadows = PassParameters->RayTracedShadows.GetTexture(), RayTracedShadowsUAV = PassParameters->RayTracedShadowsUAV, GroupSize, ScissorRect, NumCapsuleShapeData, ShadowingType](FRHIComputeCommandList& RHICmdList)
{
FRHIComputeShader* ShaderRHI = ComputeShader.GetComputeShader();
RHICmdList.SetComputeShader(ShaderRHI);
ComputeShader->SetParameters(
RHICmdList,
Scene,
View,
&LightSceneInfo,
RayTracedShadows->GetRHI(),
RayTracedShadowsUAV->GetRHI(),
GroupSize,
&View.ViewState->CapsuleTileIntersectionCountsBuffer,
FVector2D(GroupSize.X, GroupSize.Y),
GCapsuleMaxDirectOcclusionDistance,
ScissorRect,
GetCapsuleShadowDownsampleFactor(),
NumCapsuleShapeData,
LightSceneInfo.ShadowCapsuleShapesSRV.GetReference(),
0,
nullptr,
nullptr,
nullptr,
ShadowingType);
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), GroupSize.X, GroupSize.Y, 1);
ComputeShader->UnsetParameters(RHICmdList, &View.ViewState->CapsuleTileIntersectionCountsBuffer);
});
}
{
FUpsampleCapsuleShadowParameters* PassParameters = GraphBuilder.AllocParameters<FUpsampleCapsuleShadowParameters>();
PassParameters->RenderTargets[0] = FRenderTargetBinding(ScreenShadowMaskTexture, ERenderTargetLoadAction::ELoad);
PassParameters->RayTracedShadows = RayTracedShadowsRT;
PassParameters->SceneTextures = SceneTexturesUniformBuffer;
GraphBuilder.AddPass(
RDG_EVENT_NAME("UpsampleCapsuleShadow %dx%d", ScissorRect.Width(), ScissorRect.Height()),
PassParameters,
ERDGPassFlags::Raster,
[this, &View, &LightSceneInfo, RayTracedShadowsRT, GroupSize, ScissorRect, bProjectingForForwardShading](FRHICommandList& RHICmdList)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BlendState = FProjectedShadowInfo::GetBlendStateForProjection(
LightSceneInfo.GetDynamicShadowMapChannel(),
false,
false,
bProjectingForForwardShading,
false);
auto VertexShader = View.ShaderMap->GetShader<FCapsuleShadowingUpsampleVS>();
FCapsuleShadowingUpsamplePS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCapsuleShadowingUpsamplePS::FUpsampleRequired>(!GCapsuleShadowsFullResolution);
PermutationVector.Set<FCapsuleShadowingUpsamplePS::FApplySSAO>(false);
auto PixelShader = View.ShaderMap->GetShader<FCapsuleShadowingUpsamplePS>(PermutationVector);
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GTileVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
VertexShader->SetParameters(RHICmdList, View, GroupSize, ScissorRect, View.ViewState->CapsuleTileIntersectionCountsBuffer);
PixelShader->SetParameters(RHICmdList, View, ScissorRect, RayTracedShadowsRT->GetRHI(), true);
RHICmdList.SetStreamSource(0, GTileTexCoordVertexBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(GTileIndexBuffer.IndexBufferRHI,
0,
0,
4,
0,
2 * NumTileQuadsInBuffer,
FMath::DivideAndRoundUp(GroupSize.X * GroupSize.Y, NumTileQuadsInBuffer));
});
}
}
}
return true;
}
void FDeferredShadingSceneRenderer::CreateIndirectCapsuleShadows()
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_CreateIndirectCapsuleShadows);
if (!SupportsCapsuleIndirectShadows(FeatureLevel, ShaderPlatform))
{
return;
}
for (int32 PrimitiveIndex = 0; PrimitiveIndex < Scene->DynamicIndirectCasterPrimitives.Num(); PrimitiveIndex++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = Scene->DynamicIndirectCasterPrimitives[PrimitiveIndex];
FPrimitiveSceneProxy* PrimitiveProxy = PrimitiveSceneInfo->Proxy;
if (PrimitiveProxy->CastsDynamicShadow() && PrimitiveProxy->CastsDynamicIndirectShadow())
{
TArray<FPrimitiveSceneInfo*, SceneRenderingAllocator> ShadowGroupPrimitives;
PrimitiveSceneInfo->GatherLightingAttachmentGroupPrimitives(ShadowGroupPrimitives);
// Compute the composite bounds of this group of shadow primitives.
FBoxSphereBounds LightingGroupBounds = ShadowGroupPrimitives[0]->Proxy->GetBounds();
for (int32 ChildIndex = 1; ChildIndex < ShadowGroupPrimitives.Num(); ChildIndex++)
{
const FPrimitiveSceneInfo* ShadowChild = ShadowGroupPrimitives[ChildIndex];
if (ShadowChild->Proxy->CastsDynamicShadow())
{
LightingGroupBounds = LightingGroupBounds + ShadowChild->Proxy->GetBounds();
}
}
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
float EffectiveMaxIndirectOcclusionDistance = GCapsuleMaxIndirectOcclusionDistance;
if (PrimitiveProxy->HasDistanceFieldRepresentation())
{
// Increase max occlusion distance based on object size for distance field casters
// This improves the solidness of the shadows, since the fadeout distance causes internal structure of objects to become visible
EffectiveMaxIndirectOcclusionDistance += .5f * LightingGroupBounds.SphereRadius;
}
if (View.ViewFrustum.IntersectBox(LightingGroupBounds.Origin, LightingGroupBounds.BoxExtent + FVector(EffectiveMaxIndirectOcclusionDistance)))
{
View.IndirectShadowPrimitives.Add(PrimitiveSceneInfo);
}
}
}
}
}
void FDeferredShadingSceneRenderer::SetupIndirectCapsuleShadows(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
int32& NumCapsuleShapes,
int32& NumMeshesWithCapsules,
int32& NumMeshDistanceFieldCasters,
FRHIShaderResourceView*& IndirectShadowLightDirectionSRV) const
{
const float CosFadeStartAngle = FMath::Cos(GCapsuleShadowFadeAngleFromVertical);
const FSkyLightSceneProxy* SkyLight = Scene ? Scene->SkyLight : NULL;
static TArray<FCapsuleShape> CapsuleShapeData;
static TArray<FVector4> CapsuleLightSourceData;
static TArray<int32, TInlineAllocator<1>> MeshDistanceFieldCasterIndices;
static TArray<FVector4> DistanceFieldCasterLightSourceData;
CapsuleShapeData.Reset();
MeshDistanceFieldCasterIndices.Reset();
CapsuleLightSourceData.Reset();
DistanceFieldCasterLightSourceData.Reset();
IndirectShadowLightDirectionSRV = NULL;
const bool bComputeLightDataFromVolumetricLightmapOrGpuSkyEnvMapIrradiance = Scene && (Scene->VolumetricLightmapSceneData.HasData() || (Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled));
for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.IndirectShadowPrimitives.Num(); PrimitiveIndex++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = View.IndirectShadowPrimitives[PrimitiveIndex];
const FIndirectLightingCacheAllocation* Allocation = PrimitiveSceneInfo->IndirectLightingCacheAllocation;
FVector4 PackedLightDirection(0, 0, 1, PI / 16);
float ShapeFadeAlpha = 1;
if (bComputeLightDataFromVolumetricLightmapOrGpuSkyEnvMapIrradiance)
{
// Encode object position for ComputeLightDirectionsFromVolumetricLightmapCS
PackedLightDirection = FVector4(PrimitiveSceneInfo->Proxy->GetBounds().Origin, 0);
}
else if (SkyLight
&& !SkyLight->bHasStaticLighting
&& SkyLight->bWantsStaticShadowing
&& View.Family->EngineShowFlags.SkyLighting
&& Allocation)
{
// Stationary sky light case
// Get the indirect shadow direction from the unoccluded sky direction
const float ConeAngle = FMath::Max(Allocation->CurrentSkyBentNormal.W * GCapsuleSkyAngleScale * .5f * PI, GCapsuleMinSkyAngle * PI / 180.0f);
PackedLightDirection = FVector4(FVector3f(Allocation->CurrentSkyBentNormal), ConeAngle);
}
else if (SkyLight
&& !SkyLight->bHasStaticLighting
&& !SkyLight->bWantsStaticShadowing
&& View.Family->EngineShowFlags.SkyLighting)
{
// Movable sky light case
const FSHVector2 SkyLightingIntensity = FSHVectorRGB2(SkyLight->IrradianceEnvironmentMap).GetLuminance();
const FVector ExtractedMaxDirection = SkyLightingIntensity.GetMaximumDirection();
// Get the indirect shadow direction from the primary sky lighting direction
PackedLightDirection = FVector4(ExtractedMaxDirection, GCapsuleIndirectConeAngle);
}
else if (Allocation)
{
// Static sky light or no sky light case
FSHVectorRGB2 IndirectLighting;
IndirectLighting.R = FSHVector2(Allocation->SingleSamplePacked0[0]);
IndirectLighting.G = FSHVector2(Allocation->SingleSamplePacked0[1]);
IndirectLighting.B = FSHVector2(Allocation->SingleSamplePacked0[2]);
const FSHVector2 IndirectLightingIntensity = IndirectLighting.GetLuminance();
const FVector ExtractedMaxDirection = IndirectLightingIntensity.GetMaximumDirection();
// Get the indirect shadow direction from the primary indirect lighting direction
PackedLightDirection = FVector4(ExtractedMaxDirection, GCapsuleIndirectConeAngle);
}
if (CosFadeStartAngle < 1 && !bComputeLightDataFromVolumetricLightmapOrGpuSkyEnvMapIrradiance)
{
// Fade out when nearly vertical up due to self shadowing artifacts
ShapeFadeAlpha = 1 - FMath::Clamp(2 * (-PackedLightDirection.Z - CosFadeStartAngle) / (1 - CosFadeStartAngle), 0.0f, 1.0f);
}
if (ShapeFadeAlpha > 0)
{
const int32 OriginalNumCapsuleShapes = CapsuleShapeData.Num();
const int32 OriginalNumMeshDistanceFieldCasters = MeshDistanceFieldCasterIndices.Num();
TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> ShadowGroupPrimitives;
PrimitiveSceneInfo->GatherLightingAttachmentGroupPrimitives(ShadowGroupPrimitives);
for (int32 ChildIndex = 0; ChildIndex < ShadowGroupPrimitives.Num(); ChildIndex++)
{
const FPrimitiveSceneInfo* GroupPrimitiveSceneInfo = ShadowGroupPrimitives[ChildIndex];
if (GroupPrimitiveSceneInfo->Proxy->CastsDynamicShadow())
{
GroupPrimitiveSceneInfo->Proxy->GetShadowShapes(CapsuleShapeData);
if (GroupPrimitiveSceneInfo->Proxy->HasDistanceFieldRepresentation())
{
MeshDistanceFieldCasterIndices.Append(GroupPrimitiveSceneInfo->DistanceFieldInstanceIndices);
}
}
}
// Pack both values into a single float to keep float4 alignment
const FFloat16 LightAngle16f = FFloat16(PackedLightDirection.W);
const FFloat16 MinVisibility16f = FFloat16(PrimitiveSceneInfo->Proxy->GetDynamicIndirectShadowMinVisibility());
const uint32 PackedWInt = ((uint32)LightAngle16f.Encoded) | ((uint32)MinVisibility16f.Encoded << 16);
PackedLightDirection.W = *(float*)&PackedWInt;
//@todo - remove entries with 0 fade alpha
for (int32 ShapeIndex = OriginalNumCapsuleShapes; ShapeIndex < CapsuleShapeData.Num(); ShapeIndex++)
{
CapsuleLightSourceData.Add(PackedLightDirection);
}
for (int32 CasterIndex = OriginalNumMeshDistanceFieldCasters; CasterIndex < MeshDistanceFieldCasterIndices.Num(); CasterIndex++)
{
DistanceFieldCasterLightSourceData.Add(PackedLightDirection);
}
NumMeshesWithCapsules++;
}
}
if (CapsuleShapeData.Num() > 0 || MeshDistanceFieldCasterIndices.Num() > 0)
{
static_assert(sizeof(FCapsuleShape) == sizeof(FVector4)* 2, "FCapsuleShape has padding");
if (CapsuleShapeData.Num() > 0)
{
const int32 DataSize = CapsuleShapeData.Num() * CapsuleShapeData.GetTypeSize();
if (!IsValidRef(View.ViewState->IndirectShadowCapsuleShapesVertexBuffer) || (int32)View.ViewState->IndirectShadowCapsuleShapesVertexBuffer->GetSize() < DataSize)
{
View.ViewState->IndirectShadowCapsuleShapesVertexBuffer.SafeRelease();
View.ViewState->IndirectShadowCapsuleShapesSRV.SafeRelease();
FRHIResourceCreateInfo CreateInfo(TEXT("IndirectShadowCapsuleShapesVertexBuffer"));
View.ViewState->IndirectShadowCapsuleShapesVertexBuffer = RHICreateVertexBuffer(DataSize, BUF_Volatile | BUF_ShaderResource, CreateInfo);
View.ViewState->IndirectShadowCapsuleShapesSRV = RHICreateShaderResourceView(View.ViewState->IndirectShadowCapsuleShapesVertexBuffer, sizeof(FVector4), PF_A32B32G32R32F);
}
void* CapsuleShapeLockedData = RHILockBuffer(View.ViewState->IndirectShadowCapsuleShapesVertexBuffer, 0, DataSize, RLM_WriteOnly);
FPlatformMemory::Memcpy(CapsuleShapeLockedData, CapsuleShapeData.GetData(), DataSize);
RHIUnlockBuffer(View.ViewState->IndirectShadowCapsuleShapesVertexBuffer);
}
if (MeshDistanceFieldCasterIndices.Num() > 0)
{
const int32 DataSize = MeshDistanceFieldCasterIndices.Num() * MeshDistanceFieldCasterIndices.GetTypeSize();
if (!IsValidRef(View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer) || (int32)View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer->GetSize() < DataSize)
{
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer.SafeRelease();
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV.SafeRelease();
FRHIResourceCreateInfo CreateInfo(TEXT("IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer"));
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer = RHICreateVertexBuffer(DataSize, BUF_Volatile | BUF_ShaderResource, CreateInfo);
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV = RHICreateShaderResourceView(View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer, sizeof(uint32), PF_R32_UINT);
}
void* LockedData = RHILockBuffer(View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer, 0, DataSize, RLM_WriteOnly);
FPlatformMemory::Memcpy(LockedData, MeshDistanceFieldCasterIndices.GetData(), DataSize);
RHIUnlockBuffer(View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesVertexBuffer);
}
EPixelFormat LightDirectionDataFormat = PF_A32B32G32R32F;
{
size_t CapsuleLightSourceDataSize = CapsuleLightSourceData.Num() * CapsuleLightSourceData.GetTypeSize();
const int32 DataSize = CapsuleLightSourceDataSize + DistanceFieldCasterLightSourceData.Num() * DistanceFieldCasterLightSourceData.GetTypeSize();
check(DataSize > 0);
if (!IsValidRef(View.ViewState->IndirectShadowLightDirectionVertexBuffer) || (int32)View.ViewState->IndirectShadowLightDirectionVertexBuffer->GetSize() < DataSize)
{
View.ViewState->IndirectShadowLightDirectionVertexBuffer.SafeRelease();
View.ViewState->IndirectShadowLightDirectionSRV.SafeRelease();
FRHIResourceCreateInfo CreateInfo(TEXT("IndirectShadowLightDirectionVertexBuffer"));
View.ViewState->IndirectShadowLightDirectionVertexBuffer = RHICreateVertexBuffer(DataSize, BUF_Volatile | BUF_ShaderResource, CreateInfo);
View.ViewState->IndirectShadowLightDirectionSRV = RHICreateShaderResourceView(View.ViewState->IndirectShadowLightDirectionVertexBuffer, sizeof(FVector4), LightDirectionDataFormat);
}
FVector4* LightDirectionLockedData = (FVector4*)RHILockBuffer(View.ViewState->IndirectShadowLightDirectionVertexBuffer, 0, DataSize, RLM_WriteOnly);
FPlatformMemory::Memcpy(LightDirectionLockedData, CapsuleLightSourceData.GetData(), CapsuleLightSourceDataSize);
// Light data for distance fields is placed after capsule light data
// This packing behavior must match GetLightDirectionData
FPlatformMemory::Memcpy((char*)LightDirectionLockedData + CapsuleLightSourceDataSize, DistanceFieldCasterLightSourceData.GetData(), DistanceFieldCasterLightSourceData.Num() * DistanceFieldCasterLightSourceData.GetTypeSize());
RHIUnlockBuffer(View.ViewState->IndirectShadowLightDirectionVertexBuffer);
IndirectShadowLightDirectionSRV = View.ViewState->IndirectShadowLightDirectionSRV;
}
if (bComputeLightDataFromVolumetricLightmapOrGpuSkyEnvMapIrradiance)
{
int32 NumLightDataElements = CapsuleLightSourceData.Num() + DistanceFieldCasterLightSourceData.Num();
if (!IsValidRef(View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection.Buffer)
|| (int32)View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection.NumBytes != View.ViewState->IndirectShadowLightDirectionVertexBuffer->GetSize())
{
View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection.Release();
View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection.Initialize(TEXT("IndirectShadowVolumetricLightmapDerivedLightDirection"), GPixelFormats[LightDirectionDataFormat].BlockBytes, NumLightDataElements, LightDirectionDataFormat);
}
IndirectShadowLightDirectionSRV = View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection.SRV;
TShaderMapRef<FComputeLightDirectionFromVolumetricLightmapCS> ComputeShader(View.ShaderMap);
uint32 SkyLightMode = Scene->SkyLight && Scene->SkyLight->bWantsStaticShadowing ? 1 : 0;
SkyLightMode = Scene->SkyLight && Scene->SkyLight->bRealTimeCaptureEnabled ? 2 : SkyLightMode;
const int32 GroupSize = FMath::DivideAndRoundUp(NumLightDataElements, GComputeLightDirectionFromVolumetricLightmapGroupSize);
FRWBuffer& ComputedLightDirectionData = View.ViewState->IndirectShadowVolumetricLightmapDerivedLightDirection;
AddPass(GraphBuilder, [&ComputedLightDirectionData](FRHIComputeCommandList& RHICmdList)
{
RHICmdList.Transition(FRHITransitionInfo(ComputedLightDirectionData.UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute));
});
FComputeLightDirectionFromVolumetricLightmapCS::FParameters* PassParameters = GraphBuilder.AllocParameters<FComputeLightDirectionFromVolumetricLightmapCS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->NumLightDirectionData = NumLightDataElements;
PassParameters->SkyLightMode = SkyLightMode;
PassParameters->CapsuleIndirectConeAngle = GCapsuleSkyAngleScale;
PassParameters->CapsuleSkyAngleScale = GCapsuleSkyAngleScale;
PassParameters->CapsuleMinSkyAngle = GCapsuleMinSkyAngle;
PassParameters->RWComputedLightDirectionData = ComputedLightDirectionData.UAV;
PassParameters->LightDirectionData = View.ViewState->IndirectShadowLightDirectionSRV;
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("LightDirectionFromVolumetricLightmap"),
ERDGPassFlags::Compute | ERDGPassFlags::NeverCull,
ComputeShader,
PassParameters,
FIntVector(GroupSize, 1, 1));
AddPass(GraphBuilder, [&ComputedLightDirectionData](FRHIComputeCommandList& RHICmdList)
{
RHICmdList.Transition(FRHITransitionInfo(ComputedLightDirectionData.UAV, ERHIAccess::UAVCompute, ERHIAccess::SRVMask));
});
}
}
NumCapsuleShapes = CapsuleShapeData.Num();
NumMeshDistanceFieldCasters = MeshDistanceFieldCasterIndices.Num();
}
void FDeferredShadingSceneRenderer::RenderIndirectCapsuleShadows(FRDGBuilder& GraphBuilder, const FSceneTextures& SceneTextures) const
{
if (!SupportsCapsuleIndirectShadows(FeatureLevel, GShaderPlatformForFeatureLevel[FeatureLevel])
|| !ViewFamily.EngineShowFlags.DynamicShadows
|| !ViewFamily.EngineShowFlags.CapsuleShadows)
{
return;
}
RDG_CSV_STAT_EXCLUSIVE_SCOPE(GraphBuilder, RenderIndirectCapsuleShadows);
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderIndirectCapsuleShadows);
bool bAnyViewsUseCapsuleShadows = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.IndirectShadowPrimitives.Num() > 0 && View.ViewState)
{
bAnyViewsUseCapsuleShadows = true;
}
}
if (!bAnyViewsUseCapsuleShadows)
{
return;
}
RDG_EVENT_SCOPE(GraphBuilder, "IndirectCapsuleShadows");
FRDGTextureRef RayTracedShadowsRT = nullptr;
{
const FIntPoint BufferSize = GetBufferSizeForCapsuleShadows();
const FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(BufferSize, PF_G16R16F, FClearValueBinding::None, TexCreate_RenderTargetable | TexCreate_UAV));
// Reuse temporary target from RTDF shadows
RayTracedShadowsRT = GraphBuilder.CreateTexture(Desc, TEXT("RayTracedShadows"));
}
TArray<FRDGTextureRef, TInlineAllocator<2>> RenderTargets;
if (SceneTextures.Color.IsValid())
{
RenderTargets.Add(SceneTextures.Color.Target);
}
check(SceneTextures.ScreenSpaceAO);
if (!SceneTextures.ScreenSpaceAO->HasBeenProduced())
{
AddClearRenderTargetPass(GraphBuilder, SceneTextures.ScreenSpaceAO);
}
RenderTargets.Add(SceneTextures.ScreenSpaceAO);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.IndirectShadowPrimitives.Num() > 0 && View.ViewState)
{
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_GPU_STAT_SCOPE(GraphBuilder, CapsuleShadows);
int32 NumCapsuleShapes = 0;
int32 NumMeshesWithCapsules = 0;
int32 NumMeshDistanceFieldCasters = 0;
FRHIShaderResourceView* IndirectShadowLightDirectionSRV = NULL;
SetupIndirectCapsuleShadows(GraphBuilder, View, NumCapsuleShapes, NumMeshesWithCapsules, NumMeshDistanceFieldCasters, IndirectShadowLightDirectionSRV);
if (NumCapsuleShapes == 0 && NumMeshDistanceFieldCasters == 0)
{
continue;
}
check(IndirectShadowLightDirectionSRV);
const FIntRect ScissorRect = View.ViewRect;
const FIntPoint GroupSize(
FMath::DivideAndRoundUp(ScissorRect.Size().X / GetCapsuleShadowDownsampleFactor(), GShadowShapeTileSize),
FMath::DivideAndRoundUp(ScissorRect.Size().Y / GetCapsuleShadowDownsampleFactor(), GShadowShapeTileSize));
AllocateCapsuleTileIntersectionCountsBuffer(GroupSize, View.ViewState);
AddPass(GraphBuilder, [&View](FRHIComputeCommandList& RHICmdList)
{
RHICmdList.Transition(FRHITransitionInfo(View.ViewState->CapsuleTileIntersectionCountsBuffer.UAV, ERHIAccess::Unknown, ERHIAccess::UAVCompute));
RHICmdList.ClearUAVUint(View.ViewState->CapsuleTileIntersectionCountsBuffer.UAV, FUintVector4(0, 0, 0, 0));
});
{
FTiledCapsuleShadowParameters* PassParameters = GraphBuilder.AllocParameters<FTiledCapsuleShadowParameters>();
PassParameters->RayTracedShadows = RayTracedShadowsRT;
PassParameters->RayTracedShadowsUAV = GraphBuilder.CreateUAV(RayTracedShadowsRT);
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
EIndirectShadowingPrimitiveTypes PrimitiveTypes;
if (NumCapsuleShapes > 0 && NumMeshDistanceFieldCasters > 0)
{
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::CapsuleShapesAndMeshDistanceFields;
}
else if (NumCapsuleShapes > 0)
{
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::CapsuleShapes;
}
else
{
check(NumMeshDistanceFieldCasters > 0);
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::MeshDistanceFields;
}
FCapsuleShadowingCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCapsuleShadowingCS::FShapeShadow>(ECapsuleShadowingType::IndirectTiledCulling);
PermutationVector.Set<FCapsuleShadowingCS::FIndirectPrimitiveType>(PrimitiveTypes);
auto ComputeShader = View.ShaderMap->GetShader<FCapsuleShadowingCS>(PermutationVector);
GraphBuilder.AddPass(
RDG_EVENT_NAME("TiledCapsuleShadowing %u capsules among %u meshes", NumCapsuleShapes, NumMeshesWithCapsules),
PassParameters,
ERDGPassFlags::Compute,
[ComputeShader, this, &View, RayTracedShadows = PassParameters->RayTracedShadows.GetTexture(), RayTracedShadowsUAV = PassParameters->RayTracedShadowsUAV, IndirectShadowLightDirectionSRV, GroupSize, ScissorRect, NumCapsuleShapes, NumMeshDistanceFieldCasters](FRHIComputeCommandList& RHICmdList)
{
FRHIComputeShader* ShaderRHI = ComputeShader.GetComputeShader();
RHICmdList.SetComputeShader(ShaderRHI);
ComputeShader->SetParameters(
RHICmdList,
Scene,
View,
nullptr,
RayTracedShadows->GetRHI(),
RayTracedShadowsUAV->GetRHI(),
GroupSize,
&View.ViewState->CapsuleTileIntersectionCountsBuffer,
FVector2D(GroupSize.X, GroupSize.Y),
GCapsuleMaxIndirectOcclusionDistance,
ScissorRect,
GetCapsuleShadowDownsampleFactor(),
NumCapsuleShapes,
View.ViewState->IndirectShadowCapsuleShapesSRV ? View.ViewState->IndirectShadowCapsuleShapesSRV.GetReference() : nullptr,
NumMeshDistanceFieldCasters,
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV ? View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV.GetReference() : nullptr,
IndirectShadowLightDirectionSRV,
nullptr,
ECapsuleShadowingType::IndirectTiledCulling);
DispatchComputeShader(RHICmdList, ComputeShader, GroupSize.X, GroupSize.Y, 1);
ComputeShader->UnsetParameters(RHICmdList, &View.ViewState->CapsuleTileIntersectionCountsBuffer);
});
}
{
const int32 RenderTargetCount = RenderTargets.Num();
FUpsampleCapsuleShadowParameters* PassParameters = GraphBuilder.AllocParameters<FUpsampleCapsuleShadowParameters>();
for (int32 Index = 0; Index < RenderTargetCount; ++Index)
{
PassParameters->RenderTargets[Index] = FRenderTargetBinding(RenderTargets[Index], ERenderTargetLoadAction::ELoad);
}
PassParameters->RayTracedShadows = RayTracedShadowsRT;
PassParameters->SceneTextures = SceneTextures.UniformBuffer;
GraphBuilder.AddPass(
RDG_EVENT_NAME("UpsampleCapsuleShadow %dx%d", ScissorRect.Width(), ScissorRect.Height()),
PassParameters,
ERDGPassFlags::Raster,
[this, &View, RayTracedShadowsRT, RenderTargetCount, GroupSize, ScissorRect](FRHICommandList& RHICmdList)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
// Modulative blending against scene color for application to indirect diffuse
if (RenderTargetCount > 1)
{
GraphicsPSOInit.BlendState = TStaticBlendState<
CW_RGB, BO_Add, BF_DestColor, BF_Zero, BO_Add, BF_Zero, BF_One,
CW_RED, BO_Add, BF_DestColor, BF_Zero, BO_Add, BF_Zero, BF_One>::GetRHI();
}
// Modulative blending against SSAO occlusion value for application to indirect specular, since Reflection Environment pass masks by AO
else
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_DestColor, BF_Zero>::GetRHI();
}
auto VertexShader = View.ShaderMap->GetShader<FCapsuleShadowingUpsampleVS>();
FCapsuleShadowingUpsamplePS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCapsuleShadowingUpsamplePS::FUpsampleRequired>(!GCapsuleShadowsFullResolution);
PermutationVector.Set<FCapsuleShadowingUpsamplePS::FApplySSAO>(RenderTargetCount > 1);
auto PixelShader = View.ShaderMap->GetShader<FCapsuleShadowingUpsamplePS>(PermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GTileVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
VertexShader->SetParameters(RHICmdList, View, GroupSize, ScissorRect, View.ViewState->CapsuleTileIntersectionCountsBuffer);
PixelShader->SetParameters(RHICmdList, View, ScissorRect, RayTracedShadowsRT->GetRHI(), false);
RHICmdList.SetStreamSource(0, GTileTexCoordVertexBuffer.VertexBufferRHI, 0);
RHICmdList.DrawIndexedPrimitive(GTileIndexBuffer.IndexBufferRHI,
0,
0,
4,
0,
2 * NumTileQuadsInBuffer,
FMath::DivideAndRoundUp(GroupSize.X * GroupSize.Y, NumTileQuadsInBuffer));
});
}
}
}
}
bool FSceneRenderer::ShouldPrepareForDFInsetIndirectShadow() const
{
bool bSceneHasInsetDFPrimitives = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
for (int32 PrimitiveIndex = 0; PrimitiveIndex < View.IndirectShadowPrimitives.Num(); PrimitiveIndex++)
{
FPrimitiveSceneInfo* PrimitiveSceneInfo = View.IndirectShadowPrimitives[PrimitiveIndex];
TArray<const FPrimitiveSceneInfo*, SceneRenderingAllocator> ShadowGroupPrimitives;
PrimitiveSceneInfo->GatherLightingAttachmentGroupPrimitives(ShadowGroupPrimitives);
for (int32 ChildIndex = 0; ChildIndex < ShadowGroupPrimitives.Num(); ChildIndex++)
{
const FPrimitiveSceneInfo* GroupPrimitiveSceneInfo = ShadowGroupPrimitives[ChildIndex];
if (GroupPrimitiveSceneInfo->Proxy->CastsDynamicShadow() && GroupPrimitiveSceneInfo->Proxy->HasDistanceFieldRepresentation())
{
bSceneHasInsetDFPrimitives = true;
}
}
}
}
return bSceneHasInsetDFPrimitives && SupportsCapsuleIndirectShadows(FeatureLevel, GShaderPlatformForFeatureLevel[FeatureLevel]) && ViewFamily.EngineShowFlags.CapsuleShadows;
}
BEGIN_SHADER_PARAMETER_STRUCT(FCapsuleShadowsForMovableSkylightParameters, )
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTextures)
RDG_TEXTURE_ACCESS(BentNormalInput, ERHIAccess::SRVCompute)
RDG_TEXTURE_ACCESS(BentNormalOutput, ERHIAccess::UAVCompute)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, BentNormalOutputUAV)
END_SHADER_PARAMETER_STRUCT()
void FDeferredShadingSceneRenderer::RenderCapsuleShadowsForMovableSkylight(
FRDGBuilder& GraphBuilder,
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTexturesUniformBuffer,
FRDGTextureRef& BentNormalOutput) const
{
if (SupportsCapsuleIndirectShadows(FeatureLevel, GShaderPlatformForFeatureLevel[FeatureLevel])
&& ViewFamily.EngineShowFlags.CapsuleShadows)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderCapsuleShadowsSkylight);
bool bAnyViewsUseCapsuleShadows = false;
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.IndirectShadowPrimitives.Num() > 0 && View.ViewState)
{
bAnyViewsUseCapsuleShadows = true;
}
}
if (bAnyViewsUseCapsuleShadows)
{
FRDGTextureRef NewBentNormal = nullptr;
AllocateOrReuseAORenderTarget(GraphBuilder, NewBentNormal, TEXT("CapsuleBentNormal"), PF_FloatRGBA);
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
if (View.IndirectShadowPrimitives.Num() > 0 && View.ViewState)
{
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
RDG_EVENT_SCOPE(GraphBuilder, "IndirectCapsuleShadows");
RDG_GPU_STAT_SCOPE(GraphBuilder, CapsuleShadows);
int32 NumCapsuleShapes = 0;
int32 NumMeshesWithCapsules = 0;
int32 NumMeshDistanceFieldCasters = 0;
FRHIShaderResourceView* IndirectShadowLightDirectionSRV = NULL;
SetupIndirectCapsuleShadows(GraphBuilder, View, NumCapsuleShapes, NumMeshesWithCapsules, NumMeshDistanceFieldCasters, IndirectShadowLightDirectionSRV);
// Don't render indirect occlusion from mesh distance fields when operating on a movable skylight,
// DFAO is responsible for indirect occlusion from meshes with distance fields on a movable skylight.
// A single mesh should only provide indirect occlusion for a given lighting component in one way.
NumMeshDistanceFieldCasters = 0;
if (NumCapsuleShapes > 0 || NumMeshDistanceFieldCasters > 0)
{
check(IndirectShadowLightDirectionSRV);
FIntRect ScissorRect = View.ViewRect;
{
uint32 GroupSizeX = FMath::DivideAndRoundUp(ScissorRect.Size().X / GAODownsampleFactor, GShadowShapeTileSize);
uint32 GroupSizeY = FMath::DivideAndRoundUp(ScissorRect.Size().Y / GAODownsampleFactor, GShadowShapeTileSize);
auto* PassParameters = GraphBuilder.AllocParameters< FCapsuleShadowsForMovableSkylightParameters>();
PassParameters->BentNormalInput = BentNormalOutput;
PassParameters->BentNormalOutput = NewBentNormal;
PassParameters->BentNormalOutputUAV = GraphBuilder.CreateUAV(NewBentNormal);
PassParameters->SceneTextures = SceneTexturesUniformBuffer;
EIndirectShadowingPrimitiveTypes PrimitiveTypes;
if (NumCapsuleShapes > 0 && NumMeshDistanceFieldCasters > 0)
{
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::CapsuleShapesAndMeshDistanceFields;
}
else if (NumCapsuleShapes > 0)
{
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::CapsuleShapes;
}
else
{
check(NumMeshDistanceFieldCasters > 0);
PrimitiveTypes = EIndirectShadowingPrimitiveTypes::MeshDistanceFields;
}
FCapsuleShadowingCS::FPermutationDomain PermutationVector;
PermutationVector.Set<FCapsuleShadowingCS::FShapeShadow>(ECapsuleShadowingType::MovableSkylightTiledCulling);
PermutationVector.Set<FCapsuleShadowingCS::FIndirectPrimitiveType>(PrimitiveTypes);
auto ComputeShader = View.ShaderMap->GetShader<FCapsuleShadowingCS>(PermutationVector);
GraphBuilder.AddPass(
RDG_EVENT_NAME("TiledCapsuleShadowing % u capsules among % u meshes", NumCapsuleShapes, NumMeshesWithCapsules),
PassParameters,
ERDGPassFlags::Compute,
[PassParameters, ComputeShader, this, &View, GroupSizeX, GroupSizeY, NumCapsuleShapes, NumMeshDistanceFieldCasters, ScissorRect, IndirectShadowLightDirectionSRV]
(FRHICommandList& RHICmdList)
{
FRHIComputeShader* ShaderRHI = ComputeShader.GetComputeShader();
RHICmdList.SetComputeShader(ShaderRHI);
ComputeShader->SetParameters(
RHICmdList,
Scene,
View,
NULL,
PassParameters->BentNormalOutput->GetRHI(),
PassParameters->BentNormalOutputUAV->GetRHI(),
FIntPoint(GroupSizeX, GroupSizeY),
NULL,
FVector2D(GroupSizeX, GroupSizeY),
GCapsuleMaxIndirectOcclusionDistance,
ScissorRect,
GAODownsampleFactor,
NumCapsuleShapes,
View.ViewState->IndirectShadowCapsuleShapesSRV ? View.ViewState->IndirectShadowCapsuleShapesSRV.GetReference() : NULL,
NumMeshDistanceFieldCasters,
View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV ? View.ViewState->IndirectShadowMeshDistanceFieldCasterIndicesSRV.GetReference() : NULL,
IndirectShadowLightDirectionSRV,
PassParameters->BentNormalInput->GetRHI(),
ECapsuleShadowingType::MovableSkylightTiledCulling);
DispatchComputeShader(RHICmdList, ComputeShader.GetShader(), GroupSizeX, GroupSizeY, 1);
ComputeShader->UnsetParameters(RHICmdList, nullptr);
});
}
// Replace the pipeline output with our output that has capsule shadows applied
BentNormalOutput = NewBentNormal;
}
}
}
}
}
}