Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapProjection.cpp

438 lines
18 KiB
C++
Raw Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
VirtualShadowMapProjection.cpp
=============================================================================*/
#include "VirtualShadowMapProjection.h"
#include "CoreMinimal.h"
#include "Stats/Stats.h"
#include "RHI.h"
#include "RenderResource.h"
#include "RendererInterface.h"
#include "Shader.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "RHIStaticStates.h"
#include "LightSceneInfo.h"
#include "GlobalShader.h"
#include "SceneRenderTargetParameters.h"
#include "ShadowRendering.h"
#include "DeferredShadingRenderer.h"
#include "PixelShaderUtils.h"
#include "ShadowRendering.h"
#include "SceneRendering.h"
#include "VirtualShadowMapClipmap.h"
#include "HairStrands/HairStrandsData.h"
static TAutoConsoleVariable<float> CVarContactShadowLength(
TEXT( "r.Shadow.Virtual.ContactShadowLength" ),
0.02f,
TEXT( "Length of the screen space contact shadow trace (smart shadow bias) before the virtual shadow map lookup." ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarNormalBias(
TEXT( "r.Shadow.Virtual.NormalBias" ),
0.5f,
TEXT( "Receiver offset along surface normal for shadow lookup. Scaled by distance to camera." )
TEXT( "Higher values avoid artifacts on surfaces nearly parallel to the light, but also visibility offset shadows and increase the chance of hitting unmapped pages." ),
ECVF_RenderThreadSafe
);
TAutoConsoleVariable<int32> CVarVirtualShadowOnePassProjection(
TEXT("r.Shadow.Virtual.OnePassProjection"),
0,
TEXT("Single pass projects all local VSMs culled with the light grid. Used in conjunction with clustered deferred shading."),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSMRTRayCountLocal(
TEXT( "r.Shadow.Virtual.SMRT.RayCountLocal" ),
7,
TEXT( "Ray count for shadow map tracing of local lights. 0 = disabled." ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSMRTSamplesPerRayLocal(
TEXT( "r.Shadow.Virtual.SMRT.SamplesPerRayLocal" ),
8,
TEXT( "Shadow map samples per ray for local lights" ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarSMRTMaxRayAngleFromLight(
TEXT( "r.Shadow.Virtual.SMRT.MaxRayAngleFromLight" ),
0.03f,
TEXT( "Max angle (in radians) a ray is allowed to span from the light's perspective for local lights." )
TEXT( "Smaller angles limit the screen space size of shadow penumbra. " )
TEXT( "Larger angles lead to more noise. " ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSMRTRayCountDirectional(
TEXT( "r.Shadow.Virtual.SMRT.RayCountDirectional" ),
7,
TEXT( "Ray count for shadow map tracing of directional lights. 0 = disabled." ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSMRTSamplesPerRayDirectional(
TEXT( "r.Shadow.Virtual.SMRT.SamplesPerRayDirectional" ),
8,
TEXT( "Shadow map samples per ray for directional lights" ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<float> CVarSMRTRayLengthScaleDirectional(
TEXT( "r.Shadow.Virtual.SMRT.RayLengthScaleDirectional" ),
1.5f,
TEXT( "Length of ray to shoot for directional lights, scaled by distance to camera." )
TEXT( "Shorter rays limit the screen space size of shadow penumbra. " )
TEXT( "Longer rays require more samples to avoid shadows disconnecting from contact points. " ),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarSMRTAdaptiveRayCount(
TEXT( "r.Shadow.Virtual.SMRT.AdaptiveRayCount" ),
1,
TEXT( "Shoot fewer rays in fully shadowed and unshadowed regions. Currently only supported with OnePassProjection. " ),
ECVF_RenderThreadSafe
);
// Composite denoised shadow projection mask onto the light's shadow mask
// Basically just a copy shader with a special blend mode
class FVirtualShadowMapProjectionCompositePS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FVirtualShadowMapProjectionCompositePS);
SHADER_USE_PARAMETER_STRUCT(FVirtualShadowMapProjectionCompositePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE(Texture2D<float4>, InputShadowFactor)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return DoesPlatformSupportNanite(Parameters.Platform);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
// Required right now due to where the shader function lives, but not actually used
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
}
};
IMPLEMENT_GLOBAL_SHADER(FVirtualShadowMapProjectionCompositePS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapProjection.usf", "VirtualShadowMapCompositePS", SF_Pixel);
void CompositeVirtualShadowMapMask(
FRDGBuilder& GraphBuilder,
const FIntRect ScissorRect,
const FRDGTextureRef Input,
FRDGTextureRef OutputShadowMaskTexture)
{
auto ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
FVirtualShadowMapProjectionCompositePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FVirtualShadowMapProjectionCompositePS::FParameters>();
PassParameters->InputShadowFactor = Input;
PassParameters->RenderTargets[0] = FRenderTargetBinding(OutputShadowMaskTexture, ERenderTargetLoadAction::ELoad);
// See FProjectedShadowInfo::GetBlendStateForProjection, but we don't want any of the special cascade behavior, etc. as this is a post-denoised mask.
FRHIBlendState* BlendState = TStaticBlendState<CW_BA, BO_Min, BF_One, BF_One, BO_Min, BF_One, BF_One>::GetRHI();
auto PixelShader = ShaderMap->GetShader<FVirtualShadowMapProjectionCompositePS>();
ValidateShaderParameters(PixelShader, *PassParameters);
FPixelShaderUtils::AddFullscreenPass(GraphBuilder,
ShaderMap,
RDG_EVENT_NAME("MaskComposite"),
PixelShader,
PassParameters,
ScissorRect,
BlendState);
}
class FVirtualShadowMapProjectionCS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FVirtualShadowMapProjectionCS);
SHADER_USE_PARAMETER_STRUCT(FVirtualShadowMapProjectionCS, FGlobalShader)
class FDirectionalLightDim : SHADER_PERMUTATION_BOOL("DIRECTIONAL_LIGHT");
class FSMRTAdaptiveRayCountDim : SHADER_PERMUTATION_BOOL("SMRT_ADAPTIVE_RAY_COUNT");
class FTwoPhysicalTexturesDim : SHADER_PERMUTATION_BOOL("TWO_PHYSICAL_TEXTURES");
class FOnePassProjectionDim : SHADER_PERMUTATION_BOOL("ONE_PASS_PROJECTION");
class FHairStrandsDim : SHADER_PERMUTATION_BOOL("HAS_HAIR_STRANDS");
class FDebugOutputDim : SHADER_PERMUTATION_BOOL("DEBUG_OUTPUT");
using FPermutationDomain = TShaderPermutationDomain<
FDirectionalLightDim,
FOnePassProjectionDim,
FSMRTAdaptiveRayCountDim,
FTwoPhysicalTexturesDim,
FHairStrandsDim,
FDebugOutputDim>;
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FVirtualShadowMapSamplingParameters, SamplingParameters)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FSceneTextureUniformParameters, SceneTexturesStruct)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FHairStrandsViewUniformParameters, HairStrands)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(FVirtualVoxelParameters, HairStrandsVoxel)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER(FIntVector4, ProjectionRect)
SHADER_PARAMETER(float, ContactShadowLength)
SHADER_PARAMETER(float, NormalBias)
SHADER_PARAMETER(uint32, SMRTRayCount)
SHADER_PARAMETER(uint32, SMRTSamplesPerRay)
SHADER_PARAMETER(float, SMRTRayLengthScale)
SHADER_PARAMETER(float, SMRTCotMaxRayAngleFromLight)
SHADER_PARAMETER(uint32, InputType)
// One pass projection parameters
SHADER_PARAMETER_STRUCT_REF(FForwardLightData, ForwardLightData)
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint >, VirtualShadowMapIdRemap) // TODO: Move to VSM UB? Per-view though
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D< uint >, RWShadowMaskBits)
// Pass per light parameters
SHADER_PARAMETER_STRUCT(FLightShaderParameters, Light)
SHADER_PARAMETER(int32, LightUniformVirtualShadowMapId)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWShadowFactor)
// Debug output
SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FPhysicalPageMetaData >, PhysicalPageMetaData)
SHADER_PARAMETER(int32, DebugOutputType)
SHADER_PARAMETER(int32, DebugVirtualShadowMapId)
SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWDebug)
END_SHADER_PARAMETER_STRUCT()
static void ModifyCompilationEnvironment( const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment )
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
FVirtualShadowMapArray::SetShaderDefines(OutEnvironment);
FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);
FPermutationDomain PermutationVector(Parameters.PermutationId);
if (PermutationVector.Get<FSMRTAdaptiveRayCountDim>())
{
OutEnvironment.CompilerFlags.Add(CFLAG_WaveOperations);
}
OutEnvironment.CompilerFlags.Add( CFLAG_Wave32 );
OutEnvironment.CompilerFlags.Add( CFLAG_AllowRealTypes );
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
// Directional lights are always in separate passes as forward light data structure currently
// only contains a single single directional light.
if( PermutationVector.Get< FDirectionalLightDim >() && PermutationVector.Get< FOnePassProjectionDim >() )
{
return false;
}
return DoesPlatformSupportNanite(Parameters.Platform);
}
};
IMPLEMENT_GLOBAL_SHADER(FVirtualShadowMapProjectionCS, "/Engine/Private/VirtualShadowMaps/VirtualShadowMapProjection.usf", "VirtualShadowMapProjection", SF_Compute);
static float GetNormalBiasForShader()
{
return CVarNormalBias.GetValueOnRenderThread() / 1000.0f;
}
extern int32 GVirtualShadowMapAtomicWrites;
static void RenderVirtualShadowMapProjectionCommon(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FViewInfo& View,
FVirtualShadowMapArray& VirtualShadowMapArray,
const FIntRect ProjectionRect,
EVirtualShadowMapProjectionInputType InputType,
FRDGTextureRef OutputTexture,
const FLightSceneProxy* LightProxy = nullptr,
int32 VirtualShadowMapId = INDEX_NONE)
{
// Use hair strands data (i.e., hair voxel tracing) only for Gbuffer input for casting hair shadow onto opaque geometry.
const bool bHasHairStrandsData = HairStrands::HasViewHairStrandsData(View);
const bool bAdaptiveRayCount = GRHISupportsWaveOperations && CVarSMRTAdaptiveRayCount.GetValueOnRenderThread() != 0;
FVirtualShadowMapProjectionCS::FParameters* PassParameters = GraphBuilder.AllocParameters< FVirtualShadowMapProjectionCS::FParameters >();
PassParameters->SamplingParameters = VirtualShadowMapArray.GetSamplingParameters(GraphBuilder);
PassParameters->SceneTexturesStruct = SceneTextures.UniformBuffer;
PassParameters->View = View.ViewUniformBuffer;
PassParameters->ProjectionRect = FIntVector4(ProjectionRect.Min.X, ProjectionRect.Min.Y, ProjectionRect.Max.X, ProjectionRect.Max.Y);
PassParameters->ContactShadowLength = CVarContactShadowLength.GetValueOnRenderThread();
PassParameters->NormalBias = GetNormalBiasForShader();
PassParameters->InputType = uint32(InputType);
if (bHasHairStrandsData)
{
PassParameters->HairStrands = HairStrands::BindHairStrandsViewUniformParameters(View);
PassParameters->HairStrandsVoxel = HairStrands::BindHairStrandsVoxelUniformParameters(View);
}
bool bDirectionalLight = false;
bool bOnePassProjection = LightProxy == nullptr;
if (bOnePassProjection)
{
// One pass projection
PassParameters->ForwardLightData = View.ForwardLightingResources->ForwardLightDataUniformBuffer;
PassParameters->VirtualShadowMapIdRemap = GraphBuilder.CreateSRV( VirtualShadowMapArray.VirtualShadowMapIdRemapRDG[0] ); // FIXME Index proper view
PassParameters->RWShadowMaskBits = GraphBuilder.CreateUAV( OutputTexture );
}
else
{
// Pass per light
bDirectionalLight = LightProxy->GetLightType() == LightType_Directional;
FLightShaderParameters LightParameters;
LightProxy->GetLightShaderParameters(LightParameters);
PassParameters->Light = LightParameters;
PassParameters->LightUniformVirtualShadowMapId = VirtualShadowMapId;
PassParameters->RWShadowFactor = GraphBuilder.CreateUAV( OutputTexture );
}
if (bDirectionalLight)
{
PassParameters->SMRTRayCount = CVarSMRTRayCountDirectional.GetValueOnRenderThread();
PassParameters->SMRTSamplesPerRay = CVarSMRTSamplesPerRayDirectional.GetValueOnRenderThread();
PassParameters->SMRTRayLengthScale = CVarSMRTRayLengthScaleDirectional.GetValueOnRenderThread();
PassParameters->SMRTCotMaxRayAngleFromLight = 0.0f; // unused in this path
}
else
{
PassParameters->SMRTRayCount = CVarSMRTRayCountLocal.GetValueOnRenderThread();
PassParameters->SMRTSamplesPerRay = CVarSMRTSamplesPerRayLocal.GetValueOnRenderThread();
PassParameters->SMRTRayLengthScale = 0.0f; // unused in this path
PassParameters->SMRTCotMaxRayAngleFromLight = 1.0f / FMath::Tan(CVarSMRTMaxRayAngleFromLight.GetValueOnRenderThread());
}
bool bDebugOutput = false;
#if !UE_BUILD_SHIPPING
if ( VirtualShadowMapArray.DebugVisualizationProjectionOutput && InputType == EVirtualShadowMapProjectionInputType::GBuffer )
{
bDebugOutput = true;
PassParameters->DebugOutputType = VirtualShadowMapArray.DebugOutputType;
PassParameters->DebugVirtualShadowMapId = VirtualShadowMapArray.DebugVirtualShadowMapId;
PassParameters->PhysicalPageMetaData = GraphBuilder.CreateSRV( VirtualShadowMapArray.PhysicalPageMetaDataRDG );
PassParameters->RWDebug = GraphBuilder.CreateUAV( VirtualShadowMapArray.DebugVisualizationProjectionOutput );
}
#endif
FVirtualShadowMapProjectionCS::FPermutationDomain PermutationVector;
PermutationVector.Set< FVirtualShadowMapProjectionCS::FDirectionalLightDim >( bDirectionalLight );
PermutationVector.Set< FVirtualShadowMapProjectionCS::FOnePassProjectionDim >( bOnePassProjection );
PermutationVector.Set< FVirtualShadowMapProjectionCS::FSMRTAdaptiveRayCountDim >( bAdaptiveRayCount );
PermutationVector.Set< FVirtualShadowMapProjectionCS::FTwoPhysicalTexturesDim >( GVirtualShadowMapAtomicWrites == 0 );
PermutationVector.Set< FVirtualShadowMapProjectionCS::FHairStrandsDim >( bHasHairStrandsData ? 1 : 0 );
PermutationVector.Set< FVirtualShadowMapProjectionCS::FDebugOutputDim >( bDebugOutput );
auto ComputeShader = View.ShaderMap->GetShader< FVirtualShadowMapProjectionCS >( PermutationVector );
ClearUnusedGraphResources( ComputeShader, PassParameters );
ValidateShaderParameters( ComputeShader, *PassParameters );
const FIntPoint GroupCount = FIntPoint::DivideAndRoundUp( ProjectionRect.Size(), 8 );
FComputeShaderUtils::AddPass(
GraphBuilder,
RDG_EVENT_NAME("VirtualShadowMapProjection(RayCount:%s,Input:%s%s)",
bAdaptiveRayCount ? TEXT("Adaptive") : TEXT("Static"),
(InputType == EVirtualShadowMapProjectionInputType::HairStrands) ? TEXT("HairStrands") : TEXT("GBuffer"),
bDebugOutput ? TEXT(",Debug") : TEXT("")),
ComputeShader,
PassParameters,
FIntVector( GroupCount.X, GroupCount.Y, 1 )
);
}
FRDGTextureRef RenderVirtualShadowMapProjectionOnePass(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FViewInfo& View,
FVirtualShadowMapArray& VirtualShadowMapArray,
EVirtualShadowMapProjectionInputType InputType)
{
FIntRect ProjectionRect = View.ViewRect;
const FRDGTextureDesc ShadowMaskDesc = FRDGTextureDesc::Create2D(
SceneTextures.Config.Extent,
PF_R32_UINT,
FClearValueBinding::None,
TexCreate_ShaderResource | TexCreate_UAV );
FRDGTextureRef ShadowMaskBits = GraphBuilder.CreateTexture( ShadowMaskDesc, InputType == EVirtualShadowMapProjectionInputType::HairStrands ? TEXT("ShadowMaskBits(HairStrands)") : TEXT("ShadowMaskBits(Gbuffer)"));
RenderVirtualShadowMapProjectionCommon(
GraphBuilder,
SceneTextures,
View,
VirtualShadowMapArray,
ProjectionRect,
InputType,
ShadowMaskBits);
return ShadowMaskBits;
}
static FRDGTextureRef CreateShadowFactorTexture(FRDGBuilder& GraphBuilder, FIntPoint Extent)
{
const FLinearColor ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
Extent,
PF_R32_FLOAT,
FClearValueBinding(ClearColor),
TexCreate_ShaderResource | TexCreate_UAV);
FRDGTextureRef Texture = GraphBuilder.CreateTexture(Desc, TEXT("Shadow.Virtual.ShadowFactor"));
AddClearUAVPass(GraphBuilder, GraphBuilder.CreateUAV(Texture), ClearColor);
return Texture;
}
FRDGTextureRef RenderVirtualShadowMapProjection(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FViewInfo& View,
FVirtualShadowMapArray& VirtualShadowMapArray,
const FIntRect ScissorRect,
EVirtualShadowMapProjectionInputType InputType,
FProjectedShadowInfo* ShadowInfo)
{
FRDGTextureRef OutputTexture = CreateShadowFactorTexture(GraphBuilder, SceneTextures.Config.Extent);
RenderVirtualShadowMapProjectionCommon(
GraphBuilder,
SceneTextures,
View,
VirtualShadowMapArray,
ScissorRect,
InputType,
OutputTexture,
ShadowInfo->GetLightSceneInfo().Proxy,
ShadowInfo->VirtualShadowMaps[0]->ID);
return OutputTexture;
}
FRDGTextureRef RenderVirtualShadowMapProjection(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
const FViewInfo& View,
FVirtualShadowMapArray& VirtualShadowMapArray,
const FIntRect ScissorRect,
EVirtualShadowMapProjectionInputType InputType,
const TSharedPtr<FVirtualShadowMapClipmap>& Clipmap)
{
FRDGTextureRef OutputTexture = CreateShadowFactorTexture(GraphBuilder, SceneTextures.Config.Extent);
RenderVirtualShadowMapProjectionCommon(
GraphBuilder,
SceneTextures,
View,
VirtualShadowMapArray,
ScissorRect,
InputType,
OutputTexture,
Clipmap->GetLightSceneInfo().Proxy,
Clipmap->GetVirtualShadowMap()->ID);
return OutputTexture;
}