Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapProjection.cpp
andrew lauritzen 93dd5946b6 Re-implement debug output visualization for VSM projection
Use actor label for light identification/selection in editor; fall back on component name (UUID) temporarily until this data is made available outside the editor as well
Collect debug data in RayState for SMRT and return the last valid sample data
- Alternative is to always generate debug data based on just a single VSM sample even when SMRT is enabled, but it's useful to have a debug output path for the true SMRT data, even if a bit more complex
Move some common visualization utilities to /Visualization.ush
Implement but disable backface culling for shadow evaluation pending change that handles various SHADINGMODEL's properly

#rb graham.wihlidal, ola.olsson

#ROBOMERGE-SOURCE: CL 16407102 in //UE5/Private-Frosty/...
#ROBOMERGE-BOT: STARSHIP (Private-Frosty -> Main) (v804-16311228)

[CL 16412421 by andrew lauritzen in ue5-main branch]
2021-05-20 19:32:20 -04:00

438 lines
18 KiB
C++

// 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;
}