// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. /*============================================================================= TranslucentLighting.cpp: Translucent lighting implementation. =============================================================================*/ #include "RendererPrivate.h" #include "ScenePrivate.h" #include "OneColorShader.h" #include "LightRendering.h" #include "SceneFilterRendering.h" #include "ScreenRendering.h" #include "AmbientCubemapParameters.h" #include "SceneUtils.h" class FMaterial; /** Whether to allow rendering translucency shadow depths. */ bool GUseTranslucencyShadowDepths = true; int32 GUseTranslucentLightingVolumes = 1; FAutoConsoleVariableRef CVarUseTranslucentLightingVolumes( TEXT("r.TranslucentLightingVolume"), GUseTranslucentLightingVolumes, TEXT("Whether to allow updating the translucent lighting volumes.\n") TEXT("0:off, otherwise on, default is 1"), ECVF_Cheat | ECVF_RenderThreadSafe ); float GTranslucentVolumeMinFOV = 45; static FAutoConsoleVariableRef CVarTranslucentVolumeMinFOV( TEXT("r.TranslucentVolumeMinFOV"), GTranslucentVolumeMinFOV, TEXT("Minimum FOV for translucent lighting volume. Prevents popping in lighting when zooming in."), ECVF_Cheat | ECVF_RenderThreadSafe ); float GTranslucentVolumeFOVSnapFactor = 10; static FAutoConsoleVariableRef CTranslucentVolumeFOVSnapFactor( TEXT("r.TranslucentVolumeFOVSnapFactor"), GTranslucentVolumeFOVSnapFactor, TEXT("FOV will be snapped to a factor of this before computing volume bounds."), ECVF_Cheat | ECVF_RenderThreadSafe ); int32 GUseTranslucencyVolumeBlur = 1; FAutoConsoleVariableRef CVarUseTranslucentLightingVolumeBlur( TEXT("r.TranslucencyVolumeBlur"), GUseTranslucencyVolumeBlur, TEXT("Whether to blur the translucent lighting volumes.\n") TEXT("0:off, otherwise on, default is 1"), ECVF_Scalability | ECVF_RenderThreadSafe ); int32 GTranslucencyLightingVolumeDim = 64; FAutoConsoleVariableRef CVarTranslucencyLightingVolumeDim( TEXT("r.TranslucencyLightingVolumeDim"), GTranslucencyLightingVolumeDim, TEXT("Dimensions of the volume textures used for translucency lighting. Larger textures result in higher resolution but lower performance."), ECVF_Scalability | ECVF_RenderThreadSafe ); static TAutoConsoleVariable CVarTranslucencyLightingVolumeInnerDistance( TEXT("r.TranslucencyLightingVolumeInnerDistance"), 1500.0f, TEXT("Distance from the camera that the first volume cascade should end"), ECVF_RenderThreadSafe); static TAutoConsoleVariable CVarTranslucencyLightingVolumeOuterDistance( TEXT("r.TranslucencyLightingVolumeOuterDistance"), 5000.0f, TEXT("Distance from the camera that the second volume cascade should end"), ECVF_RenderThreadSafe); void FViewInfo::CalcTranslucencyLightingVolumeBounds(FBox* InOutCascadeBoundsArray, int32 NumCascades) const { for (int32 CascadeIndex = 0; CascadeIndex < NumCascades; CascadeIndex++) { float InnerDistance = CVarTranslucencyLightingVolumeInnerDistance.GetValueOnRenderThread(); float OuterDistance = CVarTranslucencyLightingVolumeOuterDistance.GetValueOnRenderThread(); const float FrustumStartDistance = CascadeIndex == 0 ? 0 : InnerDistance; const float FrustumEndDistance = CascadeIndex == 0 ? InnerDistance : OuterDistance; float FOV = PI / 4.0f; float AspectRatio = 1.0f; if (IsPerspectiveProjection()) { // Derive FOV and aspect ratio from the perspective projection matrix FOV = FMath::Atan(1.0f / ShadowViewMatrices.ProjMatrix.M[0][0]); // Clamp to prevent shimmering when zooming in FOV = FMath::Max(FOV, GTranslucentVolumeMinFOV * (float)PI / 180.0f); const float RoundFactorRadians = GTranslucentVolumeFOVSnapFactor * (float)PI / 180.0f; // Round up to a fixed factor // This causes the volume lighting to make discreet jumps as the FOV animates, instead of slowly crawling over a long period FOV = FOV + RoundFactorRadians - FMath::Fmod(FOV, RoundFactorRadians); AspectRatio = ShadowViewMatrices.ProjMatrix.M[1][1] / ShadowViewMatrices.ProjMatrix.M[0][0]; } const float StartHorizontalLength = FrustumStartDistance * FMath::Tan(FOV); const FVector StartCameraRightOffset = ShadowViewMatrices.ViewMatrix.GetColumn(0) * StartHorizontalLength; const float StartVerticalLength = StartHorizontalLength / AspectRatio; const FVector StartCameraUpOffset = ShadowViewMatrices.ViewMatrix.GetColumn(1) * StartVerticalLength; const float EndHorizontalLength = FrustumEndDistance * FMath::Tan(FOV); const FVector EndCameraRightOffset = ShadowViewMatrices.ViewMatrix.GetColumn(0) * EndHorizontalLength; const float EndVerticalLength = EndHorizontalLength / AspectRatio; const FVector EndCameraUpOffset = ShadowViewMatrices.ViewMatrix.GetColumn(1) * EndVerticalLength; FVector SplitVertices[8]; SplitVertices[0] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumStartDistance + StartCameraRightOffset + StartCameraUpOffset; SplitVertices[1] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumStartDistance + StartCameraRightOffset - StartCameraUpOffset; SplitVertices[2] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumStartDistance - StartCameraRightOffset + StartCameraUpOffset; SplitVertices[3] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumStartDistance - StartCameraRightOffset - StartCameraUpOffset; SplitVertices[4] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumEndDistance + EndCameraRightOffset + EndCameraUpOffset; SplitVertices[5] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumEndDistance + EndCameraRightOffset - EndCameraUpOffset; SplitVertices[6] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumEndDistance - EndCameraRightOffset + EndCameraUpOffset; SplitVertices[7] = ShadowViewMatrices.ViewOrigin + GetViewDirection() * FrustumEndDistance - EndCameraRightOffset - EndCameraUpOffset; FVector Center(0,0,0); // Weight the far vertices more so that the bounding sphere will be further from the camera // This minimizes wasted shadowmap space behind the viewer const float FarVertexWeightScale = 10.0f; for (int32 VertexIndex = 0; VertexIndex < 8; VertexIndex++) { const float Weight = VertexIndex > 3 ? 1 / (4.0f + 4.0f / FarVertexWeightScale) : 1 / (4.0f + 4.0f * FarVertexWeightScale); Center += SplitVertices[VertexIndex] * Weight; } float RadiusSquared = 0; for (int32 VertexIndex = 0; VertexIndex < 8; VertexIndex++) { RadiusSquared = FMath::Max(RadiusSquared, (Center - SplitVertices[VertexIndex]).SizeSquared()); } FSphere SphereBounds(Center, FMath::Sqrt(RadiusSquared)); // Snap the center to a multiple of the volume dimension for stability SphereBounds.Center.X = SphereBounds.Center.X - FMath::Fmod(SphereBounds.Center.X, SphereBounds.W * 2 / GTranslucencyLightingVolumeDim); SphereBounds.Center.Y = SphereBounds.Center.Y - FMath::Fmod(SphereBounds.Center.Y, SphereBounds.W * 2 / GTranslucencyLightingVolumeDim); SphereBounds.Center.Z = SphereBounds.Center.Z - FMath::Fmod(SphereBounds.Center.Z, SphereBounds.W * 2 / GTranslucencyLightingVolumeDim); InOutCascadeBoundsArray[CascadeIndex] = FBox(SphereBounds.Center - SphereBounds.W, SphereBounds.Center + SphereBounds.W); } } /** * Vertex shader used to render shadow maps for translucency. */ class FTranslucencyShadowDepthVS : public FMeshMaterialShader { DECLARE_SHADER_TYPE(FTranslucencyShadowDepthVS,MeshMaterial); public: static bool ShouldCache(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType) { return IsTranslucentBlendMode(Material->GetBlendMode()) && IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } FTranslucencyShadowDepthVS() {} FTranslucencyShadowDepthVS(const FMeshMaterialShaderType::CompiledShaderInitializerType& Initializer) : FMeshMaterialShader(Initializer) { ShadowParameters.Bind(Initializer.ParameterMap); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FMeshMaterialShader::Serialize(Ar); Ar << ShadowParameters; return bShaderHasOutdatedParameters; } void SetParameters( FRHICommandList& RHICmdList, const FMaterialRenderProxy* MaterialRenderProxy, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo ) { FMeshMaterialShader::SetParameters(RHICmdList, GetVertexShader(), MaterialRenderProxy, *MaterialRenderProxy->GetMaterial(View.GetFeatureLevel()), View, ESceneRenderTargetsMode::DontSet); ShadowParameters.SetVertexShader(RHICmdList, this, View, ShadowInfo, MaterialRenderProxy); } void SetMesh(FRHICommandList& RHICmdList, const FVertexFactory* VertexFactory,const FSceneView& View,const FPrimitiveSceneProxy* Proxy,const FMeshBatchElement& BatchElement, float DitheredLODTransitionValue) { FMeshMaterialShader::SetMesh(RHICmdList, GetVertexShader(),VertexFactory,View,Proxy,BatchElement,DitheredLODTransitionValue); } private: FShadowDepthShaderParameters ShadowParameters; }; enum ETranslucencyShadowDepthShaderMode { TranslucencyShadowDepth_PerspectiveCorrect, TranslucencyShadowDepth_Standard, }; template class TTranslucencyShadowDepthVS : public FTranslucencyShadowDepthVS { DECLARE_SHADER_TYPE(TTranslucencyShadowDepthVS,MeshMaterial); public: TTranslucencyShadowDepthVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FTranslucencyShadowDepthVS(Initializer) { } TTranslucencyShadowDepthVS() {} static void ModifyCompilationEnvironment( EShaderPlatform Platform, const FMaterial* Material, FShaderCompilerEnvironment& OutEnvironment ) { FTranslucencyShadowDepthVS::ModifyCompilationEnvironment(Platform, Material, OutEnvironment); OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == TranslucencyShadowDepth_PerspectiveCorrect ? 1 : 0)); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthVS,TEXT("TranslucentShadowDepthShaders"),TEXT("MainVS"),SF_Vertex); IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthVS,TEXT("TranslucentShadowDepthShaders"),TEXT("MainVS"),SF_Vertex); /** * Pixel shader used for accumulating translucency layer densities */ class FTranslucencyShadowDepthPS : public FMeshMaterialShader { DECLARE_SHADER_TYPE(FTranslucencyShadowDepthPS,MeshMaterial); public: static bool ShouldCache(EShaderPlatform Platform,const FMaterial* Material,const FVertexFactoryType* VertexFactoryType) { return IsTranslucentBlendMode(Material->GetBlendMode()) && IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } FTranslucencyShadowDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FMeshMaterialShader(Initializer) { TranslInvMaxSubjectDepth.Bind(Initializer.ParameterMap,TEXT("TranslInvMaxSubjectDepth")); TranslucentShadowStartOffset.Bind(Initializer.ParameterMap,TEXT("TranslucentShadowStartOffset")); TranslucencyProjectionParameters.Bind(Initializer.ParameterMap); } FTranslucencyShadowDepthPS() {} void SetParameters( FRHICommandList& RHICmdList, const FMaterialRenderProxy* MaterialRenderProxy, const FSceneView& View, const FProjectedShadowInfo* ShadowInfo ) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); const auto FeatureLevel = View.GetFeatureLevel(); //@todo - scene depth can be bound by the material for use in depth fades // This is incorrect when rendering a shadowmap as it's not from the camera's POV // Set the scene depth texture to something safe when rendering shadow depths FMeshMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialRenderProxy, *MaterialRenderProxy->GetMaterial(FeatureLevel), View, ESceneRenderTargetsMode::NonSceneAlignedPass); SetShaderValue(RHICmdList, ShaderRHI, TranslInvMaxSubjectDepth, ShadowInfo->InvMaxSubjectDepth); const float LocalToWorldScale = ShadowInfo->GetParentSceneInfo()->Proxy->GetLocalToWorld().GetScaleVector().GetMax(); const float TranslucentShadowStartOffsetValue = MaterialRenderProxy->GetMaterial(FeatureLevel)->GetTranslucentShadowStartOffset() * LocalToWorldScale; SetShaderValue(RHICmdList, ShaderRHI,TranslucentShadowStartOffset, TranslucentShadowStartOffsetValue / (ShadowInfo->MaxSubjectZ - ShadowInfo->MinSubjectZ)); TranslucencyProjectionParameters.Set(RHICmdList, this); } void SetMesh(FRHICommandList& RHICmdList, const FVertexFactory* VertexFactory,const FSceneView& View,const FPrimitiveSceneProxy* Proxy,const FMeshBatchElement& BatchElement, float DitheredLODTransitionValue) { FMeshMaterialShader::SetMesh(RHICmdList, GetPixelShader(),VertexFactory,View,Proxy,BatchElement,DitheredLODTransitionValue); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FMeshMaterialShader::Serialize(Ar); Ar << TranslInvMaxSubjectDepth; Ar << TranslucentShadowStartOffset; Ar << TranslucencyProjectionParameters; return bShaderHasOutdatedParameters; } private: FShaderParameter TranslInvMaxSubjectDepth; FShaderParameter TranslucentShadowStartOffset; FTranslucencyShadowProjectionShaderParameters TranslucencyProjectionParameters; }; template class TTranslucencyShadowDepthPS : public FTranslucencyShadowDepthPS { DECLARE_SHADER_TYPE(TTranslucencyShadowDepthPS,MeshMaterial); public: TTranslucencyShadowDepthPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FTranslucencyShadowDepthPS(Initializer) { } TTranslucencyShadowDepthPS() {} static void ModifyCompilationEnvironment( EShaderPlatform Platform, const FMaterial* Material, FShaderCompilerEnvironment& OutEnvironment ) { FTranslucencyShadowDepthPS::ModifyCompilationEnvironment(Platform, Material, OutEnvironment); OutEnvironment.SetDefine(TEXT("PERSPECTIVE_CORRECT_DEPTH"), (uint32)(ShaderMode == TranslucencyShadowDepth_PerspectiveCorrect ? 1 : 0)); } }; IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthPS,TEXT("TranslucentShadowDepthShaders"),TEXT("MainOpacityPS"),SF_Pixel); IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthPS,TEXT("TranslucentShadowDepthShaders"),TEXT("MainOpacityPS"),SF_Pixel); /** * Drawing policy used to create Fourier opacity maps */ class FTranslucencyShadowDepthDrawingPolicy : public FMeshDrawingPolicy { public: struct ContextDataType : public FMeshDrawingPolicy::ContextDataType { const FProjectedShadowInfo* ShadowInfo; explicit ContextDataType(const FProjectedShadowInfo* InShadowInfo) : ShadowInfo(InShadowInfo) {} }; FTranslucencyShadowDepthDrawingPolicy( const FVertexFactory* InVertexFactory, const FMaterialRenderProxy* InMaterialRenderProxy, const FMaterial& InMaterialResource, bool bInDirectionalLight ): FMeshDrawingPolicy(InVertexFactory,InMaterialRenderProxy,InMaterialResource,false,false) { const bool bUsePerspectiveCorrectShadowDepths = !bInDirectionalLight; if (bUsePerspectiveCorrectShadowDepths) { VertexShader = InMaterialResource.GetShader >(InVertexFactory->GetType()); PixelShader = InMaterialResource.GetShader >(InVertexFactory->GetType()); } else { VertexShader = InMaterialResource.GetShader >(InVertexFactory->GetType()); PixelShader = InMaterialResource.GetShader >(InVertexFactory->GetType()); } } void SetSharedState(FRHICommandList& RHICmdList, const FSceneView* View, const ContextDataType PolicyContext) const { // Set the shared mesh resources. FMeshDrawingPolicy::SetSharedState(RHICmdList, View, PolicyContext); VertexShader->SetParameters(RHICmdList, MaterialRenderProxy, *View, PolicyContext.ShadowInfo); PixelShader->SetParameters(RHICmdList, MaterialRenderProxy, *View, PolicyContext.ShadowInfo); } /** * Create bound shader state using the vertex decl from the mesh draw policy * as well as the shaders needed to draw the mesh * @return new bound shader state object */ FBoundShaderStateInput GetBoundShaderStateInput(ERHIFeatureLevel::Type InFeatureLevel) { return FBoundShaderStateInput( FMeshDrawingPolicy::GetVertexDeclaration(), VertexShader->GetVertexShader(), NULL, NULL, PixelShader->GetPixelShader(), NULL); } void SetMeshRenderState( FRHICommandList& RHICmdList, const FSceneView& View, const FPrimitiveSceneProxy* PrimitiveSceneProxy, const FMeshBatch& Mesh, int32 BatchElementIndex, bool bBackFace, float DitheredLODTransitionValue, const ElementDataType& ElementData, const ContextDataType PolicyContext ) const { const FMeshBatchElement& BatchElement = Mesh.Elements[BatchElementIndex]; VertexShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement,DitheredLODTransitionValue); PixelShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement,DitheredLODTransitionValue); FMeshDrawingPolicy::SetMeshRenderState(RHICmdList, View,PrimitiveSceneProxy,Mesh,BatchElementIndex,bBackFace, DitheredLODTransitionValue,ElementData,PolicyContext); } private: FTranslucencyShadowDepthVS* VertexShader; FTranslucencyShadowDepthPS* PixelShader; }; class FTranslucencyShadowDepthDrawingPolicyFactory { public: enum { bAllowSimpleElements = false }; struct ContextType { ContextType(const FProjectedShadowInfo* InShadowInfo, bool bInDirectionalLight) : ShadowInfo(InShadowInfo) , bDirectionalLight(bInDirectionalLight) {} const FProjectedShadowInfo* ShadowInfo; bool bDirectionalLight; }; static bool DrawDynamicMesh( FRHICommandList& RHICmdList, const FSceneView& View, ContextType DrawingContext, const FMeshBatch& Mesh, bool bBackFace, bool bPreFog, const FPrimitiveSceneProxy* PrimitiveSceneProxy, FHitProxyId HitProxyId ) { bool bDirty = false; const auto FeatureLevel = View.GetFeatureLevel(); if (Mesh.CastShadow) { const FMaterialRenderProxy* MaterialRenderProxy = Mesh.MaterialRenderProxy; const EBlendMode BlendMode = MaterialRenderProxy->GetMaterial(FeatureLevel)->GetBlendMode(); // Only render translucent meshes into the Fourier opacity maps if (IsTranslucentBlendMode(BlendMode)) { FTranslucencyShadowDepthDrawingPolicy DrawingPolicy(Mesh.VertexFactory, MaterialRenderProxy, *MaterialRenderProxy->GetMaterial(FeatureLevel), DrawingContext.bDirectionalLight); RHICmdList.BuildAndSetLocalBoundShaderState(DrawingPolicy.GetBoundShaderStateInput(View.GetFeatureLevel())); DrawingPolicy.SetSharedState(RHICmdList, &View, FTranslucencyShadowDepthDrawingPolicy::ContextDataType(DrawingContext.ShadowInfo)); for (int32 BatchElementIndex = 0; BatchElementIndex < Mesh.Elements.Num(); BatchElementIndex++) { DrawingPolicy.SetMeshRenderState(RHICmdList, View,PrimitiveSceneProxy,Mesh,BatchElementIndex,bBackFace,Mesh.DitheredLODTransitionAlpha, FTranslucencyShadowDepthDrawingPolicy::ElementDataType(), FTranslucencyShadowDepthDrawingPolicy::ContextDataType(DrawingContext.ShadowInfo) ); DrawingPolicy.DrawMesh(RHICmdList, Mesh,BatchElementIndex); } bDirty = true; } } return bDirty; } static bool DrawStaticMesh( FRHICommandList& RHICmdList, const FViewInfo& View, ContextType DrawingContext, const FStaticMesh& StaticMesh, bool bPreFog, const FPrimitiveSceneProxy* PrimitiveSceneProxy, FHitProxyId HitProxyId ) { bool bDirty = false; bDirty |= DrawDynamicMesh( RHICmdList, View, DrawingContext, StaticMesh, false, bPreFog, PrimitiveSceneProxy, HitProxyId ); return bDirty; } }; /** Renders shadow maps for translucent primitives. */ void FProjectedShadowInfo::RenderTranslucencyDepths(FRHICommandList& RHICmdList, FDeferredShadingSceneRenderer* SceneRenderer) { checkSlow(!bWholeSceneShadow); SCOPE_CYCLE_COUNTER(STAT_RenderWholeSceneShadowDepthsTime); // Choose an arbitrary view where this shadow's subject is relevant. FViewInfo* FoundView = NULL; for(int32 ViewIndex = 0;ViewIndex < SceneRenderer->Views.Num();ViewIndex++) { FViewInfo* CheckView = &SceneRenderer->Views[ViewIndex]; const FVisibleLightViewInfo& VisibleLightViewInfo = CheckView->VisibleLightInfos[LightSceneInfo->Id]; FPrimitiveViewRelevance ViewRel = VisibleLightViewInfo.ProjectedShadowViewRelevanceMap[ShadowId]; if (ViewRel.bShadowRelevance) { FoundView = CheckView; break; } } check(FoundView && IsInRenderingThread()); // Backup properties of the view that we will override TUniformBufferRef OriginalUniformBuffer = FoundView->UniformBuffer; FMatrix OriginalViewMatrix = FoundView->ViewMatrices.ViewMatrix; // Override the view matrix so that billboarding primitives will be aligned to the light FoundView->ViewMatrices.ViewMatrix = ShadowViewMatrix; FBox VolumeBounds[TVC_MAX]; FoundView->UniformBuffer = FoundView->CreateUniformBuffer( nullptr, ShadowViewMatrix, ShadowViewMatrix.Inverse(), VolumeBounds, TVC_MAX); // Prevent materials from getting overridden during shadow casting and in viewmodes like lighting only // Lighting only should only affect the material used with direct lighting, not the indirect lighting FoundView->bForceShowMaterials = true; { #if WANTS_DRAW_MESH_EVENTS FString EventName; GetShadowTypeNameForDrawEvent(EventName); SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepthActor, *EventName); #endif FTextureRHIParamRef ShadowTransmission0 = GSceneRenderTargets.TranslucencyShadowTransmission[0]->GetRenderTargetItem().TargetableTexture; FTextureRHIParamRef ShadowTransmission1 = GSceneRenderTargets.TranslucencyShadowTransmission[1]->GetRenderTargetItem().TargetableTexture; FTextureRHIParamRef RenderTargets[2] = { ShadowTransmission0, ShadowTransmission1 }; SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIParamRef(), 0, NULL); // Clear the shadow and its border RHICmdList.SetViewport( X, Y, 0.0f, (X + SHADOW_BORDER*2 + ResolutionX), (Y + SHADOW_BORDER*2 + ResolutionY), 1.0f ); FLinearColor ClearColors[2] = {FLinearColor(0,0,0,0), FLinearColor(0,0,0,0)}; DrawClearQuadMRT(RHICmdList, SceneRenderer->FeatureLevel, true, ARRAY_COUNT(ClearColors), ClearColors, false, 1.0f, false, 0); // Set the viewport for the shadow. RHICmdList.SetViewport( (X + SHADOW_BORDER), (Y + SHADOW_BORDER), 0.0f, (X + SHADOW_BORDER + ResolutionX), (Y + SHADOW_BORDER + ResolutionY), 1.0f ); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); RHICmdList.SetBlendState(TStaticBlendState< CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One, CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI()); FTranslucencyShadowDepthDrawingPolicyFactory::ContextType DrawingContext(this,bDirectionalLight); for (int32 MeshBatchIndex = 0; MeshBatchIndex < DynamicSubjectTranslucentMeshElements.Num(); MeshBatchIndex++) { const FMeshBatchAndRelevance& MeshBatchAndRelevance = DynamicSubjectTranslucentMeshElements[MeshBatchIndex]; const FMeshBatch& MeshBatch = *MeshBatchAndRelevance.Mesh; FTranslucencyShadowDepthDrawingPolicyFactory::DrawDynamicMesh(RHICmdList, *FoundView, DrawingContext, MeshBatch, false, true, MeshBatchAndRelevance.PrimitiveSceneProxy, MeshBatch.BatchHitProxyId); } for (int32 PrimitiveIndex = 0; PrimitiveIndex < SubjectTranslucentPrimitives.Num(); PrimitiveIndex++) { const FPrimitiveSceneInfo* PrimitiveSceneInfo = SubjectTranslucentPrimitives[PrimitiveIndex]; int32 PrimitiveId = PrimitiveSceneInfo->GetIndex(); FPrimitiveViewRelevance ViewRelevance = FoundView->PrimitiveViewRelevanceMap[PrimitiveId]; if (!ViewRelevance.bInitializedThisFrame) { // Compute the subject primitive's view relevance since it wasn't cached ViewRelevance = PrimitiveSceneInfo->Proxy->GetViewRelevance(FoundView); } if (ViewRelevance.bDrawRelevance && ViewRelevance.bStaticRelevance) { for (int32 MeshIndex = 0; MeshIndex < PrimitiveSceneInfo->StaticMeshes.Num(); MeshIndex++) { FTranslucencyShadowDepthDrawingPolicyFactory::DrawStaticMesh( RHICmdList, *FoundView, DrawingContext, PrimitiveSceneInfo->StaticMeshes[MeshIndex], true, PrimitiveSceneInfo->Proxy, FHitProxyId()); } } } FResolveParams ResolveParams; RHICmdList.CopyToResolveTarget(ShadowTransmission0, ShadowTransmission0, true, ResolveParams); RHICmdList.CopyToResolveTarget(ShadowTransmission1, ShadowTransmission0, true, ResolveParams); } // Restore overridden properties FoundView->bForceShowMaterials = false; FoundView->UniformBuffer = OriginalUniformBuffer; FoundView->ViewMatrices.ViewMatrix = OriginalViewMatrix; } IMPLEMENT_SHADER_TYPE(,FWriteToSliceGS,TEXT("TranslucentLightingShaders"),TEXT("WriteToSliceMainGS"),SF_Geometry); IMPLEMENT_SHADER_TYPE(,FWriteToSliceVS,TEXT("TranslucentLightingShaders"),TEXT("WriteToSliceMainVS"),SF_Vertex); /** Pixel shader used to filter a single volume lighting cascade. */ class FFilterTranslucentVolumePS : public FGlobalShader { DECLARE_SHADER_TYPE(FFilterTranslucentVolumePS,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } FFilterTranslucentVolumePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { TexelSize.Bind(Initializer.ParameterMap, TEXT("TexelSize")); TranslucencyLightingVolumeAmbient.Bind(Initializer.ParameterMap, TEXT("TranslucencyLightingVolumeAmbient")); TranslucencyLightingVolumeAmbientSampler.Bind(Initializer.ParameterMap, TEXT("TranslucencyLightingVolumeAmbientSampler")); TranslucencyLightingVolumeDirectional.Bind(Initializer.ParameterMap, TEXT("TranslucencyLightingVolumeDirectional")); TranslucencyLightingVolumeDirectionalSampler.Bind(Initializer.ParameterMap, TEXT("TranslucencyLightingVolumeDirectionalSampler")); } FFilterTranslucentVolumePS() {} void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, int32 VolumeCascadeIndex) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); SetShaderValue(RHICmdList, ShaderRHI, TexelSize, 1.0f / GTranslucencyLightingVolumeDim); SetTextureParameter( RHICmdList, ShaderRHI, TranslucencyLightingVolumeAmbient, TranslucencyLightingVolumeAmbientSampler, TStaticSamplerState::GetRHI(), GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture); SetTextureParameter( RHICmdList, ShaderRHI, TranslucencyLightingVolumeDirectional, TranslucencyLightingVolumeDirectionalSampler, TStaticSamplerState::GetRHI(), GSceneRenderTargets.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << TexelSize; Ar << TranslucencyLightingVolumeAmbient; Ar << TranslucencyLightingVolumeAmbientSampler; Ar << TranslucencyLightingVolumeDirectional; Ar << TranslucencyLightingVolumeDirectionalSampler; return bShaderHasOutdatedParameters; } private: FShaderParameter TexelSize; FShaderResourceParameter TranslucencyLightingVolumeAmbient; FShaderResourceParameter TranslucencyLightingVolumeAmbientSampler; FShaderResourceParameter TranslucencyLightingVolumeDirectional; FShaderResourceParameter TranslucencyLightingVolumeDirectionalSampler; }; IMPLEMENT_SHADER_TYPE(,FFilterTranslucentVolumePS,TEXT("TranslucentLightingShaders"),TEXT("FilterMainPS"),SF_Pixel); /** Shader parameters needed to inject direct lighting into a volume. */ class FTranslucentInjectParameters { public: void Bind(const FShaderParameterMap& ParameterMap) { WorldToShadowMatrix.Bind(ParameterMap,TEXT("WorldToShadowMatrix")); ShadowmapMinMax.Bind(ParameterMap,TEXT("ShadowmapMinMax")); VolumeCascadeIndex.Bind(ParameterMap,TEXT("VolumeCascadeIndex")); } template void Set( FRHICommandList& RHICmdList, const ShaderRHIParamRef ShaderRHI, FShader* Shader, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, const FProjectedShadowInfo* ShadowMap, uint32 VolumeCascadeIndexValue, bool bDynamicallyShadowed) const { SetDeferredLightParameters(RHICmdList, ShaderRHI, Shader->GetUniformBufferParameter(), LightSceneInfo, View); if (bDynamicallyShadowed) { FVector4 ShadowmapMinMaxValue; FMatrix WorldToShadowMatrixValue = ShadowMap->GetWorldToShadowMatrix(ShadowmapMinMaxValue); SetShaderValue(RHICmdList, ShaderRHI, WorldToShadowMatrix, WorldToShadowMatrixValue); SetShaderValue(RHICmdList, ShaderRHI, ShadowmapMinMax, ShadowmapMinMaxValue); } SetShaderValue(RHICmdList, ShaderRHI, VolumeCascadeIndex, VolumeCascadeIndexValue); } /** Serializer. */ friend FArchive& operator<<(FArchive& Ar,FTranslucentInjectParameters& P) { Ar << P.WorldToShadowMatrix; Ar << P.ShadowmapMinMax; Ar << P.VolumeCascadeIndex; return Ar; } private: FShaderParameter WorldToShadowMatrix; FShaderParameter ShadowmapMinMax; FShaderParameter VolumeCascadeIndex; }; /** Pixel shader used to accumulate per-object translucent shadows into a volume texture. */ class FTranslucentObjectShadowingPS : public FGlobalShader { DECLARE_SHADER_TYPE(FTranslucentObjectShadowingPS,Global); public: static void ModifyCompilationEnvironment( EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment ) { FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("INJECTION_PIXEL_SHADER"), 1); } static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } FTranslucentObjectShadowingPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { TranslucencyProjectionParameters.Bind(Initializer.ParameterMap); TranslucentInjectParameters.Bind(Initializer.ParameterMap); } FTranslucentObjectShadowingPS() {} void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, const FProjectedShadowInfo* ShadowMap, uint32 VolumeCascadeIndex) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); TranslucencyProjectionParameters.Set(RHICmdList, this); TranslucentInjectParameters.Set(RHICmdList, GetPixelShader(), this, View, LightSceneInfo, ShadowMap, VolumeCascadeIndex, true); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << TranslucencyProjectionParameters; Ar << TranslucentInjectParameters; return bShaderHasOutdatedParameters; } private: FTranslucencyShadowProjectionShaderParameters TranslucencyProjectionParameters; FTranslucentInjectParameters TranslucentInjectParameters; }; IMPLEMENT_SHADER_TYPE(,FTranslucentObjectShadowingPS,TEXT("TranslucentLightingShaders"),TEXT("PerObjectShadowingMainPS"),SF_Pixel); /** Shader that adds direct lighting contribution from the given light to the current volume lighting cascade. */ template class TTranslucentLightingInjectPS : public FMaterialShader { DECLARE_SHADER_TYPE(TTranslucentLightingInjectPS,Material); public: static void ModifyCompilationEnvironment( EShaderPlatform Platform, const FMaterial* Material, FShaderCompilerEnvironment& OutEnvironment ) { FMaterialShader::ModifyCompilationEnvironment(Platform, Material, OutEnvironment); OutEnvironment.SetDefine(TEXT("RADIAL_ATTENUATION"), (uint32)(InjectionType != LightType_Directional)); OutEnvironment.SetDefine(TEXT("INJECTION_PIXEL_SHADER"), 1); OutEnvironment.SetDefine(TEXT("DYNAMICALLY_SHADOWED"), (uint32)bDynamicallyShadowed); OutEnvironment.SetDefine(TEXT("APPLY_LIGHT_FUNCTION"), (uint32)bApplyLightFunction); OutEnvironment.SetDefine(TEXT("INVERSE_SQUARED_FALLOFF"), (uint32)bInverseSquared); } /** * Makes sure only shaders for materials that are explicitly flagged * as 'UsedAsLightFunction' in the Material Editor gets compiled into * the shader cache. */ static bool ShouldCache(EShaderPlatform Platform, const FMaterial* Material) { return (Material->IsLightFunction() || Material->IsSpecialEngineMaterial()) && IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } TTranslucentLightingInjectPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FMaterialShader(Initializer) { DepthBiasParameters.Bind(Initializer.ParameterMap, TEXT("DepthBiasParameters")); CascadeBounds.Bind(Initializer.ParameterMap, TEXT("CascadeBounds")); InnerCascadeBounds.Bind(Initializer.ParameterMap, TEXT("InnerCascadeBounds")); ClippingPlanes.Bind(Initializer.ParameterMap, TEXT("ClippingPlanes")); ShadowInjectParams.Bind(Initializer.ParameterMap, TEXT("ShadowInjectParams")); SpotlightMask.Bind(Initializer.ParameterMap, TEXT("SpotlightMask")); ShadowDepthTexture.Bind(Initializer.ParameterMap, TEXT("ShadowDepthTexture")); ShadowDepthTextureSampler.Bind(Initializer.ParameterMap, TEXT("ShadowDepthTextureSampler")); OnePassShadowParameters.Bind(Initializer.ParameterMap); LightFunctionParameters.Bind(Initializer.ParameterMap); TranslucentInjectParameters.Bind(Initializer.ParameterMap); LightFunctionWorldToLight.Bind(Initializer.ParameterMap, TEXT("LightFunctionWorldToLight")); bStaticallyShadowed.Bind(Initializer.ParameterMap, TEXT("bStaticallyShadowed")); StaticShadowDepthTexture.Bind(Initializer.ParameterMap, TEXT("StaticShadowDepthTexture")); StaticShadowDepthTextureSampler.Bind(Initializer.ParameterMap, TEXT("StaticShadowDepthTextureSampler")); WorldToStaticShadowMatrix.Bind(Initializer.ParameterMap, TEXT("WorldToStaticShadowMatrix")); } TTranslucentLightingInjectPS() {} // @param InnerSplitIndex which CSM shadow map level, INDEX_NONE if no directional light // @param VolumeCascadeIndexValue which volume we render to void SetParameters( FRHICommandList& RHICmdList, const FViewInfo& View, const FLightSceneInfo* LightSceneInfo, const FMaterialRenderProxy* MaterialProxy, const FProjectedShadowInfo* ShadowMap, int32 InnerSplitIndex, int32 VolumeCascadeIndexValue) { check(ShadowMap || !bDynamicallyShadowed); const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FMaterialShader::SetParameters(RHICmdList, ShaderRHI, MaterialProxy, *MaterialProxy->GetMaterial(View.GetFeatureLevel()), View, false, ESceneRenderTargetsMode::SetTextures); FSphere ShadowBounds = ShadowMap ? ShadowMap->ShadowBounds : FSphere(FVector(0,0,0), HALF_WORLD_MAX); SetShaderValue(RHICmdList, ShaderRHI, CascadeBounds, ShadowBounds); FSphere InnerBounds(0); // default to ignore the plane FVector4 Planes[2] = { FVector4(0, 0, 0, -1), FVector4(0, 0, 0, -1) }; // .zw:DistanceFadeMAD to use MAD for efficiency in the shader, default to ignore the plane FVector4 ShadowInjectParamValue(1, 1, 0, 0); if (InnerSplitIndex >= 0) { FShadowCascadeSettings ShadowCascadeSettings; // todo: optimize, not all computed data is needed InnerBounds = LightSceneInfo->Proxy->GetShadowSplitBounds(View, (uint32)InnerSplitIndex, LightSceneInfo->IsPrecomputedLightingValid(), &ShadowCascadeSettings); // near cascade plane { ShadowInjectParamValue.X = ShadowCascadeSettings.SplitNearFadeRegion == 0 ? 1.0f : 1.0f / ShadowCascadeSettings.SplitNearFadeRegion; Planes[0] = FVector4((FVector)(ShadowCascadeSettings.NearFrustumPlane), -ShadowCascadeSettings.NearFrustumPlane.W); } uint32 CascadeCount = LightSceneInfo->Proxy->GetNumViewDependentWholeSceneShadows(View, LightSceneInfo->IsPrecomputedLightingValid()); // far cascade plane if(InnerSplitIndex != CascadeCount - 1) { ShadowInjectParamValue.Y = 1.0f / ShadowCascadeSettings.SplitFarFadeRegion; Planes[1] = FVector4((FVector)(ShadowCascadeSettings.FarFrustumPlane), -ShadowCascadeSettings.FarFrustumPlane.W); } const FVector2D FadeParams = LightSceneInfo->Proxy->GetDirectionalLightDistanceFadeParameters(View.GetFeatureLevel(), LightSceneInfo->IsPrecomputedLightingValid()); // setup constants for the MAD in shader ShadowInjectParamValue.Z = FadeParams.Y; ShadowInjectParamValue.W = -FadeParams.X * FadeParams.Y; } SetShaderValue(RHICmdList, ShaderRHI, ShadowInjectParams, ShadowInjectParamValue); SetShaderValue(RHICmdList, ShaderRHI, InnerCascadeBounds, InnerBounds); SetShaderValueArray(RHICmdList, ShaderRHI, ClippingPlanes, Planes, ARRAY_COUNT(Planes)); //@todo - needs to be a permutation to reduce shadow filtering work SetShaderValue(RHICmdList, ShaderRHI, SpotlightMask, (LightSceneInfo->Proxy->GetLightType() == LightType_Spot ? 1.0f : 0.0f)); if (bDynamicallyShadowed) { SetShaderValue(RHICmdList, ShaderRHI, DepthBiasParameters, FVector2D(ShadowMap->GetShaderDepthBias(), 1.0f / (ShadowMap->MaxSubjectZ - ShadowMap->MinSubjectZ))); SetTextureParameter( RHICmdList, ShaderRHI, ShadowDepthTexture, ShadowDepthTextureSampler, TStaticSamplerState::GetRHI(), GSceneRenderTargets.GetShadowDepthZTexture() ); } if (bDynamicallyShadowed && InjectionType == LightType_Point) { OnePassShadowParameters.Set(RHICmdList, ShaderRHI, ShadowMap); } LightFunctionParameters.Set(RHICmdList, ShaderRHI, LightSceneInfo, 1); TranslucentInjectParameters.Set(RHICmdList, ShaderRHI, this, View, LightSceneInfo, ShadowMap, VolumeCascadeIndexValue, bDynamicallyShadowed); if (LightFunctionWorldToLight.IsBound()) { const FVector Scale = LightSceneInfo->Proxy->GetLightFunctionScale(); // Switch x and z so that z of the user specified scale affects the distance along the light direction const FVector InverseScale = FVector( 1.f / Scale.Z, 1.f / Scale.Y, 1.f / Scale.X ); const FMatrix WorldToLight = LightSceneInfo->Proxy->GetWorldToLight() * FScaleMatrix(FVector(InverseScale)); SetShaderValue(RHICmdList, ShaderRHI, LightFunctionWorldToLight, WorldToLight); } const FStaticShadowDepthMap* StaticShadowDepthMap = LightSceneInfo->Proxy->GetStaticShadowDepthMap(); const uint32 bStaticallyShadowedValue = LightSceneInfo->IsPrecomputedLightingValid() && StaticShadowDepthMap && StaticShadowDepthMap->TextureRHI ? 1 : 0; FTextureRHIParamRef StaticShadowDepthMapTexture = bStaticallyShadowedValue ? StaticShadowDepthMap->TextureRHI : GWhiteTexture->TextureRHI; const FMatrix WorldToStaticShadow = bStaticallyShadowedValue ? StaticShadowDepthMap->WorldToLight : FMatrix::Identity; SetShaderValue(RHICmdList, ShaderRHI, bStaticallyShadowed, bStaticallyShadowedValue); SetTextureParameter( RHICmdList, ShaderRHI, StaticShadowDepthTexture, StaticShadowDepthTextureSampler, TStaticSamplerState::GetRHI(), StaticShadowDepthMapTexture ); SetShaderValue(RHICmdList, ShaderRHI, WorldToStaticShadowMatrix, WorldToStaticShadow); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FMaterialShader::Serialize(Ar); Ar << DepthBiasParameters; Ar << CascadeBounds; Ar << InnerCascadeBounds; Ar << ClippingPlanes; Ar << ShadowInjectParams; Ar << SpotlightMask; Ar << ShadowDepthTexture; Ar << ShadowDepthTextureSampler; Ar << OnePassShadowParameters; Ar << LightFunctionParameters; Ar << TranslucentInjectParameters; Ar << LightFunctionWorldToLight; Ar << bStaticallyShadowed; Ar << StaticShadowDepthTexture; Ar << StaticShadowDepthTextureSampler; Ar << WorldToStaticShadowMatrix; return bShaderHasOutdatedParameters; } private: FShaderParameter DepthBiasParameters; FShaderParameter CascadeBounds; FShaderParameter InnerCascadeBounds; FShaderParameter ClippingPlanes; FShaderParameter ShadowInjectParams; FShaderParameter SpotlightMask; FShaderResourceParameter ShadowDepthTexture; FShaderResourceParameter ShadowDepthTextureSampler; FOnePassPointShadowProjectionShaderParameters OnePassShadowParameters; FLightFunctionSharedParameters LightFunctionParameters; FTranslucentInjectParameters TranslucentInjectParameters; FShaderParameter LightFunctionWorldToLight; FShaderParameter bStaticallyShadowed; FShaderResourceParameter StaticShadowDepthTexture; FShaderResourceParameter StaticShadowDepthTextureSampler; FShaderParameter WorldToStaticShadowMatrix; }; #define IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType,bDynamicallyShadowed,bApplyLightFunction,bInverseSquared) \ typedef TTranslucentLightingInjectPS TTranslucentLightingInjectPS##LightType##bDynamicallyShadowed##bApplyLightFunction##bInverseSquared; \ IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucentLightingInjectPS##LightType##bDynamicallyShadowed##bApplyLightFunction##bInverseSquared,TEXT("TranslucentLightInjectionShaders"),TEXT("InjectMainPS"),SF_Pixel); /** Versions with a light function. */ IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Directional,true,true,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Directional,false,true,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,true,true,true); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,false,true,true); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,true,true,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,false,true,false); /** Versions without a light function. */ IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Directional,true,false,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Directional,false,false,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,true,false,true); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,false,false,true); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,true,false,false); IMPLEMENT_INJECTION_PIXELSHADER_TYPE(LightType_Point,false,false,false); /** Vertex buffer used for rendering into a volume texture. */ class FVolumeRasterizeVertexBuffer : public FVertexBuffer { public: virtual void InitRHI() override { // Used as a non-indexed triangle strip, so 4 vertices per quad const uint32 Size = 4 * sizeof(FScreenVertex); FRHIResourceCreateInfo CreateInfo; VertexBufferRHI = RHICreateVertexBuffer(Size, BUF_Static, CreateInfo); void* Buffer = RHILockVertexBuffer(VertexBufferRHI, 0, Size, RLM_WriteOnly); FScreenVertex* DestVertex = (FScreenVertex*)Buffer; // Setup a full - render target quad // A viewport and UVScaleBias will be used to implement rendering to a sub region DestVertex[0].Position = FVector2D(1, -GProjectionSignY); DestVertex[0].UV = FVector2D(1, 1); DestVertex[1].Position = FVector2D(1, GProjectionSignY); DestVertex[1].UV = FVector2D(1, 0); DestVertex[2].Position = FVector2D(-1, -GProjectionSignY); DestVertex[2].UV = FVector2D(0, 1); DestVertex[3].Position = FVector2D(-1, GProjectionSignY); DestVertex[3].UV = FVector2D(0, 0); RHIUnlockVertexBuffer(VertexBufferRHI); } }; TGlobalResource GVolumeRasterizeVertexBuffer; /** Draws a quad per volume texture slice to the subregion of the volume texture specified. */ void RasterizeToVolumeTexture(FRHICommandList& RHICmdList, FVolumeBounds VolumeBounds) { // Setup the viewport to only render to the given bounds RHICmdList.SetViewport(VolumeBounds.MinX, VolumeBounds.MinY, 0, VolumeBounds.MaxX, VolumeBounds.MaxY, 0); RHICmdList.SetStreamSource(0, GVolumeRasterizeVertexBuffer.VertexBufferRHI, sizeof(FScreenVertex), 0); const int32 NumInstances = VolumeBounds.MaxZ - VolumeBounds.MinZ; // Render a quad per slice affected by the given bounds RHICmdList.DrawPrimitive(PT_TriangleStrip, 0, 2, NumInstances); } /** Helper function that clears the given volume texture render targets. */ template void ClearVolumeTextures(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, const FTextureRHIParamRef* RenderTargets, const FLinearColor* ClearColors) { SetRenderTargets(RHICmdList, NumRenderTargets, RenderTargets, FTextureRHIRef(), 0, NULL); static FGlobalBoundShaderState VolumeClearBoundShaderState; #if PLATFORM_XBOXONE // ClearMRT is faster on Xbox if (true) #else // Currently using a manual clear, which is ~10x faster than a hardware clear of the volume textures on AMD PC GPU's if (false) #endif { RHICmdList.ClearMRT(true, NumRenderTargets, ClearColors, false, 0, false, 0, FIntRect()); } else { RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim); auto ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef GeometryShader(ShaderMap); TShaderMapRef > PixelShader(ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, VolumeClearBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader); VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); FLinearColor ShaderClearColors[MaxSimultaneousRenderTargets]; FMemory::Memzero(ShaderClearColors); for (int32 i = 0; i < NumRenderTargets; i++) { ShaderClearColors[i] = ClearColors[i]; } SetShaderValueArray(RHICmdList, PixelShader->GetPixelShader(), PixelShader->ColorParameter, ShaderClearColors, NumRenderTargets); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); } } void FDeferredShadingSceneRenderer::ClearTranslucentVolumeLighting(FRHICommandListImmediate& RHICmdList) { if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering) { SCOPED_DRAW_EVENT(RHICmdList, ClearTranslucentVolumeLighting); // Clear all volume textures in the same draw with MRT, which is faster than individually static_assert(TVC_MAX == 2, "Only expecting two translucency lighting cascades."); FTextureRHIParamRef RenderTargets[4]; RenderTargets[0] = GSceneRenderTargets.TranslucencyLightingVolumeAmbient[0]->GetRenderTargetItem().TargetableTexture; RenderTargets[1] = GSceneRenderTargets.TranslucencyLightingVolumeDirectional[0]->GetRenderTargetItem().TargetableTexture; RenderTargets[2] = GSceneRenderTargets.TranslucencyLightingVolumeAmbient[1]->GetRenderTargetItem().TargetableTexture; RenderTargets[3] = GSceneRenderTargets.TranslucencyLightingVolumeDirectional[1]->GetRenderTargetItem().TargetableTexture; FLinearColor ClearColors[4]; ClearColors[0] = FLinearColor(0, 0, 0, 0); ClearColors[1] = FLinearColor(0, 0, 0, 0); ClearColors[2] = FLinearColor(0, 0, 0, 0); ClearColors[3] = FLinearColor(0, 0, 0, 0); ClearVolumeTextures(RHICmdList, FeatureLevel, RenderTargets, ClearColors); } } /** Encapsulates a pixel shader that is adding ambient cubemap to the volume. */ class FInjectAmbientCubemapPS : public FGlobalShader { DECLARE_SHADER_TYPE(FInjectAmbientCubemapPS, Global); static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } /** Default constructor. */ FInjectAmbientCubemapPS() {} public: FCubemapShaderParameters CubemapShaderParameters; /** Initialization constructor. */ FInjectAmbientCubemapPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { CubemapShaderParameters.Bind(Initializer.ParameterMap); } // FShader interface. virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << CubemapShaderParameters; return bShaderHasOutdatedParameters; } void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, const FFinalPostProcessSettings::FCubemapEntry& CubemapEntry) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); CubemapShaderParameters.SetParameters(RHICmdList, ShaderRHI, CubemapEntry); } }; IMPLEMENT_SHADER_TYPE(,FInjectAmbientCubemapPS,TEXT("TranslucentLightingShaders"),TEXT("InjectAmbientCubemapMainPS"),SF_Pixel); void FDeferredShadingSceneRenderer::InjectAmbientCubemapTranslucentVolumeLighting(FRHICommandList& RHICmdList) { //@todo - support multiple views const FViewInfo& View = Views[0]; if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering && View.FinalPostProcessSettings.ContributingCubemaps.Num() && !IsSimpleDynamicLightingEnabled()) { SCOPED_DRAW_EVENT(RHICmdList, InjectAmbientCubemapTranslucentVolumeLighting); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); RHICmdList.SetBlendState(TStaticBlendState::GetRHI()); const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim); auto ShaderMap = GetGlobalShaderMap(FeatureLevel); for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { // we don't update the directional volume (could be a HQ option) SetRenderTarget(RHICmdList, GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().TargetableTexture, FTextureRHIRef()); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef GeometryShader(ShaderMap); TShaderMapRef PixelShader(ShaderMap); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader); VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); uint32 Count = View.FinalPostProcessSettings.ContributingCubemaps.Num(); for(uint32 i = 0; i < Count; ++i) { const FFinalPostProcessSettings::FCubemapEntry& CubemapEntry = View.FinalPostProcessSettings.ContributingCubemaps[i]; PixelShader->SetParameters(RHICmdList, View, CubemapEntry); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); } RHICmdList.CopyToResolveTarget(GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().TargetableTexture, GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); } } } void FDeferredShadingSceneRenderer::ClearTranslucentVolumePerObjectShadowing(FRHICommandList& RHICmdList) { if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering) { SCOPED_DRAW_EVENT(RHICmdList, ClearTranslucentVolumePerLightShadowing); static_assert(TVC_MAX == 2, "Only expecting two translucency lighting cascades."); FTextureRHIParamRef RenderTargets[2]; RenderTargets[0] = GSceneRenderTargets.GetTranslucencyVolumeAmbient(TVC_Inner)->GetRenderTargetItem().TargetableTexture; RenderTargets[1] = GSceneRenderTargets.GetTranslucencyVolumeDirectional(TVC_Inner)->GetRenderTargetItem().TargetableTexture; FLinearColor ClearColors[2]; ClearColors[0] = FLinearColor(1, 1, 1, 1); ClearColors[1] = FLinearColor(1, 1, 1, 1); ClearVolumeTextures(RHICmdList, FeatureLevel, RenderTargets, ClearColors); } } /** Calculates volume texture bounds for the given light in the given translucent lighting volume cascade. */ FVolumeBounds CalculateLightVolumeBounds(const FSphere& LightBounds, const FViewInfo& View, uint32 VolumeCascadeIndex, bool bDirectionalLight) { FVolumeBounds VolumeBounds; if (bDirectionalLight) { VolumeBounds = FVolumeBounds(GTranslucencyLightingVolumeDim); } else { // Determine extents in the volume texture const FVector MinPosition = (LightBounds.Center - LightBounds.W - View.TranslucencyLightingVolumeMin[VolumeCascadeIndex]) / View.TranslucencyVolumeVoxelSize[VolumeCascadeIndex]; const FVector MaxPosition = (LightBounds.Center + LightBounds.W - View.TranslucencyLightingVolumeMin[VolumeCascadeIndex]) / View.TranslucencyVolumeVoxelSize[VolumeCascadeIndex]; VolumeBounds.MinX = FMath::Max(FMath::TruncToInt(MinPosition.X), 0); VolumeBounds.MinY = FMath::Max(FMath::TruncToInt(MinPosition.Y), 0); VolumeBounds.MinZ = FMath::Max(FMath::TruncToInt(MinPosition.Z), 0); VolumeBounds.MaxX = FMath::Min(FMath::TruncToInt(MaxPosition.X) + 1, GTranslucencyLightingVolumeDim); VolumeBounds.MaxY = FMath::Min(FMath::TruncToInt(MaxPosition.Y) + 1, GTranslucencyLightingVolumeDim); VolumeBounds.MaxZ = FMath::Min(FMath::TruncToInt(MaxPosition.Z) + 1, GTranslucencyLightingVolumeDim); } return VolumeBounds; } FGlobalBoundShaderState ObjectShadowingBoundShaderState; void FDeferredShadingSceneRenderer::AccumulateTranslucentVolumeObjectShadowing(FRHICommandList& RHICmdList, const FProjectedShadowInfo* InProjectedShadowInfo, bool bClearVolume) { const FLightSceneInfo* LightSceneInfo = &InProjectedShadowInfo->GetLightSceneInfo(); if (bClearVolume) { ClearTranslucentVolumePerObjectShadowing(RHICmdList); } if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering) { SCOPED_DRAW_EVENT(RHICmdList, AccumulateTranslucentVolumeShadowing); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); // Modulate the contribution of multiple object shadows in rgb RHICmdList.SetBlendState(TStaticBlendState::GetRHI()); auto ShaderMap = GetGlobalShaderMap(FeatureLevel); // Inject into each volume cascade for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { //@todo - support multiple views const FViewInfo& View = Views[0]; const bool bDirectionalLight = LightSceneInfo->Proxy->GetLightType() == LightType_Directional; const FVolumeBounds VolumeBounds = CalculateLightVolumeBounds(LightSceneInfo->Proxy->GetBoundingSphere(), View, VolumeCascadeIndex, bDirectionalLight); if (VolumeBounds.IsValid()) { FTextureRHIParamRef RenderTarget; if (VolumeCascadeIndex == 0) { RenderTarget = GSceneRenderTargets.GetTranslucencyVolumeAmbient(TVC_Inner)->GetRenderTargetItem().TargetableTexture; } else { RenderTarget = GSceneRenderTargets.GetTranslucencyVolumeDirectional(TVC_Inner)->GetRenderTargetItem().TargetableTexture; } SetRenderTarget(RHICmdList, RenderTarget, FTextureRHIRef()); TShaderMapRef VertexShader(ShaderMap); TShaderMapRef GeometryShader(ShaderMap); TShaderMapRef PixelShader(ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, ObjectShadowingBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader); VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, InProjectedShadowInfo, VolumeCascadeIndex); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); RHICmdList.CopyToResolveTarget(GSceneRenderTargets.GetTranslucencyVolumeAmbient((ETranslucencyVolumeCascade)VolumeCascadeIndex)->GetRenderTargetItem().TargetableTexture, GSceneRenderTargets.GetTranslucencyVolumeAmbient((ETranslucencyVolumeCascade)VolumeCascadeIndex)->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); } } } } /** * Helper function for finding and setting the right version of TTranslucentLightingInjectPS given template parameters. * @param MaterialProxy must not be 0 * @param InnerSplitIndex todo: get from ShadowMap, INDEX_NONE if no directional light */ template void SetInjectionShader( FRHICommandList& RHICmdList, const FViewInfo& View, const FMaterialRenderProxy* MaterialProxy, const FLightSceneInfo* LightSceneInfo, const FProjectedShadowInfo* ShadowMap, int32 InnerSplitIndex, int32 VolumeCascadeIndexValue, FWriteToSliceVS* VertexShader, FWriteToSliceGS* GeometryShader, bool bApplyLightFunction, bool bInverseSquared) { check(ShadowMap || !bDynamicallyShadowed); const FMaterialShaderMap* MaterialShaderMap = MaterialProxy->GetMaterial(View.GetFeatureLevel())->GetRenderingThreadShaderMap(); FMaterialShader* PixelShader = NULL; const bool Directional = InjectionType == LightType_Directional; if (bApplyLightFunction) { if( bInverseSquared ) { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); PixelShader = InjectionPixelShader; } else { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); PixelShader = InjectionPixelShader; } } else { if( bInverseSquared ) { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); PixelShader = InjectionPixelShader; } else { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); PixelShader = InjectionPixelShader; } } FBoundShaderStateRHIRef& BoundShaderState = LightSceneInfo->TranslucentInjectBoundShaderState[InjectionType][bDynamicallyShadowed][bApplyLightFunction][bInverseSquared]; const FMaterialShaderMap*& CachedShaderMap = LightSceneInfo->TranslucentInjectCachedShaderMaps[InjectionType][bDynamicallyShadowed][bApplyLightFunction][bInverseSquared]; // Recreate the bound shader state if the shader map has changed since the last cache // This can happen due to async shader compiling if (!IsValidRef(BoundShaderState) || CachedShaderMap != MaterialShaderMap) { CachedShaderMap = MaterialShaderMap; check(IsInRenderingThread()); // I didn't know quite how to deal with this caching. It won't work with threads. BoundShaderState = RHICreateBoundShaderState( GScreenVertexDeclaration.VertexDeclarationRHI, VertexShader->GetVertexShader(), FHullShaderRHIRef(), FDomainShaderRHIRef(), PixelShader->GetPixelShader(), GeometryShader->GetGeometryShader()); } RHICmdList.SetBoundShaderState(BoundShaderState); // Now shader is set, bind parameters if (bApplyLightFunction) { if( bInverseSquared ) { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue); } else { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue); } } else { if( bInverseSquared ) { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue); } else { auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS >(); check(InjectionPixelShader); InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue); } } } /** * Information about a light to be injected. * Cached in this struct to avoid recomputing multiple times (multiple cascades). */ struct FTranslucentLightInjectionData { // must not be 0 const FLightSceneInfo* LightSceneInfo; // can be 0 const FProjectedShadowInfo* ProjectedShadowInfo; // bool bApplyLightFunction; // must not be 0 const FMaterialRenderProxy* LightFunctionMaterialProxy; }; /** * Adds a light to LightInjectionData if it should be injected into the translucent volume, and caches relevant information in a FTranslucentLightInjectionData. * @param InProjectedShadowInfo is 0 for unshadowed lights */ static void AddLightForInjection( FDeferredShadingSceneRenderer& SceneRenderer, const FLightSceneInfo& LightSceneInfo, const FProjectedShadowInfo* InProjectedShadowInfo, TArray& LightInjectionData) { if (LightSceneInfo.Proxy->AffectsTranslucentLighting()) { const FVisibleLightInfo& VisibleLightInfo = SceneRenderer.VisibleLightInfos[LightSceneInfo.Id]; const ERHIFeatureLevel::Type FeatureLevel = SceneRenderer.Scene->GetFeatureLevel(); const bool bApplyLightFunction = (SceneRenderer.ViewFamily.EngineShowFlags.LightFunctions && LightSceneInfo.Proxy->GetLightFunctionMaterial() && LightSceneInfo.Proxy->GetLightFunctionMaterial()->GetMaterial(FeatureLevel)->IsLightFunction()); const FMaterialRenderProxy* MaterialProxy = bApplyLightFunction ? LightSceneInfo.Proxy->GetLightFunctionMaterial() : UMaterial::GetDefaultMaterial(MD_LightFunction)->GetRenderProxy(false); // Skip rendering if the DefaultLightFunctionMaterial isn't compiled yet if (MaterialProxy->GetMaterial(FeatureLevel)->IsLightFunction()) { FTranslucentLightInjectionData InjectionData; InjectionData.LightSceneInfo = &LightSceneInfo; InjectionData.ProjectedShadowInfo = InProjectedShadowInfo; InjectionData.bApplyLightFunction = bApplyLightFunction; InjectionData.LightFunctionMaterialProxy = MaterialProxy; LightInjectionData.Add(InjectionData); } } } /** Injects all the lights in LightInjectionData into the translucent lighting volume textures. */ static void InjectTranslucentLightArray(FRHICommandListImmediate& RHICmdList, const FViewInfo& View, const TArray& LightInjectionData) { INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucency, LightInjectionData.Num()); // Inject into each volume cascade // Operate on one cascade at a time to reduce render target switches for (uint32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { const IPooledRenderTarget* RT0 = GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]; const IPooledRenderTarget* RT1 = GSceneRenderTargets.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex]; GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT0); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT1); FTextureRHIParamRef RenderTargets[2]; RenderTargets[0] = RT0->GetRenderTargetItem().TargetableTexture; RenderTargets[1] = RT1->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIRef(), 0, NULL); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); for (int32 LightIndex = 0; LightIndex < LightInjectionData.Num(); LightIndex++) { const FTranslucentLightInjectionData& InjectionData = LightInjectionData[LightIndex]; const FLightSceneInfo* const LightSceneInfo = InjectionData.LightSceneInfo; const bool bInverseSquared = LightSceneInfo->Proxy->IsInverseSquared(); const bool bDirectionalLight = LightSceneInfo->Proxy->GetLightType() == LightType_Directional; const FVolumeBounds VolumeBounds = CalculateLightVolumeBounds(LightSceneInfo->Proxy->GetBoundingSphere(), View, VolumeCascadeIndex, bDirectionalLight); if (VolumeBounds.IsValid()) { TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef GeometryShader(View.ShaderMap); if (bDirectionalLight) { // Accumulate the contribution of multiple lights // Directional lights write their shadowing into alpha of the ambient texture RHICmdList.SetBlendState(TStaticBlendState< CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One, CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI()); if (InjectionData.ProjectedShadowInfo) { // shadows, restricting light contribution to the cascade bounds (except last cascade far to get light functions and no shadows there) SetInjectionShader(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo, InjectionData.ProjectedShadowInfo, InjectionData.ProjectedShadowInfo->CascadeSettings.ShadowSplitIndex, VolumeCascadeIndex, *VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, false); } else { // no shadows SetInjectionShader(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo, InjectionData.ProjectedShadowInfo, -1, VolumeCascadeIndex, *VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, false); } } else { // Accumulate the contribution of multiple lights RHICmdList.SetBlendState(TStaticBlendState< CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One, CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI()); if (InjectionData.ProjectedShadowInfo) { SetInjectionShader(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo, InjectionData.ProjectedShadowInfo, -1, VolumeCascadeIndex, *VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, bInverseSquared); } else { SetInjectionShader(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo, InjectionData.ProjectedShadowInfo, -1, VolumeCascadeIndex, *VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, bInverseSquared); } } VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); } } RHICmdList.CopyToResolveTarget(RT0->GetRenderTargetItem().TargetableTexture, RT0->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); RHICmdList.CopyToResolveTarget(RT1->GetRenderTargetItem().TargetableTexture, RT1->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); } } void FDeferredShadingSceneRenderer::InjectTranslucentVolumeLighting(FRHICommandListImmediate& RHICmdList, const FLightSceneInfo& LightSceneInfo, const FProjectedShadowInfo* InProjectedShadowInfo) { if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering) { SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime); //@todo - support multiple views const FViewInfo& View = Views[0]; TArray LightInjectionData; AddLightForInjection(*this, LightSceneInfo, InProjectedShadowInfo, LightInjectionData); // shadowed or unshadowed (InProjectedShadowInfo==0) InjectTranslucentLightArray(RHICmdList, View, LightInjectionData); } } void FDeferredShadingSceneRenderer::InjectTranslucentVolumeLightingArray(FRHICommandListImmediate& RHICmdList, const TArray& SortedLights, int32 NumLights) { SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime); //@todo - support multiple views const FViewInfo& View = Views[0]; TArray LightInjectionData; LightInjectionData.Empty(NumLights); for (int32 LightIndex = 0; LightIndex < NumLights; LightIndex++) { const FSortedLightSceneInfo& SortedLightInfo = SortedLights[LightIndex]; const FLightSceneInfoCompact& LightSceneInfoCompact = SortedLightInfo.SceneInfo; const FLightSceneInfo* const LightSceneInfo = LightSceneInfoCompact.LightSceneInfo; AddLightForInjection(*this, *LightSceneInfo, NULL, LightInjectionData); } // non-shadowed, non-light function lights InjectTranslucentLightArray(RHICmdList, View, LightInjectionData); } /** Pixel shader used to inject simple lights into the translucent lighting volume */ class FSimpleLightTranslucentLightingInjectPS : public FGlobalShader { DECLARE_SHADER_TYPE(FSimpleLightTranslucentLightingInjectPS,Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } FSimpleLightTranslucentLightingInjectPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer): FGlobalShader(Initializer) { VolumeCascadeIndex.Bind(Initializer.ParameterMap, TEXT("VolumeCascadeIndex")); SimpleLightPositionAndRadius.Bind(Initializer.ParameterMap, TEXT("SimpleLightPositionAndRadius")); SimpleLightColorAndExponent.Bind(Initializer.ParameterMap, TEXT("SimpleLightColorAndExponent")); } FSimpleLightTranslucentLightingInjectPS() {} void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, const FSimpleLightEntry& SimpleLight, const FSimpleLightPerViewEntry& SimpleLightPerViewData, int32 VolumeCascadeIndexValue) { FGlobalShader::SetParameters(RHICmdList, GetPixelShader(), View); FVector4 PositionAndRadius(SimpleLightPerViewData.Position, SimpleLight.Radius); SetShaderValue(RHICmdList, GetPixelShader(), VolumeCascadeIndex, VolumeCascadeIndexValue); SetShaderValue(RHICmdList, GetPixelShader(), SimpleLightPositionAndRadius, PositionAndRadius); FVector4 LightColorAndExponent(SimpleLight.Color, SimpleLight.Exponent); if (SimpleLight.Exponent == 0) { // Correction for lumen units LightColorAndExponent.X *= 16.0f; LightColorAndExponent.Y *= 16.0f; LightColorAndExponent.Z *= 16.0f; } SetShaderValue(RHICmdList, GetPixelShader(), SimpleLightColorAndExponent, LightColorAndExponent); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << VolumeCascadeIndex; Ar << SimpleLightPositionAndRadius; Ar << SimpleLightColorAndExponent; return bShaderHasOutdatedParameters; } private: FShaderParameter VolumeCascadeIndex; FShaderParameter SimpleLightPositionAndRadius; FShaderParameter SimpleLightColorAndExponent; }; IMPLEMENT_SHADER_TYPE(,FSimpleLightTranslucentLightingInjectPS,TEXT("TranslucentLightInjectionShaders"),TEXT("SimpleLightInjectMainPS"),SF_Pixel); FGlobalBoundShaderState InjectSimpleLightBoundShaderState; void FDeferredShadingSceneRenderer::InjectSimpleTranslucentVolumeLightingArray(FRHICommandListImmediate& RHICmdList, const FSimpleLightArray& SimpleLights) { SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime); int32 NumLightsToInject = 0; for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++) { if (SimpleLights.InstanceData[LightIndex].bAffectTranslucency) { NumLightsToInject++; } } if (NumLightsToInject > 0) { //@todo - support multiple views const FViewInfo& View = Views[0]; const int32 ViewIndex = 0; INC_DWORD_STAT_BY(STAT_NumLightsInjectedIntoTranslucency, NumLightsToInject); // Inject into each volume cascade // Operate on one cascade at a time to reduce render target switches for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { const IPooledRenderTarget* RT0 = GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]; const IPooledRenderTarget* RT1 = GSceneRenderTargets.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex]; GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT0); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT1); FTextureRHIParamRef RenderTargets[2]; RenderTargets[0] = RT0->GetRenderTargetItem().TargetableTexture; RenderTargets[1] = RT1->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIRef(), 0, NULL); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); for (int32 LightIndex = 0; LightIndex < SimpleLights.InstanceData.Num(); LightIndex++) { const FSimpleLightEntry& SimpleLight = SimpleLights.InstanceData[LightIndex]; const FSimpleLightPerViewEntry& SimpleLightPerViewData = SimpleLights.GetViewDependentData(LightIndex, ViewIndex, Views.Num()); if (SimpleLight.bAffectTranslucency) { const FSphere LightBounds(SimpleLightPerViewData.Position, SimpleLight.Radius); const FVolumeBounds VolumeBounds = CalculateLightVolumeBounds(LightBounds, View, VolumeCascadeIndex, false); if (VolumeBounds.IsValid()) { TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef GeometryShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, InjectSimpleLightBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader); VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); PixelShader->SetParameters(RHICmdList, View, SimpleLight, SimpleLightPerViewData, VolumeCascadeIndex); // Accumulate the contribution of multiple lights RHICmdList.SetBlendState(TStaticBlendState< CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One, CW_RGB, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_One>::GetRHI()); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); } } } RHICmdList.CopyToResolveTarget(RT0->GetRenderTargetItem().TargetableTexture, RT0->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); RHICmdList.CopyToResolveTarget(RT1->GetRenderTargetItem().TargetableTexture, RT1->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); } } } FGlobalBoundShaderState FilterBoundShaderState; void FDeferredShadingSceneRenderer::FilterTranslucentVolumeLighting(FRHICommandListImmediate& RHICmdList) { if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering) { // textures have to be finalized before reading. for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { const IPooledRenderTarget* RT0 = GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]; const IPooledRenderTarget* RT1 = GSceneRenderTargets.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex]; FTextureRHIRef TargetTexture0 = RT0->GetRenderTargetItem().TargetableTexture; FTextureRHIRef TargetTexture1 = RT1->GetRenderTargetItem().TargetableTexture; RHICmdList.CopyToResolveTarget(TargetTexture0, TargetTexture0, true, FResolveParams()); RHICmdList.CopyToResolveTarget(TargetTexture1, TargetTexture1, true, FResolveParams()); } if (GUseTranslucencyVolumeBlur) { SCOPED_DRAW_EVENT(RHICmdList, FilterTranslucentVolume); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); // Filter each cascade for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++) { const IPooledRenderTarget* RT0 = GSceneRenderTargets.GetTranslucencyVolumeAmbient((ETranslucencyVolumeCascade)VolumeCascadeIndex); const IPooledRenderTarget* RT1 = GSceneRenderTargets.GetTranslucencyVolumeDirectional((ETranslucencyVolumeCascade)VolumeCascadeIndex); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT0); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, RT1); FTextureRHIParamRef RenderTargets[2]; RenderTargets[0] = RT0->GetRenderTargetItem().TargetableTexture; RenderTargets[1] = RT1->GetRenderTargetItem().TargetableTexture; SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIRef(), 0, NULL); //@todo - support multiple views const FViewInfo& View = Views[0]; const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim); TShaderMapRef VertexShader(View.ShaderMap); TShaderMapRef GeometryShader(View.ShaderMap); TShaderMapRef PixelShader(View.ShaderMap); SetGlobalBoundShaderState(RHICmdList, FeatureLevel, FilterBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader); VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim); GeometryShader->SetParameters(RHICmdList, VolumeBounds); PixelShader->SetParameters(RHICmdList, View, VolumeCascadeIndex); RasterizeToVolumeTexture(RHICmdList, VolumeBounds); RHICmdList.CopyToResolveTarget(RT0->GetRenderTargetItem().TargetableTexture, RT0->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); RHICmdList.CopyToResolveTarget(RT1->GetRenderTargetItem().TargetableTexture, RT1->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams()); } } } }