// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= ShadowRendering.h: Shadow rendering definitions. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "HAL/IConsoleManager.h" #include "Templates/RefCounting.h" #include "RHI.h" #include "DataDrivenShaderPlatformInfo.h" #include "LightSceneProxy.h" #include "RenderResource.h" #include "UniformBuffer.h" #include "SceneInterface.h" #include "ShaderParameters.h" #include "Shader.h" #include "HitProxies.h" #include "ConvexVolume.h" #include "RHIStaticStates.h" #include "RendererInterface.h" #include "SceneManagement.h" #include "ScenePrivateBase.h" #include "SceneCore.h" #include "GlobalShader.h" #include "SystemTextures.h" #include "PostProcess/SceneRenderTargets.h" #include "SceneRenderTargetParameters.h" #include "ShaderParameterUtils.h" #include "LightRendering.h" #include "HairStrands/HairStrandsRendering.h" #include "Substrate/Substrate.h" #include "SimpleMeshDrawCommandPass.h" #include "Engine/SubsurfaceProfile.h" class FPrimitiveSceneInfo; class FPrimitiveSceneProxy; class FProjectedShadowInfo; class FScene; class FSceneRenderer; class FViewInfo; class FVirtualShadowMapArrayCacheManager; class FVirtualShadowMapPerLightCacheEntry; class FLightTileIntersectionParameters; class FDistanceFieldCulledObjectBufferParameters; namespace ShadowRendering { // Use Shadow stencil mask is set to 0x07u instead of 0xFF so that that highest bit can be used for Substrate classification without clearing the stencil bit for pre-shadow/per-object static shadow mask constexpr uint32 ShadowStencilMask = 0x07u; }; DECLARE_GPU_DRAWCALL_STAT_NAMED_EXTERN(ShadowDepths, TEXT("Shadow Depths")); /** Renders a cone with a spherical cap, used for rendering spot lights in deferred passes. */ extern void DrawStencilingCone(const FMatrix& ConeToWorld, float ConeAngle, float SphereRadius, const FVector& PreViewTranslation); class FShadowDepthBasePS; /** * Overrides a material used for shadow depth rendering with the default material when appropriate. * Overriding in this manner can reduce state switches and the number of shaders that have to be compiled. * This logic needs to stay in sync with shadow depth shader ShouldCache logic. */ void OverrideWithDefaultMaterialForShadowDepth( const FMaterialRenderProxy*& InOutMaterialRenderProxy, const FMaterial*& InOutMaterialResource, ERHIFeatureLevel::Type InFeatureLevel ); void InitMobileShadowProjectionOutputs(FRHICommandListImmediate& RHICmdList, const FIntPoint& Extent, const bool bRequireMultiView); void ReleaseMobileShadowProjectionOutputs(); enum EShadowDepthRenderMode { /** The render mode used by regular shadows */ ShadowDepthRenderMode_Normal, /** The render mode used when injecting emissive-only objects into the RSM. */ ShadowDepthRenderMode_EmissiveOnly, /** The render mode used when rendering volumes which block global illumination. */ ShadowDepthRenderMode_GIBlockingVolumes, }; enum class EShadowDepthType { None = 0, VSM = 1 << 0, Directional = 1 << 1, Point = 1 << 2, OnePassPoint = 1 << 3 }; ENUM_CLASS_FLAGS(EShadowDepthType); extern EMeshPass::Type GetShadowMeshPassType(EShadowDepthType ShadowDepthType); extern bool UseCachedMeshDrawCommands(EShadowDepthType ShadowDepthType); /** * Used to select what meshes to draw into what shadow infos. Each mesh selects the support it has, and * the shadow info stores a mask of what types it collects. This makes it easy to allow regular SMs to * collect both types if VSMs are disabled. */ enum class EShadowMeshSelection : uint8 { SM = 1U << 0U, VSM = 1U << 1U, // declared if GPU-Scene instace culling is supported. All = SM | VSM, }; ENUM_CLASS_FLAGS(EShadowMeshSelection) class FShadowDepthPassMeshProcessor : public FSceneRenderingAllocatorObject, public FMeshPassProcessor { public: FShadowDepthPassMeshProcessor( const FScene* Scene, const ERHIFeatureLevel::Type InFeatureLevel, const FSceneView* InViewIfDynamicMeshCommand, EShadowDepthType ShadowDepthType, FMeshPassDrawListContext* InDrawListContext); virtual void AddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId = -1) override final; virtual void CollectPSOInitializers(const FSceneTexturesConfig& SceneTexturesConfig, const FMaterial& Material, const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FPSOPrecacheParams& PreCacheParams, TArray& PSOInitializers) override final; FMeshPassProcessorRenderState PassDrawRenderState; private: bool TryAddMeshBatch(const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, int32 StaticMeshId, const FMaterialRenderProxy& MaterialRenderProxy, const FMaterial& Material); bool Process( const FMeshBatch& RESTRICT MeshBatch, uint64 BatchElementMask, int32 StaticMeshId, const FPrimitiveSceneProxy* RESTRICT PrimitiveSceneProxy, const FMaterialRenderProxy& RESTRICT MaterialRenderProxy, const FMaterial& RESTRICT MaterialResource, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode); void CollectPSOInitializersForEachShadowDepthType( const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FMaterial& RESTRICT MaterialResource, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode, bool bCastShadowAsTwoSided, TArray& PSOInitializers); void CollectPSOInitializersForEachStreamSetup( const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FMaterial& RESTRICT MaterialResource, EShadowDepthType InShadowDepthType, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode, bool bRequired, TArray& PSOInitializers); void CollectPSOInitializersInternal( const FPSOPrecacheVertexFactoryData& VertexFactoryData, const FMaterial& RESTRICT MaterialResource, EShadowDepthType InShadowDepthType, ERasterizerFillMode MeshFillMode, ERasterizerCullMode MeshCullMode, bool bSupportsPositionAndNormalOnlyStream, bool bRequired, TArray& PSOInitializers); EShadowDepthType ShadowDepthType; EShadowMeshSelection MeshSelectionMask = EShadowMeshSelection::All; }; enum EShadowDepthCacheMode { SDCM_MovablePrimitivesOnly, SDCM_StaticPrimitivesOnly, SDCM_CSMScrolling, SDCM_Uncached }; inline bool IsShadowCacheModeOcclusionQueryable(EShadowDepthCacheMode CacheMode) { // SDCM_StaticPrimitivesOnly shadowmaps are emitted randomly as the cache needs to be updated, // And therefore not appropriate for occlusion queries which are latent and therefore need to be stable. // Only one the cache modes from ComputeWholeSceneShadowCacheModes should be queryable return CacheMode != SDCM_StaticPrimitivesOnly; } struct FTiledShadowRendering { enum class ETileType : uint8 { Tile16bits, Tile12bits }; FRDGBufferRef DrawIndirectParametersBuffer; FRDGBufferSRVRef TileListDataBufferSRV; uint32 TileSize; ETileType TileType = ETileType::Tile16bits; }; class FShadowMapRenderTargets { public: TArray ColorTargets; IPooledRenderTarget* DepthTarget; FShadowMapRenderTargets() : DepthTarget(nullptr) {} FIntPoint GetSize() const { if (DepthTarget) { return DepthTarget->GetDesc().Extent; } else { check(ColorTargets.Num() > 0); return ColorTargets[0]->GetDesc().Extent; } } void CopyReferencesFromRenderTargets(const FShadowMapRenderTargetsRefCounted& SourceTargets) { int32 ColorTargetsCount = SourceTargets.ColorTargets.Num(); ColorTargets.Empty(ColorTargetsCount); ColorTargets.AddDefaulted(ColorTargetsCount); for (int32 TargetIndex = 0; TargetIndex < ColorTargetsCount; TargetIndex++) { ColorTargets[TargetIndex] = SourceTargets.ColorTargets[TargetIndex].GetReference(); } DepthTarget = SourceTargets.DepthTarget.GetReference(); } }; typedef TFunctionRef FBeginShadowRenderPassFunction; using FPackedNaniteView = Nanite::FPackedView; BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FShadowDepthPassUniformParameters,) SHADER_PARAMETER_STRUCT(FSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER(FMatrix44f, ProjectionMatrix) SHADER_PARAMETER(FMatrix44f, ViewMatrix) SHADER_PARAMETER(FVector4f, ShadowParams) SHADER_PARAMETER(float, bClampToNearPlane) SHADER_PARAMETER_ARRAY(FMatrix44f, ShadowViewProjectionMatrices, [6]) SHADER_PARAMETER_ARRAY(FMatrix44f, ShadowViewMatrices, [6]) SHADER_PARAMETER(int, bRenderToVirtualShadowMap) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, VirtualSmPageTable) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< FPackedNaniteView >, PackedNaniteViews) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint4 >, AllocatedPageRectBounds) SHADER_PARAMETER_RDG_BUFFER_SRV(StructuredBuffer< uint4 >, UncachedPageRectBounds) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2DArray< uint >, OutDepthBufferArray) END_GLOBAL_SHADER_PARAMETER_STRUCT() BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FMobileShadowDepthPassUniformParameters, RENDERER_API) SHADER_PARAMETER_STRUCT(FMobileSceneTextureUniformParameters, SceneTextures) SHADER_PARAMETER(FMatrix44f, ProjectionMatrix) SHADER_PARAMETER(FMatrix44f, ViewMatrix) SHADER_PARAMETER(FVector4f, ShadowParams) SHADER_PARAMETER(float, bClampToNearPlane) SHADER_PARAMETER_ARRAY(FMatrix44f, ShadowViewProjectionMatrices, [6]) END_GLOBAL_SHADER_PARAMETER_STRUCT() /** * Information about a projected shadow. */ class FProjectedShadowInfo : public FRefCountedObject { public: typedef TArray PrimitiveArrayType; /** * The view to be used when rendering this shadow's depths. * WARNING: This view is a bastardization of the 'main view' - i.e., the view used to render the visible geometry (or one of them in the case of multi-view, e.g., split screen) * the only substantial differences are that the _view_ matrix is overridden (using the 'HackOverrideViewMatrixForShadows') * and the 'HackRemoveTemporalAAProjectionJitter' has been called, to presumably remove the AA Jitter. * IT DOES NOT contain the correct shadow projection matrix, PreViewTranslation, or much else that you would expect. * Instead use the GetShadowDepthRenderingViewMatrices() function to get these for rendering shadows. * Main reason for this state of affairs is to facilitate code that depends on the 'main' view matrices for LOD calculations in various places. */ FViewInfo* ShadowDepthView; /** The depth or color targets this shadow was rendered to. */ FShadowMapRenderTargets RenderTargets; EShadowDepthCacheMode CacheMode; /** The main view this shadow must be rendered in, or NULL for a view independent shadow. */ FViewInfo* DependentView; /** Index of the shadow into FVisibleLightInfo::AllProjectedShadows. */ int32 ShadowId; /** A translation that is applied to world-space before transforming by one of the shadow matrices. */ FVector PreShadowTranslation; /** * The view matrix of the shadow, ALSO used as an override to the main view's view matrix when rendering the shadow depth pass. */ FMatrix TranslatedWorldToView; /** View space to clip space. Excluding border area. */ FMatrix ViewToClipInner; /** View space to clip space. Including border area. */ FMatrix ViewToClipOuter; /** * Matrix used for rendering the shadow depth buffer. * Note that this does not necessarily contain all of the shadow casters with CSM, since the vertex shader flattens them onto the near plane of the projection. */ FMatrix44f TranslatedWorldToClipInnerMatrix; FMatrix44f TranslatedWorldToClipOuterMatrix; FMatrix44f InvReceiverInnerMatrix; float InvMaxSubjectDepth; /** * Subject depth extents, in world space units. * These can be used to convert shadow depth buffer values back into world space units. */ float MaxSubjectZ; float MinSubjectZ; /** Frustum containing all potential shadow casters. Including border area. */ FConvexVolume CasterOuterFrustum; /** Frustum containing all shadow receivers. Excluding border area. */ FConvexVolume ReceiverInnerFrustum; float MinPreSubjectZ; FSphere ShadowBounds; FShadowCascadeSettings CascadeSettings; /** * X and Y position of the shadow in the appropriate depth buffer. These are only initialized after the shadow has been allocated. * The actual contents of the shadowmap are at X + BorderSize, Y + BorderSize. */ uint32 X; uint32 Y; /** * Resolution of the shadow, excluding the border. * The full size of the region allocated to this shadow is therefore ResolutionX + 2 * BorderSize, ResolutionY + 2 * BorderSize. */ uint32 ResolutionX; uint32 ResolutionY; /** Size of the border, if any, used to allow filtering without clamping for shadows stored in an atlas. */ uint32 BorderSize; /** Scissor rect size to exclude portion of the CSM slices outside the view frustum */ FIntRect ScissorRectOptim; /** Compute the scissors coords to exclude portion of the CSM slices outside the view frustum*/ void ComputeScissorRectOptim(); /** The largest percent of either the width or height of any view. */ float MaxScreenPercent; /** Fade Alpha per view. */ TArray > FadeAlphas; /** Whether the shadow has been allocated in the shadow depth buffer, and its X and Y properties have been initialized. */ uint32 bAllocated : 1; /** Whether the shadow's projection has been rendered. */ uint32 bRendered : 1; /** Whether the shadow has been allocated in the preshadow cache, so its X and Y properties offset into the preshadow cache depth buffer. */ uint32 bAllocatedInPreshadowCache : 1; /** Whether the shadow is in the preshadow cache and its depths are up to date. */ uint32 bDepthsCached : 1; // redundant to LightSceneInfo->Proxy->GetLightType() == LightType_Directional, could be made ELightComponentType LightType uint32 bDirectionalLight : 1; /** Whether the shadow is a point light shadow that renders all faces of a cubemap in one pass. */ uint32 bOnePassPointLightShadow : 1; /** Whether the shadow is a virtual shadow map. */ uint32 bVSM : 1; /** Whether this shadow affects the whole scene or only a group of objects. */ uint32 bWholeSceneShadow : 1; /** Whether this shadow should support casting shadows from translucent surfaces. */ uint32 bTranslucentShadow : 1; /** Whether the shadow will be computed by ray tracing the distance field. */ uint32 bRayTracedDistanceField : 1; /** Whether this is a per-object shadow that should use capsule shapes to shadow instead of the mesh's triangles. */ uint32 bCapsuleShadow : 1; /** Whether the shadow is a preshadow or not. A preshadow is a per object shadow that handles the static environment casting on a dynamic receiver. */ uint32 bPreShadow : 1; /** To not cast a shadow on the ground outside the object and having higher quality (useful for first person weapon). */ uint32 bSelfShadowOnly : 1; /** Whether the shadow is a per object shadow or not. */ uint32 bPerObjectOpaqueShadow : 1; /** Whether turn on back-lighting transmission. */ uint32 bTransmission : 1; /** Whether turn on hair strands deep shadow. */ uint32 bHairStrandsDeepShadow : 1; /** Whether to render Nanite geometry into this shadow map. */ uint32 bNaniteGeometry : 1; /** Whether the the shadow overlaps any nanite primitives */ uint32 bContainsNaniteSubjects : 1; uint32 bShouldRenderVSM : 1; /** Whether this shadow should support casting shadows from volumetric surfaces. */ uint32 bVolumetricShadow : 1; EShadowMeshSelection MeshSelectionMask = EShadowMeshSelection::All; int32 VirtualShadowMapId = INDEX_NONE; TSharedPtr VirtualShadowMapPerLightCacheEntry; /** View projection matrices for each cubemap face, used by one pass point light shadows. */ TArray OnePassShadowViewProjectionMatrices; /** View matrices for each cubemap face, used by one pass point light shadows. */ TArray OnePassShadowViewMatrices; /** Face projection matrix for cube map shadows. */ FMatrix OnePassShadowFaceProjectionMatrix; /** Controls fading out of per-object shadows in the distance to avoid casting super-sharp shadows far away. */ float PerObjectShadowFadeStart; float InvPerObjectShadowFadeLength; /** The Z offset in shadow light coordinate of the cached shadow bound center and current shadow bound center when scrolling the cached shadow map. */ float CSMScrollingZOffset; FVector4f OverlappedUVOnCachedShadowMap; FVector4f OverlappedUVOnCurrentShadowMap; /** The extra culling planes to cull the static meshes which already in the overlapped area when scrolling the cached shadow map. */ TArray> CSMScrollingExtraCullingPlanes; /** Projection index for light types that use multiple projections for shadows. */ int32 ProjectionIndex = 0; /** Index of the subject primitive for per object shadows. */ int32 SubjectPrimitiveComponentIndex = -1; TArray > ViewIds; TSharedPtr VirtualShadowMapClipmap; public: // default constructor FProjectedShadowInfo(); ~FProjectedShadowInfo(); /** * for a per-object shadow. e.g. translucent particle system or a dynamic object in a precomputed shadow situation * @param InParentSceneInfo must not be 0 * @return success, if false the shadow project is invalid and the projection should nto be created */ bool SetupPerObjectProjection( FLightSceneInfo* InLightSceneInfo, const FPrimitiveSceneInfo* InParentSceneInfo, const FPerObjectProjectedShadowInitializer& Initializer, bool bInPreShadow, uint32 InResolutionX, uint32 MaxShadowResolutionY, uint32 InBorderSize, float InMaxScreenPercent, bool bInTranslucentShadow ); /** for a whole-scene shadow. */ void SetupWholeSceneProjection( FLightSceneInfo* InLightSceneInfo, FViewInfo* InDependentView, const FWholeSceneProjectedShadowInitializer& Initializer, uint32 InResolutionX, uint32 InResolutionY, uint32 InSnapResolutionX, uint32 InSnapResolutionY, uint32 InBorderSize ); /** for a clipmap shadow. */ void SetupClipmapProjection( FLightSceneInfo* InLightSceneInfo, FViewInfo* InDependentView, const TSharedPtr &VirtualShadowMapClipmap, float InMaxNonFarCascadeDistance ); /** * Get `FViewMatrices` instance set up for drawing geometry to the SM, using the outer projection, * @parameter bUseForVSMCubeFaceWorkaround - If true, when get a cube face the OnePassShadowViewMatrices is flipped in Y to undo the * flip used when creating OnePassShadowViewMatrices. This is also done when sampling the VSM. */ FViewMatrices GetShadowDepthRenderingViewMatrices(int32 CubeFaceIndex = -1, bool bUseForVSMCubeFaceWorkaround = false) const; float GetShaderDepthBias() const { return ShaderDepthBias; } float GetShaderSlopeDepthBias() const { return ShaderSlopeDepthBias; } float GetShaderMaxSlopeDepthBias() const { return ShaderMaxSlopeDepthBias; } float GetShaderReceiverDepthBias() const; void SetStateForView(FRHICommandList& RHICmdList, bool bRequiresBorderClamp = false) const; /** Get view rect excluding border area */ FIntRect GetInnerViewRect() const { return { int32(X + BorderSize), int32(Y + BorderSize), int32(X + BorderSize + ResolutionX), int32(Y + BorderSize + ResolutionY), }; } /** Get view rect including border area */ FIntRect GetOuterViewRect() const { return { int32(X), int32(Y), int32(X + 2 * BorderSize + ResolutionX), int32(Y + 2 * BorderSize + ResolutionY), }; } /** Set state for depth rendering */ void SetStateForDepth(FMeshPassProcessorRenderState& DrawRenderState) const; void ClearDepth(FRHICommandList& RHICmdList) const; /** Renders shadow maps for translucent primitives. */ void RenderTranslucencyDepths(FRDGBuilder& GraphBuilder, class FSceneRenderer* SceneRenderer, const FRenderTargetBindingSlots& RenderTargets, FInstanceCullingManager& InstanceCullingManager); static bool HasShadowStencilCulling(FStaticShaderPlatform ShaderPlatform); static FRHIBlendState* GetBlendStateForProjection( int32 ShadowMapChannel, bool bIsWholeSceneDirectionalShadow, bool bUseFadePlane, bool bProjectingForForwardShading, bool bMobileModulatedProjections); FRHIBlendState* GetBlendStateForProjection(bool bProjectingForForwardShading, bool bMobileModulatedProjections) const; /** * Projects the shadow onto the scene for a particular view. */ void RenderProjection( FRDGBuilder& GraphBuilder, const FShadowProjectionPassParameters& CommonPassParameters, int32 ViewIndex, const FViewInfo* View, const FLightSceneProxy* LightSceneProxy, const FSceneRenderer* SceneRender, bool bProjectingForForwardShading, bool bSubPixelShadow) const; void RenderProjectionInternal( FRHICommandList& RHICmdList, int32 ViewIndex, const FViewInfo* View, const FLightSceneProxy* LightSceneProxy, const FSceneRenderer* SceneRender, bool bProjectingForForwardShading, bool bMobileModulatedProjections, const FInstanceCullingDrawParams& InstanceCullingDrawParams, FRHIUniformBuffer* HairStrandsUniformBuffer) const; /** * Projects the mobile modulated shadow onto the scene for a particular view. */ void RenderMobileModulatedShadowProjection( FRHICommandList& RHICmdList, int32 ViewIndex, const FViewInfo* View, const FLightSceneProxy* LightSceneProxy, const FSceneRenderer* SceneRender) const; /** * Renders the shadow subject depth, to a particular hacked view */ void RenderDepth( FRDGBuilder& GraphBuilder, const FSceneRenderer* SceneRenderer, FRDGTextureRef ShadowDepthTexture, bool bDoParallelDispatch, bool bDoCrossGPUCopy); /** Reset cached ray traced distance field shadows texture. */ void ResetRayTracedDistanceFieldShadow(const FViewInfo* View) { DistanceFieldShadowViewGPUData& SDFShadowViewGPUData = CachedDistanceFieldShadowViewGPUData.FindOrAdd(View); SDFShadowViewGPUData.RayTracedShadowsTexture = nullptr; } /** * Render ray traced distance field shadows into a texture. * Output texture is cached per view. This is useful to support async compute. * (ie: kick off distance field shadows early in the frame using async compute, and then combine result into shadow mask texture when necessary) */ FScreenPassTexture RenderRayTracedDistanceFieldProjection( FRDGBuilder& GraphBuilder, bool bAsyncCompute, const FMinimalSceneTextures& SceneTextures, const FViewInfo& View, const FIntRect& ScissorRect); /** * Renders ray traced distance field shadows into an existing texture. * Will use cached results if already calculated earlier in the frame (ie: async compute) */ void RenderRayTracedDistanceFieldProjection( FRDGBuilder& GraphBuilder, const FMinimalSceneTextures& SceneTextures, FRDGTextureRef ScreenShadowMaskTexture, const FViewInfo& View, FIntRect ScissorRect, bool bProjectingForForwardShading, bool bForceRGBModulation = false, FTiledShadowRendering* TiledShadowRendering = nullptr); /** Render one pass point light shadow projections. */ void RenderOnePassPointLightProjection( FRDGBuilder& GraphBuilder, const FShadowProjectionPassParameters& CommonPassParameters, int32 ViewIndex, const FViewInfo& View, const FLightSceneProxy* LightSceneProxy, bool bProjectingForForwardShading, bool bSubPixelShadow) const; /** * Renders the projected shadow's frustum wireframe with the given FPrimitiveDrawInterface. */ void RenderFrustumWireframe(FPrimitiveDrawInterface* PDI) const; /** * Adds a primitive to the shadow's subject list. */ bool AddSubjectPrimitive(FPrimitiveSceneInfo* PrimitiveSceneInfo, TArrayView ViewArray, bool bRecordShadowSubjectForMobileShading, bool bInvertViewShadowRelevance = false); uint64 AddSubjectPrimitive_AnyThread( const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact, TArrayView ViewArray, ERHIFeatureLevel::Type FeatureLevel, struct FAddSubjectPrimitiveStats& OutStats, struct FAddSubjectPrimitiveOverflowedIndices& OverflowBuffer) const; void PresizeSubjectPrimitiveArrays(struct FAddSubjectPrimitiveStats const& Stats); void FinalizeAddSubjectPrimitive( FDynamicShadowsTaskData& TaskData, struct FAddSubjectPrimitiveOp const& Op, TArrayView ViewArray, struct FFinalizeAddSubjectPrimitiveContext& Context); /** * @return TRUE if this shadow info has any casting subject prims to render */ bool HasSubjectPrims() const; /** * Adds a primitive to the shadow's receiver list. */ void AddReceiverPrimitive(FPrimitiveSceneInfo* PrimitiveSceneInfo); enum class EGatherDynamicMeshElementsPass : uint8 { // Processes all operations in a single pass. All, // Parallel pass runs first and processes elements in parallel with other shadows. Parallel, // Serial pass runs second and processes elements serially other shadows. Serial, }; /** Gathers dynamic mesh elements for all the shadow's primitives arrays. */ bool GatherDynamicMeshElements(FMeshElementCollector& MeshCollector, FSceneRenderer& Renderer, class FVisibleLightInfo& VisibleLightInfo, TArray& ReusedViewsArray, EGatherDynamicMeshElementsPass Pass); void SetupMeshDrawCommandsForShadowDepth(FSceneRenderer& Renderer, FInstanceCullingManager& InstanceCullingManager); void SetupMeshDrawCommandsForProjectionStenciling(FSceneRenderer& Renderer, FInstanceCullingManager& InstanceCullingManager); /** * @param View view to check visibility in * @return true if this shadow info has any subject prims visible in the view */ bool SubjectsVisible(const FViewInfo& View) const; /** Clears arrays allocated with the scene rendering allocator. */ void ClearTransientArrays(); /** Hash function. */ friend uint32 GetTypeHash(const FProjectedShadowInfo* ProjectedShadowInfo) { return PointerHash(ProjectedShadowInfo); } /** Returns a matrix that transforms a screen space position into shadow space. */ FMatrix GetScreenToShadowMatrix(const FSceneView& View) const { return GetScreenToShadowMatrix(View, X, Y, ResolutionX, ResolutionY); } FVector4f GetClipToShadowBufferUvScaleBias() const; /** Returns a matrix that transforms a screen space position into shadow space. Additional parameters allow overriding of shadow's tile location. Used with modulated shadows to reduce precision problems when calculating ScreenToShadow in pixel shader. */ FMatrix GetScreenToShadowMatrix(const FSceneView& View, uint32 TileOffsetX, uint32 TileOffsetY, uint32 TileResolutionX, uint32 TileResolutionY) const; /** Returns a matrix that transforms a world space position into shadow space. */ FMatrix GetWorldToShadowMatrix(FVector4f& ShadowmapMinMax, const FIntPoint* ShadowBufferResolutionOverride = nullptr) const; /** Returns the resolution of the shadow buffer used for this shadow, based on the shadow's type. */ FIntPoint GetShadowBufferResolution() const { if (HasVirtualShadowMap()) { return FIntPoint(FVirtualShadowMap::VirtualMaxResolutionXY, FVirtualShadowMap::VirtualMaxResolutionXY); } return RenderTargets.GetSize(); } /** Computes and updates ShaderDepthBias and ShaderSlopeDepthBias */ void UpdateShaderDepthBias(); /** How large the soft PCF comparison should be, similar to DepthBias, before this was called TransitionScale and 1/Size */ float ComputeTransitionSize() const; bool IsWholeSceneDirectionalShadow() const; bool IsWholeScenePointLightShadow() const; bool ShouldClampToNearPlane() const { return IsWholeSceneDirectionalShadow() || (bPreShadow && bDirectionalLight); } // 0 if Setup...() wasn't called yet FLightSceneInfo& GetLightSceneInfo() const { return *LightSceneInfo; } const FLightSceneInfoCompact& GetLightSceneInfoCompact() const { return LightSceneInfoCompact; } /** * Parent primitive of the shadow group that created this shadow, if not a bWholeSceneShadow. * 0 if Setup...() wasn't called yet */ const FPrimitiveSceneInfo* GetParentSceneInfo() const { return ParentSceneInfo; } /** Creates a new view from the pool and caches it in ShadowDepthView for depth rendering. */ void SetupShadowDepthView(FSceneRenderer* SceneRenderer); EShadowDepthType GetShadowDepthType() const { EShadowDepthType ShadowDepthType = bVSM ? EShadowDepthType::VSM : EShadowDepthType::None; if (bDirectionalLight) { ShadowDepthType |= EShadowDepthType::Directional; } else if (bOnePassPointLightShadow) { ShadowDepthType |= EShadowDepthType::OnePassPoint; } else { ShadowDepthType |= EShadowDepthType::Point; } return ShadowDepthType; } EMeshPass::Type GetTargetMeshPassType() const { return ::GetShadowMeshPassType(GetShadowDepthType()); } bool HasVirtualShadowMap() const { return VirtualShadowMapId != INDEX_NONE || VirtualShadowMapClipmap.IsValid(); } FParallelMeshDrawCommandPass& GetShadowDepthPass() { return ShadowDepthPass; } float GetMaxNonFarCascadeDistance() const { return MaxNonFarCascadeDistance; } void BuildRenderingCommands( FRDGBuilder& GraphBuilder, FGPUScene& GPUScene, FInstanceCullingDrawParams& InstanceCullingDrawParams) { ShadowDepthView->DynamicPrimitiveCollector.Commit(); return ShadowDepthPass.BuildRenderingCommands(GraphBuilder, GPUScene, InstanceCullingDrawParams); } /** Check if we need to set the scissor rect to exclude portion of the CSM slices outside the view frustum */ bool ShouldUseCSMScissorOptim() const; const TArray& GetDynamicSubjectHeterogeneousVolumeMeshElements() const { return DynamicSubjectHeterogeneousVolumeMeshElements; } private: // 0 if Setup...() wasn't called yet FLightSceneInfo* LightSceneInfo; FLightSceneInfoCompact LightSceneInfoCompact; /** * Parent primitive of the shadow group that created this shadow, if not a bWholeSceneShadow. * 0 if Setup...() wasn't called yet or for whole scene shadows */ const FPrimitiveSceneInfo* ParentSceneInfo; /** dynamic shadow casting elements */ PrimitiveArrayType DynamicSubjectPrimitives; /** For preshadows, this contains the receiver primitives to mask the projection to. */ PrimitiveArrayType ReceiverPrimitives; /** Subject primitives with translucent relevance. */ PrimitiveArrayType SubjectTranslucentPrimitives; /** Subject primitives for heterogeneous volume shadows. */ PrimitiveArrayType SubjectHeterogeneousVolumePrimitives; /** Dynamic mesh elements for subject primitives. */ TArray DynamicSubjectMeshElements; /** Dynamic mesh elements for translucent subject primitives. */ TArray DynamicSubjectTranslucentMeshElements; /** Dynamic mesh elements for heterogeneous volume primitives. */ TArray DynamicSubjectHeterogeneousVolumeMeshElements; TArray SubjectMeshCommandBuildRequests; TArray SubjectMeshCommandBuildFlags; /** Number of elements of DynamicSubjectMeshElements meshes. */ int32 NumDynamicSubjectMeshElements; /** Number of elements of SubjectMeshCommandBuildRequests meshes. */ int32 NumSubjectMeshCommandBuildRequestElements; FMeshCommandOneFrameArray ShadowDepthPassVisibleCommands; FParallelMeshDrawCommandPass ShadowDepthPass; TArray> ProjectionStencilingPasses; FDynamicMeshDrawCommandStorage DynamicMeshDrawCommandStorage; FGraphicsMinimalPipelineStateSet GraphicsMinimalPipelineStateSet; bool NeedsShaderInitialisation; // If >= 0.0f then this records the cascade distance to the farthest non-'far' shadow cascade. Only used for clipmaps at the moment. float MaxNonFarCascadeDistance = -1.0f; /** * Bias during in shadowmap rendering, stored redundantly for better performance * Set by UpdateShaderDepthBias(), get with GetShaderDepthBias(), -1 if not set */ float ShaderDepthBias; float ShaderSlopeDepthBias; float ShaderMaxSlopeDepthBias; /** Cached light tile intersection parameters in case we needed to trace a distance field multiple times for a view but for different depth buffer*/ struct DistanceFieldShadowViewGPUData { /** Ray traced DF shadow intermediate output. Populated by BeginRenderRayTracedDistanceFieldProjection and consumed by RenderRayTracedDistanceFieldProjection. */ FRDGTextureRef RayTracedShadowsTexture = nullptr; FLightTileIntersectionParameters* SDFLightTileIntersectionParameters = nullptr; FDistanceFieldCulledObjectBufferParameters* SDFCulledObjectBufferParameters = nullptr; FLightTileIntersectionParameters* HeightFieldLightTileIntersectionParameters = nullptr; FDistanceFieldCulledObjectBufferParameters* HeightFieldCulledObjectBufferParameters = nullptr; }; TMap CachedDistanceFieldShadowViewGPUData; void CopyCachedShadowMap( FRDGBuilder& GraphBuilder, const FViewInfo& View, const FSceneRenderer* SceneRenderer, const FRenderTargetBindingSlots& RenderTargets, const FMeshPassProcessorRenderState& DrawRenderState); float GetLODDistanceFactor() const; /** * Modifies the passed in view for this shadow */ void ModifyViewForShadow(FViewInfo* FoundView) const; friend class FVirtualShadowMapArray; void BeginRenderView(FRDGBuilder& GraphBuilder, FScene* Scene); /** * Finds a relevant view for a shadow */ FViewInfo* FindViewForShadow(FSceneRenderer* SceneRenderer) const; void AddCachedMeshDrawCommandsForPass( const FMeshDrawCommandPrimitiveIdInfo &PrimitiveIdInfo, const FPrimitiveSceneInfo* InPrimitiveSceneInfo, const FStaticMeshBatchRelevance& RESTRICT StaticMeshRelevance, const FStaticMeshBatch& StaticMesh, EMeshDrawCommandCullingPayloadFlags CullingPayloadFlags, const FScene* Scene, EMeshPass::Type PassType, FMeshCommandOneFrameArray& VisibleMeshCommands, TArray& MeshCommandBuildRequests, TArray MeshCommandBuildFlags, int32& NumMeshCommandBuildRequestElements); void AddCachedMeshDrawCommands_AnyThread( const FScene* Scene, const FStaticMeshBatchRelevance& RESTRICT StaticMeshRelevance, int32 StaticMeshIdx, int32& NumAcceptedStaticMeshes, struct FAddSubjectPrimitiveResult& OutResult, struct FAddSubjectPrimitiveStats& OutStats, struct FAddSubjectPrimitiveOverflowedIndices& OverflowBuffer) const; /* * Helper to calculate the LOD such that we do not repeat this in threaded & non threaded versions * NOTE: Reads and modifies the CurrentView.PrimitivesLODMask for the given primitive. * Storing it is an optimization for the case where a primitive is not visible in the main view but in several shadow views (e.g., cascades). * This also creates a potential data race if we ever process multiple shadows infos for the same view and primitive in parallel (e.g., cascades). */ FORCEINLINE_DEBUGGABLE FLODMask CalcAndUpdateLODToRender(FViewInfo& CurrentView, const FBoxSphereBounds& Bounds, const FPrimitiveSceneInfo* PrimitiveSceneInfo, int32 ForcedLOD) const; /** * Check relevance flags and shadow mesh pass filter, returns true if the mesh is to be added. * @param bOutDrawingStaticMeshes - set to true if the relevancy checks pass, even if the mesh was discarded using the pass filter check. Otherwise the mesh is treated as dynamic. */ FORCEINLINE bool ShouldDrawStaticMesh(const FStaticMeshBatchRelevance& StaticMeshRelevance, const FLODMask &ShadowLODToRender, bool& bOutDrawingStaticMeshes) const; /** Will return if we should draw the static mesh for the shadow, and will perform lazy init of primitive if it wasn't visible */ bool ShouldDrawStaticMeshes(FViewInfo& InCurrentView, FPrimitiveSceneInfo* InPrimitiveSceneInfo); bool ShouldDrawStaticMeshes_AnyThread( FViewInfo& InCurrentView, const FPrimitiveSceneInfoCompact& PrimitiveSceneInfoCompact, bool bMayBeFading, struct FAddSubjectPrimitiveResult& OutResult, struct FAddSubjectPrimitiveStats& OutStats, struct FAddSubjectPrimitiveOverflowedIndices& OverflowBuffer) const; void GetShadowTypeNameForDrawEvent(FString& TypeName) const; /** Updates object buffers needed by ray traced distance field shadows. */ int32 UpdateShadowCastingObjectBuffers() const; /** Gathers dynamic mesh elements for the given primitive array. */ bool GatherDynamicMeshElementsArray( FMeshElementCollector& Collector, const PrimitiveArrayType& PrimitiveArray, const TArray& Views, const FSceneViewFamily& ViewFamily, TArray& OutDynamicMeshElements, int32& OutNumDynamicSubjectMeshElements, EGatherDynamicMeshElementsPass Pass); bool GatherDynamicHeterogeneousVolumeMeshElementsArray( FSceneRenderer& Renderer, FMeshElementCollector& Collector, const PrimitiveArrayType& PrimitiveArray, const TArray& Views, const FSceneViewFamily& ViewFamily, TArray& OutDynamicMeshElements, int32& OutNumDynamicSubjectMeshElements, EGatherDynamicMeshElementsPass Pass); void SetupFrustumForProjection(const FViewInfo* View, TArray>& OutFrustumVertices, bool& bOutCameraInsideShadowFrustum, FPlane* OutPlanes) const; void SetupProjectionStencilMask( FRHICommandList& RHICmdList, const FViewInfo* View, int32 ViewIndex, const class FSceneRenderer* SceneRender, const TArray>& FrustumVertices, bool bMobileModulatedProjections, bool bCameraInsideShadowFrustum, const FInstanceCullingDrawParams& InstanceCullingDrawParams) const; void SetupProjectionStencilMaskForHair(FRHICommandList& RHICmdList, const FViewInfo* View) const; FORCEINLINE bool TestPrimitiveFarCascadeConditions(bool bPrimitiveCastsFarShadow, const FBoxSphereBounds& Bounds) const; friend class FShadowDepthVS; friend class FShadowDepthBasePS; friend class FShadowVolumeBoundProjectionVS; friend class FShadowProjectionPS; }; enum class EShadowProjectionVertexShaderFlags { None, DrawingFrustum = 0x01 }; ENUM_CLASS_FLAGS(EShadowProjectionVertexShaderFlags) /** * A vertex shader for projecting a shadow depth buffer onto the scene. */ class FShadowVolumeBoundProjectionVS : public FGlobalShader { DECLARE_SHADER_TYPE(FShadowVolumeBoundProjectionVS,Global); public: FShadowVolumeBoundProjectionVS() {} FShadowVolumeBoundProjectionVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { const FShaderParameterMap& ParameterMap = Initializer.ParameterMap; StencilingGeometryParameters.Bind(ParameterMap); InvReceiverInnerMatrix.Bind(ParameterMap, TEXT("InvReceiverInnerMatrix")); PreShadowToPreViewTranslation.Bind(ParameterMap, TEXT("PreShadowToPreViewTranslation")); } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, EShadowProjectionVertexShaderFlags Flags); private: LAYOUT_FIELD(FStencilingGeometryShaderParameters, StencilingGeometryParameters); LAYOUT_FIELD(FShaderParameter, InvReceiverInnerMatrix); LAYOUT_FIELD(FShaderParameter, PreShadowToPreViewTranslation); }; class FShadowProjectionNoTransformVS : public FGlobalShader { DECLARE_SHADER_TYPE(FShadowProjectionNoTransformVS,Global); public: FShadowProjectionNoTransformVS() {} FShadowProjectionNoTransformVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { } void SetParameters(FRHIBatchedShaderParameters& BatchedParameters, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, EShadowProjectionVertexShaderFlags Flags) {} }; /** * FShadowProjectionPixelShaderInterface - used to handle templated versions */ class FShadowProjectionPixelShaderInterface : public FGlobalShader { DECLARE_TYPE_LAYOUT(FShadowProjectionPixelShaderInterface, NonVirtual); public: FShadowProjectionPixelShaderInterface() : FGlobalShader() {} /** * Constructor - binds all shader params and initializes the sample offsets * @param Initializer - init data from shader compiler */ FShadowProjectionPixelShaderInterface(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) {} }; /** Shadow projection parameters used by multiple shaders. */ class FShadowProjectionShaderParameters { DECLARE_TYPE_LAYOUT(FShadowProjectionShaderParameters, NonVirtual); public: void Bind(const FShader::CompiledShaderInitializerType& Initializer) { const FShaderParameterMap& ParameterMap = Initializer.ParameterMap; ScreenToShadowMatrix.Bind(ParameterMap,TEXT("ScreenToShadowMatrix")); SoftTransitionScale.Bind(ParameterMap,TEXT("SoftTransitionScale")); ShadowBufferSize.Bind(ParameterMap,TEXT("ShadowBufferSize")); ShadowDepthTexture.Bind(ParameterMap,TEXT("ShadowDepthTexture")); ShadowDepthTextureSampler.Bind(ParameterMap,TEXT("ShadowDepthTextureSampler")); ProjectionDepthBias.Bind(ParameterMap,TEXT("ProjectionDepthBiasParameters")); FadePlaneOffset.Bind(ParameterMap,TEXT("FadePlaneOffset")); InvFadePlaneLength.Bind(ParameterMap,TEXT("InvFadePlaneLength")); ShadowTileOffsetAndSizeParam.Bind(ParameterMap, TEXT("ShadowTileOffsetAndSize")); LightPositionOrDirection.Bind(ParameterMap, TEXT("LightPositionOrDirection")); PerObjectShadowFadeStart.Bind(ParameterMap, TEXT("PerObjectShadowFadeStart")); InvPerObjectShadowFadeLength.Bind(ParameterMap, TEXT("InvPerObjectShadowFadeLength")); ShadowNearAndFarDepth.Bind(ParameterMap, TEXT("ShadowNearAndFarDepth")); bCascadeUseFadePlane.Bind(ParameterMap, TEXT("bCascadeUseFadePlane")); } void Set(FRHIBatchedShaderParameters& BatchedParameters, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bModulatedShadows, bool bUseFadePlane, bool SubPixelShadow) { const FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation(); const FIntPoint ShadowBufferResolution = ShadowInfo->GetShadowBufferResolution(); if (ShadowTileOffsetAndSizeParam.IsBound()) { FVector2D InverseShadowBufferResolution(1.0f / ShadowBufferResolution.X, 1.0f / ShadowBufferResolution.Y); FVector4f ShadowTileOffsetAndSize( (ShadowInfo->BorderSize + ShadowInfo->X) * InverseShadowBufferResolution.X, (ShadowInfo->BorderSize + ShadowInfo->Y) * InverseShadowBufferResolution.Y, ShadowInfo->ResolutionX * InverseShadowBufferResolution.X, ShadowInfo->ResolutionY * InverseShadowBufferResolution.Y); SetShaderValue(BatchedParameters, ShadowTileOffsetAndSizeParam, ShadowTileOffsetAndSize); } // Set the transform from screen coordinates to shadow depth texture coordinates. if (bModulatedShadows) { // UE-29083 : work around precision issues with ScreenToShadowMatrix on low end devices. const FMatrix44f ScreenToShadow = FMatrix44f(ShadowInfo->GetScreenToShadowMatrix(View, 0, 0, ShadowBufferResolution.X, ShadowBufferResolution.Y)); // LWC_TODO: Precision loss SetShaderValue(BatchedParameters, ScreenToShadowMatrix, ScreenToShadow); } else { const FMatrix44f ScreenToShadow = FMatrix44f(ShadowInfo->GetScreenToShadowMatrix(View)); // LWC_TODO: Precision loss SetShaderValue(BatchedParameters, ScreenToShadowMatrix, ScreenToShadow); } if (SoftTransitionScale.IsBound()) { const float TransitionSize = ShadowInfo->ComputeTransitionSize(); SetShaderValue(BatchedParameters, SoftTransitionScale, FVector3f(0, 0, 1.0f / TransitionSize)); } if (ShadowBufferSize.IsBound()) { FVector2D ShadowBufferSizeValue(ShadowBufferResolution.X, ShadowBufferResolution.Y); SetShaderValue(BatchedParameters, ShadowBufferSize, FVector4f(ShadowBufferSizeValue.X, ShadowBufferSizeValue.Y, 1.0f / ShadowBufferSizeValue.X, 1.0f / ShadowBufferSizeValue.Y)); } FRHITexture* ShadowDepthTextureValue; // Translucency shadow projection has no depth target if (ShadowInfo->RenderTargets.DepthTarget) { ShadowDepthTextureValue = ShadowInfo->RenderTargets.DepthTarget->GetRHI(); } else { ShadowDepthTextureValue = GSystemTextures.BlackDummy->GetRHI(); } FRHISamplerState* DepthSamplerState = TStaticSamplerState::GetRHI(); SetTextureParameter(BatchedParameters, ShadowDepthTexture, ShadowDepthTextureSampler, DepthSamplerState, ShadowDepthTextureValue); if (ShadowDepthTextureSampler.IsBound()) { SetSamplerParameter(BatchedParameters, ShadowDepthTextureSampler, DepthSamplerState); } SetShaderValue(BatchedParameters, ProjectionDepthBias, FVector4f(ShadowInfo->GetShaderDepthBias(), ShadowInfo->GetShaderSlopeDepthBias(), ShadowInfo->GetShaderReceiverDepthBias(), ShadowInfo->MaxSubjectZ - ShadowInfo->MinSubjectZ)); SetShaderValue(BatchedParameters, FadePlaneOffset, ShadowInfo->CascadeSettings.FadePlaneOffset); if(InvFadePlaneLength.IsBound() && bUseFadePlane) { check(ShadowInfo->CascadeSettings.FadePlaneLength > 0); SetShaderValue(BatchedParameters, InvFadePlaneLength, 1.0f / ShadowInfo->CascadeSettings.FadePlaneLength); } if (LightPositionOrDirection.IsBound()) { const FVector LightDirection = ShadowInfo->GetLightSceneInfo().Proxy->GetDirection(); const FVector LightPosition = ShadowInfo->GetLightSceneInfo().Proxy->GetPosition() + PreViewTranslation; const bool bIsDirectional = ShadowInfo->GetLightSceneInfo().Proxy->GetLightType() == LightType_Directional; SetShaderValue(BatchedParameters, LightPositionOrDirection, bIsDirectional ? FVector4f((FVector3f)LightDirection,0) : FVector4f((FVector3f)LightPosition,1)); } if (SubPixelShadow) { float DeviceZNear = 1; float DeviceZFar = 0; const bool bIsCascadedShadow = ShadowInfo->bDirectionalLight && !(ShadowInfo->bPerObjectOpaqueShadow || ShadowInfo->bPreShadow); if (bIsCascadedShadow) { FVector4 Near = View.ViewMatrices.GetProjectionMatrix().TransformFVector4(FVector4(0, 0, ShadowInfo->CascadeSettings.SplitNear)); FVector4 Far = View.ViewMatrices.GetProjectionMatrix().TransformFVector4(FVector4(0, 0, ShadowInfo->CascadeSettings.SplitFar)); // LWC_TODO: precision loss? DeviceZNear = (float)(Near.Z / Near.W); DeviceZFar = (float)(Far.Z / Far.W); } FVector2f SliceNearAndFarDepth; SliceNearAndFarDepth.X = DeviceZNear; SliceNearAndFarDepth.Y = DeviceZFar; SetShaderValue(BatchedParameters, ShadowNearAndFarDepth, SliceNearAndFarDepth); SetShaderValue(BatchedParameters, bCascadeUseFadePlane, bUseFadePlane ? 1 : 0); } SetShaderValue(BatchedParameters, PerObjectShadowFadeStart, ShadowInfo->PerObjectShadowFadeStart); SetShaderValue(BatchedParameters, InvPerObjectShadowFadeLength, ShadowInfo->InvPerObjectShadowFadeLength); } private: LAYOUT_FIELD(FShaderParameter, ScreenToShadowMatrix); LAYOUT_FIELD(FShaderParameter, SoftTransitionScale); LAYOUT_FIELD(FShaderParameter, ShadowBufferSize); LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTexture); LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTextureSampler); LAYOUT_FIELD(FShaderParameter, ProjectionDepthBias); LAYOUT_FIELD(FShaderParameter, FadePlaneOffset); LAYOUT_FIELD(FShaderParameter, InvFadePlaneLength); LAYOUT_FIELD(FShaderParameter, ShadowTileOffsetAndSizeParam); LAYOUT_FIELD(FShaderParameter, LightPositionOrDirection); LAYOUT_FIELD(FShaderParameter, PerObjectShadowFadeStart); LAYOUT_FIELD(FShaderParameter, InvPerObjectShadowFadeLength); LAYOUT_FIELD(FShaderParameter, ShadowNearAndFarDepth); LAYOUT_FIELD(FShaderParameter, bCascadeUseFadePlane); }; /** * TShadowProjectionPS * A pixel shader for projecting a shadow depth buffer onto the scene. Used with any light type casting normal shadows. */ template class TShadowProjectionPS : public FShadowProjectionPixelShaderInterface { DECLARE_SHADER_TYPE(TShadowProjectionPS,Global); public: TShadowProjectionPS() : FShadowProjectionPixelShaderInterface() { } /** * Constructor - binds all shader params and initializes the sample offsets * @param Initializer - init data from shader compiler */ TShadowProjectionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FShadowProjectionPixelShaderInterface(Initializer) { ProjectionParameters.Bind(Initializer); ShadowFadeFraction.Bind(Initializer.ParameterMap,TEXT("ShadowFadeFraction")); ShadowSharpen.Bind(Initializer.ParameterMap,TEXT("ShadowSharpen")); LightPosition.Bind(Initializer.ParameterMap, TEXT("LightPositionAndInvRadius")); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) || (bUseTransmission == 0 && SubPixelShadow == 0 && MobileUsesShadowMaskTexture(Parameters.Platform)); } /** * Add any defines required by the shader * @param OutEnvironment - shader environment to modify */ static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FShadowProjectionPixelShaderInterface::ModifyCompilationEnvironment(Parameters,OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADOW_QUALITY"), Quality); OutEnvironment.SetDefine(TEXT("SUBPIXEL_SHADOW"), (uint32)(SubPixelShadow ? 1 : 0)); OutEnvironment.SetDefine(TEXT("USE_FADE_PLANE"), (uint32)(bUseFadePlane ? 1 : 0)); OutEnvironment.SetDefine(TEXT("USE_TRANSMISSION"), (uint32)(bUseTransmission ? 1 : 0)); const bool bMobileForceDepthRead = MobileUsesFullDepthPrepass(Parameters.Platform); OutEnvironment.SetDefine(TEXT("FORCE_DEPTH_TEXTURE_READS"), (uint32)(bMobileForceDepthRead ? 1 : 0)); } /** * Sets the pixel shader's parameters * @param View - current view * @param ShadowInfo - projected shadow info for a single light */ void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bUseLightFunctionAtlas) { const FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation(); const bool bUseFadePlaneEnable = ShadowInfo->CascadeSettings.FadePlaneLength > 0; ProjectionParameters.Set(BatchedParameters, View, ShadowInfo, bModulatedShadows, bUseFadePlaneEnable, SubPixelShadow); const FLightSceneProxy& LightProxy = *(ShadowInfo->GetLightSceneInfo().Proxy); SetShaderValue(BatchedParameters, ShadowFadeFraction, ShadowInfo->FadeAlphas[ViewIndex] ); SetShaderValue(BatchedParameters, ShadowSharpen, LightProxy.GetShadowSharpen() * 7.0f + 1.0f ); SetShaderValue(BatchedParameters, LightPosition, FVector4f(FVector3f((FVector)LightProxy.GetPosition() + PreViewTranslation), 1.0f / LightProxy.GetRadius())); auto DeferredLightParameter = GetUniformBufferParameter(); if (DeferredLightParameter.IsBound()) { SetDeferredLightParameters(BatchedParameters, DeferredLightParameter, &ShadowInfo->GetLightSceneInfo(), View, bUseLightFunctionAtlas); } FScene* Scene = nullptr; if (View.Family->Scene) { Scene = View.Family->Scene->GetRenderScene(); } } protected: LAYOUT_FIELD(FShadowProjectionShaderParameters, ProjectionParameters); LAYOUT_FIELD(FShaderParameter, ShadowFadeFraction); LAYOUT_FIELD(FShaderParameter, ShadowSharpen); LAYOUT_FIELD(FShaderParameter, LightPosition); }; /** Pixel shader to project modulated shadows onto the scene. */ template class TModulatedShadowProjection : public TShadowProjectionPS { DECLARE_SHADER_TYPE(TModulatedShadowProjection, Global); public: static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { TShadowProjectionPS::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("MODULATED_SHADOWS"), 1); const bool bMobileForceDepthRead = MobileUsesFullDepthPrepass(Parameters.Platform); OutEnvironment.SetDefine(TEXT("IS_MOBILE_DEPTHREAD_SUBPASS"), bMobileForceDepthRead ? 0u : 1u); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsMobilePlatform(Parameters.Platform); } TModulatedShadowProjection() {} TModulatedShadowProjection(const ShaderMetaType::CompiledShaderInitializerType& Initializer); void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bUseLightFunctionAtlas) { TShadowProjectionPS::SetParameters(BatchedParameters, ViewIndex, View, ShadowInfo, bUseLightFunctionAtlas); SetShaderValue(BatchedParameters, ModulatedShadowColorParameter, ShadowInfo->GetLightSceneInfo().Proxy->GetModulatedShadowColor()); } protected: LAYOUT_FIELD(FShaderParameter, ModulatedShadowColorParameter); LAYOUT_FIELD(FShaderUniformBufferParameter, MobileBasePassUniformBuffer); }; /** Translucency shadow projection uniform buffer containing data needed for Fourier opacity maps. */ BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FTranslucentSelfShadowUniformParameters, ) SHADER_PARAMETER(FMatrix44f, WorldToShadowMatrix) SHADER_PARAMETER(FVector4f, ShadowUVMinMax) SHADER_PARAMETER(FVector4f, DirectionalLightDirection) SHADER_PARAMETER(FVector4f, DirectionalLightColor) SHADER_PARAMETER_TEXTURE(Texture2D, Transmission0) SHADER_PARAMETER_TEXTURE(Texture2D, Transmission1) SHADER_PARAMETER_SAMPLER(SamplerState, Transmission0Sampler) SHADER_PARAMETER_SAMPLER(SamplerState, Transmission1Sampler) END_GLOBAL_SHADER_PARAMETER_STRUCT() extern void SetupTranslucentSelfShadowUniformParameters(const FProjectedShadowInfo* ShadowInfo, FTranslucentSelfShadowUniformParameters& OutParameters); /** * Default translucent self shadow data. */ class FEmptyTranslucentSelfShadowUniformBuffer : public TUniformBuffer< FTranslucentSelfShadowUniformParameters > { typedef TUniformBuffer< FTranslucentSelfShadowUniformParameters > Super; public: virtual void InitRHI(FRHICommandListBase& RHICmdList) override; }; /** Global uniform buffer containing the default precomputed lighting data. */ extern TGlobalResource< FEmptyTranslucentSelfShadowUniformBuffer > GEmptyTranslucentSelfShadowUniformBuffer; /** Pixel shader to project both opaque and translucent shadows onto opaque surfaces. */ template class TShadowProjectionFromTranslucencyPS : public TShadowProjectionPS { DECLARE_SHADER_TYPE(TShadowProjectionFromTranslucencyPS,Global); public: static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { TShadowProjectionPS::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("APPLY_TRANSLUCENCY_SHADOWS"), 1); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && TShadowProjectionPS::ShouldCompilePermutation(Parameters); } TShadowProjectionFromTranslucencyPS() {} TShadowProjectionFromTranslucencyPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): TShadowProjectionPS(Initializer) { } void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bUseLightFunctionAtlas) { TShadowProjectionPS::SetParameters(BatchedParameters, ViewIndex, View, ShadowInfo, bUseLightFunctionAtlas); FTranslucentSelfShadowUniformParameters TranslucentSelfShadowUniformParameters; SetupTranslucentSelfShadowUniformParameters(ShadowInfo, TranslucentSelfShadowUniformParameters); SetUniformBufferParameterImmediate(BatchedParameters, this->template GetUniformBufferParameter(), TranslucentSelfShadowUniformParameters); } }; BEGIN_SHADER_PARAMETER_STRUCT(FOnePassPointShadowProjection, ) SHADER_PARAMETER_RDG_TEXTURE(TextureCube, ShadowDepthCubeTexture) SHADER_PARAMETER_RDG_TEXTURE(TextureCube, ShadowDepthCubeTexture2) SHADER_PARAMETER_SAMPLER(SamplerComparisonState, ShadowDepthCubeTextureSampler) SHADER_PARAMETER_ARRAY(FMatrix44f, ShadowViewProjectionMatrices, [6]) SHADER_PARAMETER(float, InvShadowmapResolution) END_SHADER_PARAMETER_STRUCT() extern void GetOnePassPointShadowProjectionParameters(FRDGBuilder& GraphBuilder, const FProjectedShadowInfo* ShadowInfo, FOnePassPointShadowProjection& OutParameters); /** One pass point light shadow projection parameters used by multiple shaders. */ class FOnePassPointShadowProjectionShaderParameters { DECLARE_TYPE_LAYOUT(FOnePassPointShadowProjectionShaderParameters, NonVirtual); public: void Bind(const FShaderParameterMap& ParameterMap) { ShadowDepthTexture.Bind(ParameterMap,TEXT("ShadowDepthCubeTexture")); ShadowDepthTexture2.Bind(ParameterMap, TEXT("ShadowDepthCubeTexture2")); ShadowDepthCubeComparisonSampler.Bind(ParameterMap,TEXT("ShadowDepthCubeTextureSampler")); ShadowViewProjectionMatrices.Bind(ParameterMap, TEXT("ShadowViewProjectionMatrices")); InvShadowmapResolution.Bind(ParameterMap, TEXT("InvShadowmapResolution")); LightPositionOrDirection.Bind(ParameterMap, TEXT("LightPositionOrDirection")); } inline void Set(FRHIBatchedShaderParameters& BatchedParameters, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo) const { const FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation(); FRHITexture* ShadowDepthTextureValue = GBlackTextureDepthCube->TextureRHI; if (ShadowInfo) { if (FRHITexture* Texture = ShadowInfo->RenderTargets.DepthTarget->GetRHI()) { ShadowDepthTextureValue = Texture; } } SetTextureParameter(BatchedParameters, ShadowDepthTexture, ShadowDepthTextureValue); SetTextureParameter(BatchedParameters, ShadowDepthTexture2, ShadowDepthTextureValue); if (LightPositionOrDirection.IsBound()) { const FVector LightPosition = ShadowInfo ? FVector(ShadowInfo->GetLightSceneInfo().Proxy->GetPosition()) : FVector::ZeroVector; SetShaderValue(BatchedParameters, LightPositionOrDirection, FVector4f(FVector3f(LightPosition + PreViewTranslation), 1)); } if (ShadowDepthCubeComparisonSampler.IsBound()) { // Use a comparison sampler to do hardware PCF SetSamplerParameter(BatchedParameters, ShadowDepthCubeComparisonSampler, TStaticSamplerState::GetRHI()); } const int32 NumShaderMatrices = FMath::DivideAndRoundUp(ShadowViewProjectionMatrices.GetNumBytes(), sizeof(FMatrix44f)); if (ShadowInfo) { const int32 NumUsableMatrices = FMath::Min(NumShaderMatrices, ShadowInfo->OnePassShadowViewProjectionMatrices.Num()); TArray TypeCastedMatrices; TypeCastedMatrices.AddUninitialized(NumUsableMatrices); for (int32 i = 0; i < NumUsableMatrices; i++) { TypeCastedMatrices[i] = FMatrix44f(ShadowInfo->OnePassShadowViewProjectionMatrices[i]); // LWC_TODO: Precision loss? } if (NumUsableMatrices < NumShaderMatrices) { TypeCastedMatrices.AddZeroed(NumShaderMatrices - NumUsableMatrices); } SetShaderValueArray( BatchedParameters, ShadowViewProjectionMatrices, TypeCastedMatrices.GetData(), NumShaderMatrices ); SetShaderValue(BatchedParameters,InvShadowmapResolution,1.0f / ShadowInfo->ResolutionX); } else { TArray ZeroMatrices; ZeroMatrices.AddZeroed(NumShaderMatrices); SetShaderValueArray( BatchedParameters, ShadowViewProjectionMatrices, ZeroMatrices.GetData(), NumShaderMatrices ); SetShaderValue(BatchedParameters,InvShadowmapResolution,0); } } /** Serializer. */ /*friend FArchive& operator<<(FArchive& Ar,FOnePassPointShadowProjectionShaderParameters& P) { Ar << P.ShadowDepthTexture; Ar << P.ShadowDepthTexture2; Ar << P.ShadowDepthCubeComparisonSampler; Ar << P.ShadowViewProjectionMatrices; Ar << P.InvShadowmapResolution; Ar << P.LightPositionOrDirection; return Ar; }*/ private: LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTexture); LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTexture2); LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthCubeComparisonSampler); LAYOUT_FIELD(FShaderParameter, ShadowViewProjectionMatrices); LAYOUT_FIELD(FShaderParameter, InvShadowmapResolution); LAYOUT_FIELD(FShaderParameter, LightPositionOrDirection); }; /** * Pixel shader used to project one pass point light shadows. */ // Quality = 0 / 1 template class TOnePassPointShadowProjectionPS : public FGlobalShader { DECLARE_SHADER_TYPE(TOnePassPointShadowProjectionPS,Global); public: TOnePassPointShadowProjectionPS() {} TOnePassPointShadowProjectionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { HairStrandsParameters.Bind(Initializer.ParameterMap, FHairStrandsViewUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName()); SubstrateGlobalParameters.Bind(Initializer.ParameterMap, FSubstrateGlobalUniformParameters::FTypeInfo::GetStructMetadata()->GetShaderVariableName()); OnePassShadowParameters.Bind(Initializer.ParameterMap); ShadowDepthTextureSampler.Bind(Initializer.ParameterMap,TEXT("ShadowDepthTextureSampler")); LightPosition.Bind(Initializer.ParameterMap,TEXT("LightPositionAndInvRadius")); ShadowFadeFraction.Bind(Initializer.ParameterMap,TEXT("ShadowFadeFraction")); ShadowSharpen.Bind(Initializer.ParameterMap,TEXT("ShadowSharpen")); PointLightDepthBias.Bind(Initializer.ParameterMap,TEXT("PointLightDepthBias")); PointLightProjParameters.Bind(Initializer.ParameterMap, TEXT("PointLightProjParameters")); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Parameters,OutEnvironment); OutEnvironment.SetDefine(TEXT("SHADOW_QUALITY"), Quality); if (IsMobilePlatform(Parameters.Platform)) { OutEnvironment.SetDefine(TEXT("USE_TRANSMISSION"), 0); } else { OutEnvironment.SetDefine(TEXT("USE_TRANSMISSION"), (uint32)(bUseTransmission ? 1 : 0)); } OutEnvironment.SetDefine(TEXT("SUBPIXEL_SHADOW"), (uint32)(bUseSubPixel ? 1 : 0)); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { if (IsMobilePlatform(Parameters.Platform)) { return !bUseTransmission; } return true; } void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FViewInfo& View, const FProjectedShadowInfo* ShadowInfo, FRHIUniformBuffer* HairStrandsUniformBuffer) { const FVector PreViewTranslation = View.ViewMatrices.GetPreViewTranslation(); OnePassShadowParameters.Set(BatchedParameters, View, ShadowInfo); const FLightSceneProxy& LightProxy = *(ShadowInfo->GetLightSceneInfo().Proxy); SetShaderValue(BatchedParameters, LightPosition, FVector4f(FVector3f(FVector(LightProxy.GetPosition()) + PreViewTranslation), 1.0f / LightProxy.GetRadius())); SetShaderValue(BatchedParameters, ShadowFadeFraction, ShadowInfo->FadeAlphas[ViewIndex]); SetShaderValue(BatchedParameters, ShadowSharpen, LightProxy.GetShadowSharpen() * 7.0f + 1.0f); FVector2d ProjectionParams = FVector2d(ShadowInfo->OnePassShadowFaceProjectionMatrix.M[2][2], ShadowInfo->OnePassShadowFaceProjectionMatrix.M[3][2]); FVector2f InverseProjParams = FVector2f(1.0 / ProjectionParams.Y, ProjectionParams.X / ProjectionParams.Y); SetShaderValue(BatchedParameters, PointLightDepthBias, FVector3f(ShadowInfo->GetShaderDepthBias(), ShadowInfo->GetShaderSlopeDepthBias(), ShadowInfo->GetShaderMaxSlopeDepthBias())); SetShaderValue(BatchedParameters, PointLightProjParameters, InverseProjParams); if (HairStrandsParameters.IsBound()) { SetUniformBufferParameter(BatchedParameters, HairStrandsParameters, HairStrandsUniformBuffer); } if (SubstrateGlobalParameters.IsBound()) { TRDGUniformBufferRef SubstrateUniformBuffer = Substrate::BindSubstrateGlobalUniformParameters(View); SetUniformBufferParameter(BatchedParameters, SubstrateGlobalParameters, SubstrateUniformBuffer->GetRHIRef()); } FScene* Scene = nullptr; if (View.Family->Scene) { Scene = View.Family->Scene->GetRenderScene(); } SetSamplerParameter(BatchedParameters, ShadowDepthTextureSampler, TStaticSamplerState::GetRHI()); auto DeferredLightParameter = GetUniformBufferParameter(); if (DeferredLightParameter.IsBound()) { SetDeferredLightParameters(BatchedParameters, DeferredLightParameter, &ShadowInfo->GetLightSceneInfo(), View, LightFunctionAtlas::IsEnabled(View, LightFunctionAtlas::ELightFunctionAtlasSystem::DeferredLighting)); } } private: LAYOUT_FIELD(FOnePassPointShadowProjectionShaderParameters, OnePassShadowParameters); LAYOUT_FIELD(FShaderResourceParameter, ShadowDepthTextureSampler); LAYOUT_FIELD(FShaderParameter, LightPosition); LAYOUT_FIELD(FShaderParameter, ShadowFadeFraction); LAYOUT_FIELD(FShaderParameter, ShadowSharpen); LAYOUT_FIELD(FShaderParameter, PointLightDepthBias); LAYOUT_FIELD(FShaderParameter, PointLightProjParameters); LAYOUT_FIELD(FShaderUniformBufferParameter, HairStrandsParameters); LAYOUT_FIELD(FShaderUniformBufferParameter, SubstrateGlobalParameters); }; // Reversed Z struct FShadowProjectionMatrix : FMatrix { FShadowProjectionMatrix(FVector::FReal MinZ, FVector::FReal MaxZ, const FVector4& WAxis) : FMatrix( FPlane(1, 0, 0, WAxis.X), FPlane(0, 1, 0, WAxis.Y), FPlane(0, 0, -(WAxis.Z * MinZ + WAxis.W) / (MaxZ - MinZ), WAxis.Z), FPlane(0, 0, MaxZ *(WAxis.Z * MinZ + WAxis.W) / (MaxZ - MinZ), WAxis.W) ) {} // Off center projection FShadowProjectionMatrix( const FVector2D& Min, const FVector2D& Max, const FVector4& WAxis ) : FMatrix( FPlane( 2.0f / (Max.X - Min.X), 0.0f, 0.0f, WAxis.X), FPlane( 0.0f, 2.0f / (Max.Y - Min.Y), 0.0f, WAxis.Y), FPlane( -(Max.X + Min.X) / (Max.X - Min.X), -(Max.Y + Min.Y) / (Max.Y - Min.Y), 0.0f, WAxis.Z), FPlane( 0.0f, 0.0f, 1.0f, WAxis.W) ) {} // Change near and far plane FShadowProjectionMatrix( const FMatrix& InMatrix, float MinZ, float MaxZ ) : FMatrix( InMatrix ) { M[2][2] = -( M[2][3] * MinZ + M[3][3] ) / ( MaxZ - MinZ ); M[3][2] = MaxZ * -M[2][2]; } }; /** Pixel shader to project directional PCSS onto the scene. */ template class TDirectionalPercentageCloserShadowProjectionPS : public TShadowProjectionPS { DECLARE_SHADER_TYPE(TDirectionalPercentageCloserShadowProjectionPS, Global); public: TDirectionalPercentageCloserShadowProjectionPS() {} TDirectionalPercentageCloserShadowProjectionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : TShadowProjectionPS(Initializer) { PCSSParameters.Bind(Initializer.ParameterMap, TEXT("PCSSParameters")); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { TShadowProjectionPS::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_PCSS"), 1); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return TShadowProjectionPS::ShouldCompilePermutation(Parameters) && FDataDrivenShaderPlatformInfo::GetSupportsPercentageCloserShadows(Parameters.Platform); } void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bUseLightFunctionAtlas) { TShadowProjectionPS::SetParameters(BatchedParameters, ViewIndex, View, ShadowInfo, bUseLightFunctionAtlas); // GetLightSourceAngle returns the full angle. float TanLightSourceAngle = FMath::Tan(0.5 * FMath::DegreesToRadians(ShadowInfo->GetLightSceneInfo().Proxy->GetLightSourceAngle())); static IConsoleVariable* CVarMaxSoftShadowKernelSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shadow.MaxSoftKernelSize")); check(CVarMaxSoftShadowKernelSize); int32 MaxKernelSize = CVarMaxSoftShadowKernelSize->GetInt(); float SW = 2.0 * ShadowInfo->ShadowBounds.W; float SZ = ShadowInfo->MaxSubjectZ - ShadowInfo->MinSubjectZ; FVector4f PCSSParameterValues = FVector4f(TanLightSourceAngle * SZ / SW, MaxKernelSize / float(ShadowInfo->ResolutionX), 0, 0); SetShaderValue(BatchedParameters, PCSSParameters, PCSSParameterValues); } protected: LAYOUT_FIELD(FShaderParameter, PCSSParameters); }; /** Pixel shader to project PCSS spot light onto the scene. */ template class TSpotPercentageCloserShadowProjectionPS : public TShadowProjectionPS { DECLARE_SHADER_TYPE(TSpotPercentageCloserShadowProjectionPS, Global); public: TSpotPercentageCloserShadowProjectionPS() {} TSpotPercentageCloserShadowProjectionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : TShadowProjectionPS(Initializer) { PCSSParameters.Bind(Initializer.ParameterMap, TEXT("PCSSParameters")); } static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) && FDataDrivenShaderPlatformInfo::GetSupportsPercentageCloserShadows(Parameters.Platform); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { TShadowProjectionPS::ModifyCompilationEnvironment(Parameters, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_PCSS"), 1); OutEnvironment.SetDefine(TEXT("SPOT_LIGHT_PCSS"), 1); } void SetParameters( FRHIBatchedShaderParameters& BatchedParameters, int32 ViewIndex, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo, bool bUseLightFunctionAtlas) { check(ShadowInfo->GetLightSceneInfo().Proxy->GetLightType() == LightType_Spot); TShadowProjectionPS::SetParameters(BatchedParameters, ViewIndex, View, ShadowInfo, bUseLightFunctionAtlas); static IConsoleVariable* CVarMaxSoftShadowKernelSize = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Shadow.MaxSoftKernelSize")); check(CVarMaxSoftShadowKernelSize); int32 MaxKernelSize = CVarMaxSoftShadowKernelSize->GetInt(); FVector4f PCSSParameterValues = FVector4f(0, MaxKernelSize / float(ShadowInfo->ResolutionX), 0, 0); SetShaderValue(BatchedParameters, PCSSParameters, PCSSParameterValues); } protected: LAYOUT_FIELD(FShaderParameter, PCSSParameters); }; // Sort by descending resolution struct FCompareFProjectedShadowInfoByResolution { FORCEINLINE bool operator() (const FProjectedShadowInfo& A, const FProjectedShadowInfo& B) const { return (B.ResolutionX * B.ResolutionY < A.ResolutionX * A.ResolutionY); } }; // Sort by shadow type (CSMs first, then other types). // Then sort CSMs by descending split index, and other shadows by resolution. // Used to render shadow cascades in far to near order, whilst preserving the // descending resolution sort behavior for other shadow types. // Note: the ordering must match the requirements of blend modes set in SetBlendStateForProjection (blend modes that overwrite must come first) struct FCompareFProjectedShadowInfoBySplitIndex { FORCEINLINE bool operator()( const FProjectedShadowInfo& A, const FProjectedShadowInfo& B ) const { if (A.IsWholeSceneDirectionalShadow()) { if (B.IsWholeSceneDirectionalShadow()) { if (A.bRayTracedDistanceField != B.bRayTracedDistanceField) { // RTDF shadows need to be rendered after all CSM, because they overlap in depth range with Far Cascades, which will use an overwrite blend mode for the fade plane. if (!A.bRayTracedDistanceField && B.bRayTracedDistanceField) { return true; } if (A.bRayTracedDistanceField && !B.bRayTracedDistanceField) { return false; } } // Both A and B are CSMs // Compare Split Indexes, to order them far to near. return (B.CascadeSettings.ShadowSplitIndex < A.CascadeSettings.ShadowSplitIndex); } // A is a CSM, B is per-object shadow etc. // B should be rendered after A. return true; } else { if (B.IsWholeSceneDirectionalShadow()) { // B should be rendered before A. return false; } // Neither shadow is a CSM // Sort by descending resolution. return FCompareFProjectedShadowInfoByResolution()(A, B); } } };