Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/TranslucentLighting.cpp

1831 lines
77 KiB
C++
Raw Normal View History

// 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) 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 <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, 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 <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,
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<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,
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<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) 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<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) 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<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, 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<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->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<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::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<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) 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<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) 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<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());
}
}
}
}