You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
1491 lines
65 KiB
C++
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|