You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#lockdown nick.penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2806454 on 2015/12/16 by Bob.Tellez #UE4 Getting crash reporting working again on linux servers. Since -Unattended is now being passed BEFORE the target folder, the cmd line parsing code was failing so now it parses tokens and switches in a more general way. Also, diagnostics.txt had the incorrect case, since the d is supposed to be capitolized and the crash report processor is case sensitive. #rb Ben.Zeigler #codereview Dmitry.Rekman Change 2805502 on 2015/12/16 by Ben.Zeigler #UE4 Move ValidateEnumProperties into ValidateGeneratedClass, it was happening too early in the generation process so was being called at an invalid time. As a result of this ValidateEnumProperties will not be called correctly for compile on load blueprints, that issue is covered in UE-24569 #codereview mike.beach, bob.tellez Change 2805288 on 2015/12/16 by David.Nikdel #HTTP #HttpRetry - Add new Failed_ConnectionError code to EHttpRequestStatus to distinguish between connection errors and protocol errors. - Changed HTTP retry logic a little bit * If a response was received, retry on service-specific explicit HTTP codes (defaults to empty) * If a response was not received and we did not send a full request, automatically retry * If a response was not received and a request may have been sent, retry if the verb is GET or HEAD (should be idempotent) - Adjusted Curl/IOS/Mac/PS4/WinInet to try and distinguish Failed_ConnectionError where possible * Other systems will default to Failed which is ok (ConnectionError is an opportunistic categorization) * Opened a PS4 ticket to try to improve detection, but unfortunately there's no way (currently) to distinguish between send timeout, connection timeout, and receive timeout, the latter being the problematic case. - Removed the concept of global/default HTTP retry status codes. No system has enough knowledge to set those globally. * Individual requests still specify explicit "retryable" codes and McpServiceBase sets that on each request on a per-service basis #RB: Sam.Zamani #CodeReview: Sam.Zamani, Josh.Markiewicz, Alex.Fennell, Dmitry.Rekman, Sam.Spiro #Fixes: FORT-17804 Change 2803864 on 2015/12/15 by Bob.Tellez #UE4 Changed usage of !UE_SERVER to !IsRunningDedicatedServer in cases where we are preventing load attempts on UFonts. This is so running an editor build with -server works the same as running a cooked server. #rb Dmitry.Rekman #codereview Nick.Darnell Change 2803677 on 2015/12/15 by Billy.Bramer - Expose equality and inequality operators for gameplay attributes #rb Todd.Eckert Change 2802881 on 2015/12/14 by Bob.Tellez #UE4 InheritableComponentHandler no longer keeps records for components that we are no longer inheriting. #rb Phillip.Kavan, Maciej.Mroz #codereview Phillip.Kavan, Maciej.Mroz Change 2801636 on 2015/12/14 by Bob.Tellez #UE4 Returning package insert order for non-imports back to being after those of matching priorities unconditionally since this is what you want even when you are not using the asset registry to preload packages. #codereview Graeme.Thornton Change 2800400 on 2015/12/11 by Jonathan.Lindquist Submitting a new Pivot Painter Edition - now renders to textures - improved workflow - greater capabilities Change 2799579 on 2015/12/11 by John.Abercrombie [AUTOMERGE] Fixed EQS BP query wrappers getting GCed before wrapped query finishes #UE4 Fixes FORT-18649 - Patrols don't spawn consistently - The patrol blueprint was waiting (endlessly) for an EQS query to finish but because the wrapper could be GC-ed while the EQS query was running the delegate would never fire #rb me (this code was written by MieszkoZ) (removed code review for integration of Mieszko.Zielinski, Phil.Cole, Dominic.Barile) -------- Integrated using branch UE4-Fortnite-To-UE4-FortniteReleases/0.10 (reversed) of change#2799575 by John.Abercrombie on 2015/12/11 09:55:11. Change 2799018 on 2015/12/10 by Bob.Tellez #UE4 The asset registry tags stripped from cooked builds is now a blacklist by default that includes only the FiB tag. You can opt-in to using the whitelist by flipping the bUseAssetRegistryTagsWhitelistInsteadOfBlacklist flag. #rb Fred.Kimberley #codereview Peter.Knepley Change 2798926 on 2015/12/10 by Bob.Tellez #UE4 Removed some showflags from the list of "Fixed" showflags since they were actually in use at runtime in Fortnite in a scene capture. #jira FORT-18514 #codereview Martin.Mittring Change 2797758 on 2015/12/10 by Mark.Satterthwaite Defer calls to AUGraphUpdate into FCoreAudioDevice::UpdateHardware - this call will synchronise the calling thread with the CoreAudio thread/run-loop so that the CoreAudio graph is safe to modify and this may incur a significant stall. This means it is far more efficient to amortise the cost of all changes to the graph with a single call. To ensure correctness the audio format conversion components are cached and disposed of after the call to AUGraphUpdate so that any existing operations on the CoreAudio thread are completed prior to disposal. Change 2781204 on 2015/11/25 by Dmitry.Rekman Linux: use jemalloc by default if available. - Alleviates one of the reasons for player disconnect (FORT-18048), which was machines running OOM. #rb Bob.Tellez #codereview Bob.Tellez, Ben.Zeigler Change 2779398 on 2015/11/24 by Mark.Satterthwaite Switch the default graphics API on Mac back to OpenGL, but allow Metal to run with -metal (or -metalsm5 for experimental SM5 support).
1910 lines
80 KiB
C++
1910 lines
80 KiB
C++
// Copyright 1998-2016 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,const FMeshDrawingRenderState& DrawRenderState)
|
|
{
|
|
FMeshMaterialShader::SetMesh(RHICmdList, GetVertexShader(),VertexFactory,View,Proxy,BatchElement,DrawRenderState);
|
|
}
|
|
|
|
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::DontSet);
|
|
|
|
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,const FMeshDrawingRenderState& DrawRenderState)
|
|
{
|
|
FMeshMaterialShader::SetMesh(RHICmdList, GetPixelShader(),VertexFactory,View,Proxy,BatchElement,DrawRenderState);
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 FMeshDrawingRenderState& DrawRenderState,
|
|
const ElementDataType& ElementData,
|
|
const ContextDataType PolicyContext
|
|
) const
|
|
{
|
|
const FMeshBatchElement& BatchElement = Mesh.Elements[BatchElementIndex];
|
|
VertexShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement,DrawRenderState);
|
|
PixelShader->SetMesh(RHICmdList, VertexFactory,View,PrimitiveSceneProxy,BatchElement,DrawRenderState);
|
|
|
|
FMeshDrawingPolicy::SetMeshRenderState(RHICmdList, View,PrimitiveSceneProxy,Mesh,BatchElementIndex,bBackFace,DrawRenderState,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));
|
|
const FMeshDrawingRenderState DrawRenderState(Mesh.DitheredLODTransitionAlpha);
|
|
|
|
for (int32 BatchElementIndex = 0; BatchElementIndex < Mesh.Elements.Num(); BatchElementIndex++)
|
|
{
|
|
TDrawEvent<FRHICommandList> MeshEvent;
|
|
BeginMeshDrawEvent(RHICmdList, PrimitiveSceneProxy, Mesh, MeshEvent);
|
|
|
|
DrawingPolicy.SetMeshRenderState(RHICmdList, View,PrimitiveSceneProxy,Mesh,BatchElementIndex,bBackFace,DrawRenderState,
|
|
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)
|
|
{
|
|
check(IsInRenderingThread());
|
|
checkSlow(!bWholeSceneShadow);
|
|
SCOPE_CYCLE_COUNTER(STAT_RenderWholeSceneShadowDepthsTime);
|
|
|
|
bool bMakeViewSnapshot = FRHICommandListImmediate::AnyRenderThreadTasksOutstanding(); // if there are task in flight, we need to clone
|
|
|
|
// 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)
|
|
{
|
|
if (bMakeViewSnapshot)
|
|
{
|
|
FoundView = CheckView->CreateSnapshot();
|
|
}
|
|
else
|
|
{
|
|
FoundView = CheckView;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
check(FoundView);
|
|
|
|
TUniformBufferRef<FViewUniformShaderParameters> OriginalViewUniformBuffer;
|
|
TUniformBufferRef<FFrameUniformShaderParameters> OriginalFrameUniformBuffer;
|
|
|
|
FMatrix OriginalViewMatrix;
|
|
|
|
if (!bMakeViewSnapshot)
|
|
{
|
|
check(!FRHICommandListImmediate::AnyRenderThreadTasksOutstanding()); // we should add tasks that use the hacked view if it wasn't cloned.
|
|
// Backup properties of the view that we will override
|
|
OriginalViewUniformBuffer = FoundView->ViewUniformBuffer;
|
|
OriginalFrameUniformBuffer = FoundView->FrameUniformBuffer;
|
|
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->CreateUniformBuffer(
|
|
FoundView->ViewUniformBuffer,
|
|
FoundView->FrameUniformBuffer,
|
|
RHICmdList,
|
|
nullptr,
|
|
ShadowViewMatrix,
|
|
ShadowViewMatrix.Inverse(),
|
|
VolumeBounds,
|
|
TVC_MAX);
|
|
|
|
{
|
|
#if WANTS_DRAW_MESH_EVENTS
|
|
FString EventName;
|
|
GetShadowTypeNameForDrawEvent(EventName);
|
|
SCOPED_DRAW_EVENTF(RHICmdList, EventShadowDepthActor, *EventName);
|
|
#endif
|
|
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
FTextureRHIParamRef ShadowTransmission0 = SceneContext.TranslucencyShadowTransmission[0]->GetRenderTargetItem().TargetableTexture;
|
|
FTextureRHIParamRef ShadowTransmission1 = SceneContext.TranslucencyShadowTransmission[1]->GetRenderTargetItem().TargetableTexture;
|
|
FTextureRHIParamRef RenderTargets[2] =
|
|
{
|
|
ShadowTransmission0,
|
|
ShadowTransmission1
|
|
};
|
|
SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIParamRef(), 0, NULL, true);
|
|
|
|
// 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, ShadowTransmission1, true, ResolveParams);
|
|
}
|
|
|
|
// Restore overridden properties
|
|
if (!bMakeViewSnapshot)
|
|
{
|
|
check(!FRHICommandListImmediate::AnyRenderThreadTasksOutstanding()); // we should add tasks that use the hacked view if it wasn't cloned.
|
|
FoundView->ViewUniformBuffer = OriginalViewUniformBuffer;
|
|
FoundView->FrameUniformBuffer = OriginalFrameUniformBuffer;
|
|
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)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
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(),
|
|
SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture);
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
ShaderRHI,
|
|
TranslucencyLightingVolumeDirectional,
|
|
TranslucencyLightingVolumeDirectionalSampler,
|
|
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
|
|
SceneContext.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)));
|
|
|
|
FTexture2DRHIParamRef ShadowDepthTextureResource = nullptr;
|
|
if (InjectionType == LightType_Point)
|
|
{
|
|
if (GBlackTexture && GBlackTexture->TextureRHI)
|
|
{
|
|
ShadowDepthTextureResource = GBlackTexture->TextureRHI->GetTexture2D();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ShadowDepthTextureResource = FSceneRenderTargets::Get(RHICmdList).GetShadowDepthZTexture().GetReference();
|
|
}
|
|
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
ShaderRHI,
|
|
ShadowDepthTexture,
|
|
ShadowDepthTextureSampler,
|
|
TStaticSamplerState<SF_Point,AM_Clamp,AM_Clamp,AM_Clamp>::GetRHI(),
|
|
ShadowDepthTextureResource
|
|
);
|
|
}
|
|
|
|
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->Data.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;
|
|
void* Buffer = nullptr;
|
|
VertexBufferRHI = RHICreateAndLockVertexBuffer(Size, BUF_Static, CreateInfo, Buffer);
|
|
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, true);
|
|
#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
|
|
{
|
|
static FGlobalBoundShaderState VolumeClearBoundShaderState;
|
|
|
|
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);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
|
|
TShaderMapRef<TOneColorPixelShaderMRT<NumRenderTargets> > PixelShader(ShaderMap);
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, VolumeClearBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds);
|
|
}
|
|
PixelShader->SetColors(RHICmdList, ClearColors, NumRenderTargets);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
}
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, (FTextureRHIParamRef*)RenderTargets, NumRenderTargets);
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::ClearTranslucentVolumeLighting(FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, ClearTranslucentVolumeLighting);
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
// 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] = SceneContext.TranslucencyLightingVolumeAmbient[0]->GetRenderTargetItem().TargetableTexture;
|
|
RenderTargets[1] = SceneContext.TranslucencyLightingVolumeDirectional[0]->GetRenderTargetItem().TargetableTexture;
|
|
RenderTargets[2] = SceneContext.TranslucencyLightingVolumeAmbient[1]->GetRenderTargetItem().TargetableTexture;
|
|
RenderTargets[3] = SceneContext.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);
|
|
|
|
const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim);
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
// we don't update the directional volume (could be a HQ option)
|
|
SetRenderTarget(RHICmdList, SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().TargetableTexture, FTextureRHIRef(), true);
|
|
|
|
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());
|
|
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
|
|
TShaderMapRef<FInjectAmbientCubemapPS> PixelShader(ShaderMap);
|
|
|
|
static FGlobalBoundShaderState BoundShaderState;
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
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(SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().TargetableTexture,
|
|
SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex]->GetRenderTargetItem().ShaderResourceTexture, true, FResolveParams());
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDeferredShadingSceneRenderer::ClearTranslucentVolumePerObjectShadowing(FRHICommandList& RHICmdList)
|
|
{
|
|
if (GUseTranslucentLightingVolumes && GSupportsVolumeTextureRendering)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
SCOPED_DRAW_EVENT(RHICmdList, ClearTranslucentVolumePerLightShadowing);
|
|
|
|
static_assert(TVC_MAX == 2, "Only expecting two translucency lighting cascades.");
|
|
FTextureRHIParamRef RenderTargets[2];
|
|
RenderTargets[0] = SceneContext.GetTranslucencyVolumeAmbient(TVC_Inner)->GetRenderTargetItem().TargetableTexture;
|
|
RenderTargets[1] = SceneContext.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);
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
// 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 = SceneContext.GetTranslucencyVolumeAmbient(TVC_Inner)->GetRenderTargetItem().TargetableTexture;
|
|
}
|
|
else
|
|
{
|
|
RenderTarget = SceneContext.GetTranslucencyVolumeDirectional(TVC_Inner)->GetRenderTargetItem().TargetableTexture;
|
|
}
|
|
|
|
SetRenderTarget(RHICmdList, RenderTarget, FTextureRHIRef());
|
|
|
|
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());
|
|
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
|
|
TShaderMapRef<FTranslucentObjectShadowingPS> PixelShader(ShaderMap);
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, ObjectShadowingBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader);
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds);
|
|
}
|
|
PixelShader->SetParameters(RHICmdList, View, LightSceneInfo, InProjectedShadowInfo, VolumeCascadeIndex);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
|
|
RHICmdList.CopyToResolveTarget(SceneContext.GetTranslucencyVolumeAmbient((ETranslucencyVolumeCascade)VolumeCascadeIndex)->GetRenderTargetItem().TargetableTexture,
|
|
SceneContext.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 ? GeometryShader->GetGeometryShader() : nullptr);
|
|
}
|
|
|
|
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)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
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 = SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex];
|
|
const IPooledRenderTarget* RT1 = SceneContext.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, true);
|
|
|
|
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);
|
|
TOptionalShaderMapRef<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);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
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);
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
// 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 = SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex];
|
|
const IPooledRenderTarget* RT1 = SceneContext.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, true);
|
|
|
|
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);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
TShaderMapRef<FSimpleLightTranslucentLightingInjectPS> PixelShader(View.ShaderMap);
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, InjectSimpleLightBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
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)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
#if 0
|
|
// textures have to be finalized before reading.
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
const IPooledRenderTarget* RT0 = SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex];
|
|
const IPooledRenderTarget* RT1 = SceneContext.TranslucencyLightingVolumeDirectional[VolumeCascadeIndex];
|
|
FTextureRHIRef TargetTexture0 = RT0->GetRenderTargetItem().TargetableTexture;
|
|
FTextureRHIRef TargetTexture1 = RT1->GetRenderTargetItem().TargetableTexture;
|
|
RHICmdList.CopyToResolveTarget(TargetTexture0, TargetTexture0, true, FResolveParams());
|
|
RHICmdList.CopyToResolveTarget(TargetTexture1, TargetTexture1, true, FResolveParams());
|
|
}
|
|
#endif
|
|
|
|
if (GUseTranslucencyVolumeBlur)
|
|
{
|
|
//@todo - support multiple views
|
|
const FViewInfo& View = Views[0];
|
|
|
|
SCOPED_DRAW_EVENTF(RHICmdList, FilterTranslucentVolume, TEXT("FilterTranslucentVolume %dx%dx%d Cascades:%d"),
|
|
GTranslucencyLightingVolumeDim, GTranslucencyLightingVolumeDim, GTranslucencyLightingVolumeDim, TVC_MAX);
|
|
|
|
// Filter each cascade
|
|
for (int32 VolumeCascadeIndex = 0; VolumeCascadeIndex < TVC_MAX; VolumeCascadeIndex++)
|
|
{
|
|
const IPooledRenderTarget* RT0 = SceneContext.GetTranslucencyVolumeAmbient((ETranslucencyVolumeCascade)VolumeCascadeIndex);
|
|
const IPooledRenderTarget* RT1 = SceneContext.GetTranslucencyVolumeDirectional((ETranslucencyVolumeCascade)VolumeCascadeIndex);
|
|
|
|
const IPooledRenderTarget* Input0 = SceneContext.TranslucencyLightingVolumeAmbient[VolumeCascadeIndex];
|
|
const IPooledRenderTarget* Input1 = SceneContext.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;
|
|
|
|
FTextureRHIParamRef Inputs[2];
|
|
Inputs[0] = Input0->GetRenderTargetItem().TargetableTexture;
|
|
Inputs[1] = Input1->GetRenderTargetItem().TargetableTexture;
|
|
|
|
static_assert(TVC_MAX == 2, "Final transition logic should change");
|
|
|
|
//the volume textures should still be writable from the injection phase on the first loop.
|
|
if (VolumeCascadeIndex > 0)
|
|
{
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::EWritable, RenderTargets, 2);
|
|
}
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, Inputs, 2);
|
|
|
|
SetRenderTargets(RHICmdList, ARRAY_COUNT(RenderTargets), RenderTargets, FTextureRHIRef(), 0, NULL, true);
|
|
|
|
|
|
const FVolumeBounds VolumeBounds(GTranslucencyLightingVolumeDim);
|
|
TShaderMapRef<FWriteToSliceVS> VertexShader(View.ShaderMap);
|
|
TOptionalShaderMapRef<FWriteToSliceGS> GeometryShader(View.ShaderMap);
|
|
TShaderMapRef<FFilterTranslucentVolumePS> PixelShader(View.ShaderMap);
|
|
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, FilterBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader, *GeometryShader);
|
|
|
|
VertexShader->SetParameters(RHICmdList, VolumeBounds, GTranslucencyLightingVolumeDim);
|
|
if(GeometryShader.IsValid())
|
|
{
|
|
GeometryShader->SetParameters(RHICmdList, VolumeBounds);
|
|
}
|
|
PixelShader->SetParameters(RHICmdList, View, VolumeCascadeIndex);
|
|
|
|
RasterizeToVolumeTexture(RHICmdList, VolumeBounds);
|
|
|
|
//only do readable transition on the final loop since the other ones will do this up front.
|
|
//if (VolumeCascadeIndex == TVC_MAX - 1)
|
|
{
|
|
RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, RenderTargets, 2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|