Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/TranslucentLighting.cpp
Marcus Wassmer 2cd3355b99 Add MRT clear value binding to the RHI. This allows parallel rendering platforms to propagate clear color values across parallel execution boundaries for platforms which require low level control over hardware clears.
Also add many explicit calls to CopyToResolveTarget to indicate the transition of a render target to be able to be used as an SRV.  Soon to be replaced by a more specific RHI function.
Added r.CheckSRVTransitions for D3D11 on windows to check if any of these transition are missing.
Fixes a bug with RHIThreadFence when using PLATFORM_SUPPORTS_PARALLEL_RHI_EXECUTE
PLATFORM_SUPPORTS_PARALLEL_RHI_EXECUTE on PS4 now supports CMASK and HTILE enabled buffers.
#codereview lee.clark,gil.gribb,rolando.caloca,daniel.wright

[CL 2420986 by Marcus Wassmer in Main branch]
2015-01-27 16:14:50 -05:00

1829 lines
77 KiB
C++

// 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<float> CVarTranslucencyLightingVolumeInnerDistance(
TEXT("r.TranslucencyLightingVolumeInnerDistance"),
1500.0f,
TEXT("Distance from the camera that the first volume cascade should end"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> 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)
{
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)
{
FMeshMaterialShader::SetMesh(RHICmdList, GetVertexShader(),VertexFactory,View,Proxy,BatchElement);
}
private:
FShadowDepthShaderParameters ShadowParameters;
};
enum ETranslucencyShadowDepthShaderMode
{
TranslucencyShadowDepth_PerspectiveCorrect,
TranslucencyShadowDepth_Standard,
};
template <ETranslucencyShadowDepthShaderMode ShaderMode>
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<TranslucencyShadowDepth_PerspectiveCorrect>,TEXT("TranslucentShadowDepthShaders"),TEXT("MainVS"),SF_Vertex);
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthVS<TranslucencyShadowDepth_Standard>,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)
{
FMeshMaterialShader::SetMesh(RHICmdList, GetPixelShader(),VertexFactory,View,Proxy,BatchElement);
}
virtual bool Serialize(FArchive& Ar)
{
bool bShaderHasOutdatedParameters = FMeshMaterialShader::Serialize(Ar);
Ar << TranslInvMaxSubjectDepth;
Ar << TranslucentShadowStartOffset;
Ar << TranslucencyProjectionParameters;
return bShaderHasOutdatedParameters;
}
private:
FShaderParameter TranslInvMaxSubjectDepth;
FShaderParameter TranslucentShadowStartOffset;
FTranslucencyShadowProjectionShaderParameters TranslucencyProjectionParameters;
};
template <ETranslucencyShadowDepthShaderMode ShaderMode>
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<TranslucencyShadowDepth_PerspectiveCorrect>,TEXT("TranslucentShadowDepthShaders"),TEXT("MainOpacityPS"),SF_Pixel);
IMPLEMENT_MATERIAL_SHADER_TYPE(template<>,TTranslucencyShadowDepthPS<TranslucencyShadowDepth_Standard>,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<TTranslucencyShadowDepthVS<TranslucencyShadowDepth_PerspectiveCorrect> >(InVertexFactory->GetType());
PixelShader = InMaterialResource.GetShader<TTranslucencyShadowDepthPS<TranslucencyShadowDepth_PerspectiveCorrect> >(InVertexFactory->GetType());
}
else
{
VertexShader = InMaterialResource.GetShader<TTranslucencyShadowDepthVS<TranslucencyShadowDepth_Standard> >(InVertexFactory->GetType());
PixelShader = InMaterialResource.GetShader<TTranslucencyShadowDepthPS<TranslucencyShadowDepth_Standard> >(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,
const ElementDataType& ElementData,
const ContextDataType PolicyContext
) const
{
const FMeshBatchElement& BatchElement = Mesh.Elements[BatchElementIndex];
VertexShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement);
PixelShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement);
FMeshDrawingPolicy::SetMeshRenderState(RHICmdList, View,PrimitiveSceneProxy,Mesh,BatchElementIndex,bBackFace,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,
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<FViewUniformShaderParameters> 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,
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<false, CF_Always>::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<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
GSceneRenderTargets.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture);
SetTextureParameter(
RHICmdList,
ShaderRHI,
TranslucencyLightingVolumeDirectional,
TranslucencyLightingVolumeDirectionalSampler,
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
GSceneRenderTargets.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture);
}
virtual bool Serialize(FArchive& Ar)
{
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<typename ShaderRHIParamRef>
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<FDeferredLightUniformStruct>(), 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)
{
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<ELightComponentType InjectionType, bool bDynamicallyShadowed, bool bApplyLightFunction, bool bInverseSquared>
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, &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);
// 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());
// 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<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::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->bPrecomputedLightingIsValid && 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<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
StaticShadowDepthMapTexture
);
SetShaderValue(RHICmdList, ShaderRHI, WorldToStaticShadowMatrix, WorldToStaticShadow);
}
virtual bool Serialize(FArchive& Ar)
{
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<LightType,bDynamicallyShadowed,bApplyLightFunction,bInverseSquared> 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<FVolumeRasterizeVertexBuffer> 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<int32 NumRenderTargets>
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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim);
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FWriteToSliceVS> VertexShader(ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
TShaderMapRef<TOneColorPixelShaderMRT<NumRenderTargets> > 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<ARRAY_COUNT(RenderTargets)>(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)
{
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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
RHICmdList.SetBlendState(TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::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<FWriteToSliceVS> VertexShader(ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
TShaderMapRef<FInjectAmbientCubemapPS> 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<ARRAY_COUNT(RenderTargets)>(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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
// Modulate the contribution of multiple object shadows in rgb
RHICmdList.SetBlendState(TStaticBlendState<CW_RGB, BO_Add, BF_DestColor, BF_Zero>::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<FWriteToSliceVS> VertexShader(ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
TShaderMapRef<FTranslucentObjectShadowingPS> 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<ELightComponentType InjectionType, bool bDynamicallyShadowed>
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<InjectionType, bDynamicallyShadowed, true, true && !Directional> >();
check(InjectionPixelShader);
PixelShader = InjectionPixelShader;
}
else
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, true, false> >();
check(InjectionPixelShader);
PixelShader = InjectionPixelShader;
}
}
else
{
if( bInverseSquared )
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, false, true && !Directional> >();
check(InjectionPixelShader);
PixelShader = InjectionPixelShader;
}
else
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, false, false> >();
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<InjectionType, bDynamicallyShadowed, true, true && !Directional> >();
check(InjectionPixelShader);
InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue);
}
else
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, true, false> >();
check(InjectionPixelShader);
InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue);
}
}
else
{
if( bInverseSquared )
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, false, true && !Directional> >();
check(InjectionPixelShader);
InjectionPixelShader->SetParameters(RHICmdList, View, LightSceneInfo, MaterialProxy, ShadowMap, InnerSplitIndex, VolumeCascadeIndexValue);
}
else
{
auto InjectionPixelShader = MaterialShaderMap->GetShader< TTranslucentLightingInjectPS<InjectionType, bDynamicallyShadowed, false, false> >();
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<FTranslucentLightInjectionData, SceneRenderingAllocator>& 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<FTranslucentLightInjectionData, SceneRenderingAllocator>& 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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::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<FWriteToSliceVS> VertexShader(View.ShaderMap);
TShaderMapRef<FWriteToSliceGS> 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<LightType_Directional, true>(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo,
InjectionData.ProjectedShadowInfo, InjectionData.ProjectedShadowInfo->CascadeSettings.ShadowSplitIndex, VolumeCascadeIndex,
*VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, false);
}
else
{
// no shadows
SetInjectionShader<LightType_Directional, false>(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<LightType_Point, true>(RHICmdList, View, InjectionData.LightFunctionMaterialProxy, LightSceneInfo,
InjectionData.ProjectedShadowInfo, -1, VolumeCascadeIndex,
*VertexShader, *GeometryShader, InjectionData.bApplyLightFunction, bInverseSquared);
}
else
{
SetInjectionShader<LightType_Point, false>(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<FTranslucentLightInjectionData, SceneRenderingAllocator> LightInjectionData;
AddLightForInjection(*this, LightSceneInfo, InProjectedShadowInfo, LightInjectionData);
// shadowed or unshadowed (InProjectedShadowInfo==0)
InjectTranslucentLightArray(RHICmdList, View, LightInjectionData);
}
}
void FDeferredShadingSceneRenderer::InjectTranslucentVolumeLightingArray(FRHICommandListImmediate& RHICmdList, const TArray<FSortedLightSceneInfo, SceneRenderingAllocator>& SortedLights, int32 NumLights)
{
SCOPE_CYCLE_COUNTER(STAT_TranslucentInjectTime);
//@todo - support multiple views
const FViewInfo& View = Views[0];
TArray<FTranslucentLightInjectionData, SceneRenderingAllocator> 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)
{
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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::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<FWriteToSliceVS> VertexShader(View.ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
TShaderMapRef<FSimpleLightTranslucentLightingInjectPS> 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<FM_Solid, CM_None>::GetRHI());
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::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<FWriteToSliceVS> VertexShader(View.ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
TShaderMapRef<FFilterTranslucentVolumePS> 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());
}
}
}
}