Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PlanarReflectionRendering.cpp
charles derousiers 573de9b809 Change FDeferredLightVS to use FParameters.
#rb none
#jira none
#preflight 61b13818b12ed60581bc8fa5

#ROBOMERGE-AUTHOR: charles.derousiers
#ROBOMERGE-SOURCE: CL 18416796 in //UE5/Release-5.0/... via CL 18416798
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v897-18405271)

[CL 18416802 by charles derousiers in ue5-release-engine-test branch]
2021-12-09 02:45:53 -05:00

885 lines
36 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PlanarReflectionRendering.cpp
=============================================================================*/
#include "PlanarReflectionRendering.h"
#include "Engine/Scene.h"
#include "SceneInterface.h"
#include "RenderingThread.h"
#include "RHIStaticStates.h"
#include "RendererInterface.h"
#include "Camera/CameraTypes.h"
#include "Shader.h"
#include "TextureResource.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "ScenePrivateBase.h"
#include "PostProcess/SceneRenderTargets.h"
#include "GlobalShader.h"
#include "SceneRenderTargetParameters.h"
#include "SceneRendering.h"
#include "DeferredShadingRenderer.h"
#include "ScenePrivate.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessing.h"
#include "LightRendering.h"
#include "Components/SceneCaptureComponent.h"
#include "Components/PlanarReflectionComponent.h"
#include "PlanarReflectionSceneProxy.h"
#include "Containers/ArrayView.h"
#include "PipelineStateCache.h"
#include "ClearQuad.h"
#include "SceneTextureParameters.h"
void SetupPlanarReflectionUniformParameters(const class FSceneView& View, const FPlanarReflectionSceneProxy* ReflectionSceneProxy, FPlanarReflectionUniformParameters& OutParameters)
{
// Degenerate plane causes shader to branch around the reflection lookup
OutParameters.ReflectionPlane.Set(0.0f, 0.0f, 0.0f, 0.0f);
FTexture* PlanarReflectionTextureValue = GBlackTexture;
if (ReflectionSceneProxy && ReflectionSceneProxy->RenderTarget)
{
ensure(ReflectionSceneProxy->ViewRect[0].Min.X >= 0);
// Need to set W separately due to FVector = FPlane, which sets W to 1.0.
OutParameters.ReflectionPlane = ReflectionSceneProxy->ReflectionPlane;
OutParameters.ReflectionPlane.W = ReflectionSceneProxy->ReflectionPlane.W;
PlanarReflectionTextureValue = ReflectionSceneProxy->RenderTarget;
FIntPoint BufferSize = ReflectionSceneProxy->RenderTarget->GetSizeXY();
float InvBufferSizeX = 1.0f / BufferSize.X;
float InvBufferSizeY = 1.0f / BufferSize.Y;
FVector2D PlanarReflectionScreenBoundValue(
1 - 2 * 0.5 / ReflectionSceneProxy->ViewRect[0].Width(),
1 - 2 * 0.5 / ReflectionSceneProxy->ViewRect[0].Height());
// Uses hardware's texture unit to reliably clamp UV if the view fill the entire buffer.
if (View.Family->Views.Num() == 1 &&
ReflectionSceneProxy->ViewRect[0].Min == FIntPoint::ZeroValue &&
ReflectionSceneProxy->ViewRect[0].Max == BufferSize)
{
PlanarReflectionScreenBoundValue = FVector2D(1, 1);
}
FVector4f ScreenScaleBiasValue[2] = {
FVector4f(0, 0, 0, 0),
FVector4f(0, 0, 0, 0),
};
for (int32 ViewIndex = 0; ViewIndex < FMath::Min(View.Family->Views.Num(), GMaxPlanarReflectionViews); ViewIndex++)
{
FIntRect ViewRect = ReflectionSceneProxy->ViewRect[ViewIndex];
ScreenScaleBiasValue[ViewIndex] = FVector4f(
ViewRect.Width() * InvBufferSizeX / +2.0f,
ViewRect.Height() * InvBufferSizeY / (-2.0f * GProjectionSignY),
(ViewRect.Width() / 2.0f + ViewRect.Min.X) * InvBufferSizeX,
(ViewRect.Height() / 2.0f + ViewRect.Min.Y) * InvBufferSizeY);
}
OutParameters.PlanarReflectionOrigin = ReflectionSceneProxy->PlanarReflectionOrigin;
OutParameters.PlanarReflectionXAxis = ReflectionSceneProxy->PlanarReflectionXAxis;
OutParameters.PlanarReflectionYAxis = ReflectionSceneProxy->PlanarReflectionYAxis;
OutParameters.InverseTransposeMirrorMatrix = ReflectionSceneProxy->InverseTransposeMirrorMatrix;
OutParameters.PlanarReflectionParameters = ReflectionSceneProxy->PlanarReflectionParameters;
OutParameters.PlanarReflectionParameters2 = ReflectionSceneProxy->PlanarReflectionParameters2;
OutParameters.bIsStereo = ReflectionSceneProxy->bIsStereo;
OutParameters.PlanarReflectionScreenBound = PlanarReflectionScreenBoundValue;
// Instanced stereo needs both view's values available at once
if (ReflectionSceneProxy->bIsStereo || View.Family->Views.Num() == 1)
{
static_assert(UE_ARRAY_COUNT(ReflectionSceneProxy->ProjectionWithExtraFOV) == 2
&& GPlanarReflectionUniformMaxReflectionViews == 2, "Code assumes max 2 planar reflection views.");
OutParameters.ProjectionWithExtraFOV[0] = ReflectionSceneProxy->ProjectionWithExtraFOV[0];
OutParameters.ProjectionWithExtraFOV[1] = ReflectionSceneProxy->ProjectionWithExtraFOV[1];
OutParameters.PlanarReflectionScreenScaleBias[0] = ScreenScaleBiasValue[0];
OutParameters.PlanarReflectionScreenScaleBias[1] = ScreenScaleBiasValue[1];
}
else
{
int32 ViewIndex = 0;
for (int32 i = 0; i < View.Family->Views.Num(); i++)
{
if (&View == View.Family->Views[i])
{
ViewIndex = i;
break;
}
}
// Clamp the index to not go out of bounds (can happen for example in split screen with > 2 players).
ViewIndex = FMath::Min(ViewIndex, GPlanarReflectionUniformMaxReflectionViews - 1);
// Make sure the current view's value is at index 0
OutParameters.ProjectionWithExtraFOV[0] = ReflectionSceneProxy->ProjectionWithExtraFOV[ViewIndex];
OutParameters.ProjectionWithExtraFOV[1] = FMatrix::Identity;
OutParameters.PlanarReflectionScreenScaleBias[0] = ScreenScaleBiasValue[ViewIndex];
OutParameters.PlanarReflectionScreenScaleBias[1] = FVector4f(0, 0, 0, 0);
}
}
else
{
OutParameters.bIsStereo = false;
}
OutParameters.PlanarReflectionTexture = PlanarReflectionTextureValue->TextureRHI;
OutParameters.PlanarReflectionSampler = PlanarReflectionTextureValue->SamplerStateRHI;
}
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FPlanarReflectionUniformParameters, "PlanarReflectionStruct");
template< bool bEnablePlanarReflectionPrefilter >
class TPrefilterPlanarReflectionPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(TPrefilterPlanarReflectionPS);
SHADER_USE_PARAMETER_STRUCT(TPrefilterPlanarReflectionPS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_REF(FPlanarReflectionUniformParameters, PlanarReflection)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
SHADER_PARAMETER(float, KernelRadiusY)
SHADER_PARAMETER(float, InvPrefilterRoughnessDistance)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorInputTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, SceneColorInputSampler)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return bEnablePlanarReflectionPrefilter ? IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) : true;
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
OutEnvironment.SetDefine(TEXT("ENABLE_PLANAR_REFLECTIONS_PREFILTER"), bEnablePlanarReflectionPrefilter);
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
}
};
IMPLEMENT_SHADER_TYPE(template<>, TPrefilterPlanarReflectionPS<false>, TEXT("/Engine/Private/PlanarReflectionShaders.usf"), TEXT("PrefilterPlanarReflectionPS"), SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>, TPrefilterPlanarReflectionPS<true>, TEXT("/Engine/Private/PlanarReflectionShaders.usf"), TEXT("PrefilterPlanarReflectionPS"), SF_Pixel);
template<bool bEnablePlanarReflectionPrefilter>
void PrefilterPlanarReflection(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
FSceneTextureShaderParameters SceneTextures,
const FPlanarReflectionSceneProxy* ReflectionSceneProxy,
FRDGTextureRef SceneColorTexture,
FRDGTextureRef ViewFamilyTexture)
{
using FPrefilterPlanarReflectionPS = TPrefilterPlanarReflectionPS<bEnablePlanarReflectionPrefilter>;
if(View.FeatureLevel >= ERHIFeatureLevel::SM5)
{
SceneColorTexture = AddProcessPlanarReflectionPass(GraphBuilder, View, SceneColorTexture);
}
{
RDG_EVENT_SCOPE(GraphBuilder, "PrefilterPlanarReflection");
// Workaround for a possible driver bug on S7 Adreno, missing planar reflections
const ERenderTargetLoadAction RTLoadAction = IsVulkanMobilePlatform(View.GetShaderPlatform()) ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ENoAction;
const float FilterWidth = View.ViewRect.Width();
auto* PassParameters = GraphBuilder.AllocParameters<typename FPrefilterPlanarReflectionPS::FParameters>();
PassParameters->View = GetShaderBinding(View.ViewUniformBuffer);
{
FPlanarReflectionUniformParameters PlanarReflectionUniformParameters;
SetupPlanarReflectionUniformParameters(View, ReflectionSceneProxy, PlanarReflectionUniformParameters);
PassParameters->PlanarReflection = TUniformBufferRef<FPlanarReflectionUniformParameters>::CreateUniformBufferImmediate(PlanarReflectionUniformParameters, UniformBuffer_SingleFrame);
}
PassParameters->SceneTextures = SceneTextures;
PassParameters->KernelRadiusY = FMath::Clamp(ReflectionSceneProxy->PrefilterRoughness, 0.0f, 0.04f) * 0.5f * FilterWidth;
PassParameters->InvPrefilterRoughnessDistance = 1.0f / FMath::Max(ReflectionSceneProxy->PrefilterRoughnessDistance, DELTA);
PassParameters->SceneColorInputTexture = SceneColorTexture;
PassParameters->SceneColorInputSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
PassParameters->RenderTargets[0] = FRenderTargetBinding(ViewFamilyTexture, RTLoadAction);
FDeferredLightVS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDeferredLightVS::FRadialLight>(false);
TShaderMapRef<FDeferredLightVS> VertexShader(View.ShaderMap, PermutationVector);
TShaderMapRef<FPrefilterPlanarReflectionPS> PixelShader(View.ShaderMap);
const FIntPoint SceneColorExtent = SceneColorTexture->Desc.Extent;
GraphBuilder.AddPass(
RDG_EVENT_NAME("PrefilterPlanarReflections"),
PassParameters,
ERDGPassFlags::Raster,
[&View, VertexShader, PixelShader, PassParameters, SceneColorExtent](FRHICommandList& RHICmdList)
{
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FIntPoint UV = View.ViewRect.Min;
FIntPoint UVSize = View.ViewRect.Size();
if (RHINeedsToSwitchVerticalAxis(GShaderPlatformForFeatureLevel[View.FeatureLevel]) && !IsMobileHDR())
{
UV.Y = UV.Y + UVSize.Y;
UVSize.Y = -UVSize.Y;
}
FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
UV.X, UV.Y,
UVSize.X, UVSize.Y,
View.ViewRect.Size(),
SceneColorExtent);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS);
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
UV.X, UV.Y,
UVSize.X, UVSize.Y,
View.ViewRect.Size(),
SceneColorExtent,
VertexShader,
EDRF_UseTriangleOptimization);
});
}
}
static void UpdatePlanarReflectionContents_RenderThread(
FRHICommandListImmediate& RHICmdList,
FSceneRenderer* MainSceneRenderer,
FSceneRenderer* SceneRenderer,
FPlanarReflectionSceneProxy* SceneProxy,
FPlanarReflectionRenderTarget* RenderTarget,
const FPlane& MirrorPlane,
const FName OwnerName,
bool bUseSceneColorTexture)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderPlanarReflection);
SceneRenderer->RenderThreadBegin(RHICmdList);
// Make sure we render to the same set of GPUs as the main scene renderer.
if (MainSceneRenderer->ViewFamily.RenderTarget != nullptr)
{
RenderTarget->SetActiveGPUMask(MainSceneRenderer->ViewFamily.RenderTarget->GetGPUMask(RHICmdList));
}
else
{
RenderTarget->SetActiveGPUMask(FRHIGPUMask::GPU0());
}
FBox PlanarReflectionBounds = SceneProxy->WorldBounds;
bool bIsInAnyFrustum = false;
for (int32 ViewIndex = 0; ViewIndex < MainSceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& View = MainSceneRenderer->Views[ViewIndex];
if (MirrorPlane.PlaneDot(View.ViewMatrices.GetViewOrigin()) > 0)
{
if (View.ViewFrustum.IntersectBox(PlanarReflectionBounds.GetCenter(), PlanarReflectionBounds.GetExtent()))
{
bIsInAnyFrustum = true;
break;
}
}
}
if (bIsInAnyFrustum)
{
bool bIsVisibleInAnyView = true;
for (int32 ViewIndex = 0; ViewIndex < MainSceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& View = MainSceneRenderer->Views[ViewIndex];
FSceneViewState* ViewState = View.ViewState;
if (ViewState)
{
FIndividualOcclusionHistory& OcclusionHistory = ViewState->PlanarReflectionOcclusionHistories.FindOrAdd(SceneProxy->PlanarReflectionId);
// +1 to buffered frames because the query is submitted late into the main frame, but read at the beginning of a reflection capture frame
const int32 NumBufferedFrames = FOcclusionQueryHelpers::GetNumBufferedFrames(SceneRenderer->FeatureLevel) + 1;
// +1 to frame counter because we are operating before the main view's InitViews, which is where OcclusionFrameCounter is incremented
uint32 OcclusionFrameCounter = ViewState->OcclusionFrameCounter + 1;
FRHIRenderQuery* PastQuery = OcclusionHistory.GetPastQuery(OcclusionFrameCounter, NumBufferedFrames);
if (PastQuery)
{
uint64 NumSamples = 0;
QUICK_SCOPE_CYCLE_COUNTER(STAT_PlanarReflectionOcclusionQueryResults);
if (RHIGetRenderQueryResult(PastQuery, NumSamples, true))
{
bIsVisibleInAnyView = NumSamples > 0;
if (bIsVisibleInAnyView)
{
break;
}
}
}
}
}
if (bIsVisibleInAnyView)
{
// update any resources that needed a deferred update
FDeferredUpdateResource::UpdateResources(RHICmdList);
{
#if WANTS_DRAW_MESH_EVENTS
FString EventName;
OwnerName.ToString(EventName);
SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("PlanarReflection %s"), *EventName);
#else
SCOPED_DRAW_EVENT(RHICmdList, UpdatePlanarReflectionContent_RenderThread);
#endif
const ERHIFeatureLevel::Type FeatureLevel = SceneRenderer->FeatureLevel;
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("PlanarReflection"), FSceneRenderer::GetRDGParalelExecuteFlags(FeatureLevel));
// Reflection view late update
if (SceneRenderer->Views.Num() > 1)
{
const FMirrorMatrix MirrorMatrix(MirrorPlane);
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& ReflectionViewToUpdate = SceneRenderer->Views[ViewIndex];
const FViewInfo& UpdatedParentView = MainSceneRenderer->Views[ViewIndex];
ReflectionViewToUpdate.UpdatePlanarReflectionViewMatrix(UpdatedParentView, MirrorMatrix);
}
}
// Render the scene normally
{
RDG_RHI_EVENT_SCOPE(GraphBuilder, RenderScene);
SceneRenderer->Render(GraphBuilder);
}
SceneProxy->RenderTarget = RenderTarget;
// Update the view rects into the planar reflection proxy.
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
// Make sure screen percentage has correctly been set on render thread.
check(SceneRenderer->Views[ViewIndex].ViewRect.Area() > 0);
SceneProxy->ViewRect[ViewIndex] = SceneRenderer->Views[ViewIndex].ViewRect;
}
FRDGTextureRef ReflectionOutputTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(RenderTarget->TextureRHI, TEXT("ReflectionOutputTexture")));
GraphBuilder.SetTextureAccessFinal(ReflectionOutputTexture, ERHIAccess::SRVGraphics);
FSceneTextureShaderParameters SceneTextureParameters = CreateSceneTextureShaderParameters(GraphBuilder, SceneRenderer->FeatureLevel, ESceneTextureSetupMode::SceneDepth);
const FMinimalSceneTextures& SceneTextures = FSceneTextures::Get(GraphBuilder);
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& View = SceneRenderer->Views[ViewIndex];
RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
if (MainSceneRenderer->Scene->GetShadingPath() == EShadingPath::Deferred)
{
PrefilterPlanarReflection<true>(GraphBuilder, View, SceneTextureParameters, SceneProxy, SceneTextures.Color.Resolve, ReflectionOutputTexture);
}
else
{
PrefilterPlanarReflection<false>(GraphBuilder, View, SceneTextureParameters, SceneProxy, SceneTextures.Color.Resolve, ReflectionOutputTexture);
}
}
GraphBuilder.Execute();
}
}
}
SceneRenderer->RenderThreadEnd(RHICmdList);
}
// Used for generate valid data to update planar reflection uniform buffer but don't actually render the reflection scene when we are using mobile pixel projected reflection.
static void UpdatePlanarReflectionContentsWithoutRendering_RenderThread(
FRHICommandListImmediate& RHICmdList,
FSceneRenderer* MainSceneRenderer,
FSceneRenderer* SceneRenderer,
FPlanarReflectionSceneProxy* SceneProxy,
FPlanarReflectionRenderTarget* RenderTarget,
const FPlane& MirrorPlane,
const FName OwnerName)
{
QUICK_SCOPE_CYCLE_COUNTER(STAT_RenderPlanarReflection);
SceneRenderer->RenderThreadBegin(RHICmdList);
FBox PlanarReflectionBounds = SceneProxy->WorldBounds;
bool bIsInAnyFrustum = false;
for (int32 ViewIndex = 0; ViewIndex < MainSceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& View = MainSceneRenderer->Views[ViewIndex];
if (MirrorPlane.PlaneDot(View.ViewMatrices.GetViewOrigin()) > 0)
{
if (View.ViewFrustum.IntersectBox(PlanarReflectionBounds.GetCenter(), PlanarReflectionBounds.GetExtent()))
{
bIsInAnyFrustum = true;
break;
}
}
}
if (bIsInAnyFrustum)
{
#if WANTS_DRAW_MESH_EVENTS
FString EventName;
OwnerName.ToString(EventName);
SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("PlanarReflection %s"), *EventName);
#else
SCOPED_DRAW_EVENT(RHICmdList, UpdatePlanarReflectionContent_RenderThread);
#endif
// Reflection view late update
if (SceneRenderer->Views.Num() > 1)
{
const FMirrorMatrix MirrorMatrix(MirrorPlane);
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
FViewInfo& ReflectionViewToUpdate = SceneRenderer->Views[ViewIndex];
const FViewInfo& UpdatedParentView = MainSceneRenderer->Views[ViewIndex];
ReflectionViewToUpdate.UpdatePlanarReflectionViewMatrix(UpdatedParentView, MirrorMatrix);
}
}
SceneRenderer->PrepareViewRectsForRendering(RHICmdList);
SceneProxy->RenderTarget = RenderTarget;
// Update the view rects into the planar reflection proxy.
for (int32 ViewIndex = 0; ViewIndex < SceneRenderer->Views.Num(); ++ViewIndex)
{
// Make sure screen percentage has correctly been set on render thread.
check(SceneRenderer->Views[ViewIndex].ViewRect.Area() > 0);
SceneProxy->ViewRect[ViewIndex] = SceneRenderer->Views[ViewIndex].ViewRect;
}
}
SceneRenderer->RenderThreadEnd(RHICmdList);
}
extern void BuildProjectionMatrix(FIntPoint RenderTargetSize, float FOV, float InNearClippingPlane, FMatrix& ProjectionMatrix);
extern void SetupViewFamilyForSceneCapture(
FSceneViewFamily& ViewFamily,
USceneCaptureComponent* SceneCaptureComponent,
const TArrayView<const FSceneCaptureViewInfo> Views,
float MaxViewDistance,
bool bUseFauxOrthoViewPos,
bool bCaptureSceneColor,
bool bIsPlanarReflection,
FPostProcessSettings* PostProcessSettings,
float PostProcessBlendWeight,
const AActor* ViewActor);
void FScene::UpdatePlanarReflectionContents(UPlanarReflectionComponent* CaptureComponent, FSceneRenderer& MainSceneRenderer)
{
check(CaptureComponent);
{
FIntPoint DesiredBufferSize = FSceneRenderer::GetDesiredInternalBufferSize(MainSceneRenderer.ViewFamily);
FVector2D DesiredPlanarReflectionTextureSizeFloat = FVector2D(DesiredBufferSize.X, DesiredBufferSize.Y) * FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);
FIntPoint DesiredPlanarReflectionTextureSize;
DesiredPlanarReflectionTextureSize.X = FMath::Clamp(FMath::CeilToInt(DesiredPlanarReflectionTextureSizeFloat.X), 1, static_cast<int32>(DesiredBufferSize.X));
DesiredPlanarReflectionTextureSize.Y = FMath::Clamp(FMath::CeilToInt(DesiredPlanarReflectionTextureSizeFloat.Y), 1, static_cast<int32>(DesiredBufferSize.Y));
const bool bIsMobilePixelProjectedReflectionEnabled = IsMobilePixelProjectedReflectionEnabled(GetShaderPlatform());
const bool bIsRenderTargetValid = CaptureComponent->RenderTarget != NULL
&& CaptureComponent->RenderTarget->GetSizeXY() == DesiredPlanarReflectionTextureSize
// The RenderTarget's TextureRHI could be nullptr if it is used for mobile pixel projected reflection.
&& (bIsMobilePixelProjectedReflectionEnabled || CaptureComponent->RenderTarget->TextureRHI.IsValid());
if (CaptureComponent->RenderTarget != NULL && !bIsRenderTargetValid)
{
FPlanarReflectionRenderTarget* RenderTarget = CaptureComponent->RenderTarget;
ENQUEUE_RENDER_COMMAND(ReleaseRenderTargetCommand)(
[RenderTarget](FRHICommandListImmediate& RHICmdList)
{
RenderTarget->ReleaseResource();
delete RenderTarget;
});
CaptureComponent->RenderTarget = NULL;
}
if (CaptureComponent->RenderTarget == NULL)
{
CaptureComponent->RenderTarget = new FPlanarReflectionRenderTarget(DesiredPlanarReflectionTextureSize);
FPlanarReflectionRenderTarget* RenderTarget = CaptureComponent->RenderTarget;
FPlanarReflectionSceneProxy* SceneProxy = CaptureComponent->SceneProxy;
ENQUEUE_RENDER_COMMAND(InitRenderTargetCommand)(
[RenderTarget, SceneProxy, bIsMobilePixelProjectedReflectionEnabled](FRHICommandListImmediate& RHICmdList)
{
// Don't create the RenderTarget's RHI if it is used for mobile pixel projected reflection
if (!bIsMobilePixelProjectedReflectionEnabled)
{
RenderTarget->InitResource();
}
SceneProxy->RenderTarget = nullptr;
});
}
else
{
// Remove the render target on the planar reflection proxy so that this planar reflection is not getting drawn in its own FSceneRenderer.
FPlanarReflectionSceneProxy* SceneProxy = CaptureComponent->SceneProxy;
ENQUEUE_RENDER_COMMAND(InitRenderTargetCommand)(
[SceneProxy](FRHICommandListImmediate& RHICmdList)
{
SceneProxy->RenderTarget = nullptr;
});
}
const FMatrix ComponentTransform = CaptureComponent->GetComponentTransform().ToMatrixWithScale();
FPlane MirrorPlane = FPlane(ComponentTransform.TransformPosition(FVector::ZeroVector), ComponentTransform.TransformVector(FVector(0, 0, 1)));
// Normalize the plane to remove component scaling
bool bNormalized = MirrorPlane.Normalize();
if (!bNormalized)
{
MirrorPlane = FPlane(FVector(0, 0, 1), 0);
}
TArray<FSceneCaptureViewInfo> SceneCaptureViewInfo;
for (int32 ViewIndex = 0; ViewIndex < MainSceneRenderer.Views.Num() && ViewIndex < GMaxPlanarReflectionViews; ++ViewIndex)
{
const FViewInfo& View = MainSceneRenderer.Views[ViewIndex];
FSceneCaptureViewInfo NewView;
FVector2D ViewRectMin = FVector2D(View.UnscaledViewRect.Min.X, View.UnscaledViewRect.Min.Y);
FVector2D ViewRectMax = FVector2D(View.UnscaledViewRect.Max.X, View.UnscaledViewRect.Max.Y);
ViewRectMin *= FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);
ViewRectMax *= FMath::Clamp(CaptureComponent->ScreenPercentage / 100.f, 0.25f, 1.f);
NewView.ViewRect.Min.X = FMath::TruncToInt(ViewRectMin.X);
NewView.ViewRect.Min.Y = FMath::TruncToInt(ViewRectMin.Y);
NewView.ViewRect.Max.X = FMath::CeilToInt(ViewRectMax.X);
NewView.ViewRect.Max.Y = FMath::CeilToInt(ViewRectMax.Y);
// Create a mirror matrix and premultiply the view transform by it
const FMirrorMatrix MirrorMatrix(MirrorPlane);
const FMatrix ViewMatrix(MirrorMatrix * View.ViewMatrices.GetViewMatrix());
const FVector ViewLocation = ViewMatrix.InverseTransformPosition(FVector::ZeroVector);
const FMatrix ViewRotationMatrix = ViewMatrix.RemoveTranslation();
const float HalfFOV = FMath::Atan(1.0f / View.ViewMatrices.GetProjectionMatrix().M[0][0]);
FMatrix ProjectionMatrix;
BuildProjectionMatrix(View.UnscaledViewRect.Size(), HalfFOV + FMath::DegreesToRadians(CaptureComponent->ExtraFOV), GNearClippingPlane, ProjectionMatrix);
NewView.ViewLocation = ViewLocation;
NewView.ViewRotationMatrix = ViewRotationMatrix;
NewView.ProjectionMatrix = ProjectionMatrix;
NewView.StereoPass = View.StereoPass;
NewView.StereoViewIndex = View.StereoViewIndex;
SceneCaptureViewInfo.Add(NewView);
}
FPostProcessSettings PostProcessSettings;
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
CaptureComponent->RenderTarget,
this,
CaptureComponent->ShowFlags)
.SetResolveScene(false)
.SetRealtimeUpdate(true));
// Uses the exact same secondary view fraction on the planar reflection as the main viewport.
ViewFamily.SecondaryViewFraction = MainSceneRenderer.ViewFamily.SecondaryViewFraction;
SetupViewFamilyForSceneCapture(
ViewFamily,
CaptureComponent,
SceneCaptureViewInfo, CaptureComponent->MaxViewDistanceOverride,
/* bUseFauxOrthoViewPos = */ false, /* bCaptureSceneColor = */ true, /* bIsPlanarReflection = */ true,
&PostProcessSettings, 1.0f,
/*ViewActor =*/ nullptr);
// Fork main renderer's screen percentage interface to have exactly same settings.
ViewFamily.EngineShowFlags.ScreenPercentage = MainSceneRenderer.ViewFamily.EngineShowFlags.ScreenPercentage;
ViewFamily.SetScreenPercentageInterface(FSceneRenderer::ForkScreenPercentageInterface(
MainSceneRenderer.ViewFamily.GetScreenPercentageInterface(), ViewFamily));
FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(&ViewFamily, nullptr);
// Disable screen percentage on planar reflection renderer if main one has screen percentage disabled.
SceneRenderer->ViewFamily.EngineShowFlags.ScreenPercentage = MainSceneRenderer.ViewFamily.EngineShowFlags.ScreenPercentage;
for (int32 ViewIndex = 0; ViewIndex < SceneCaptureViewInfo.Num(); ++ViewIndex)
{
SceneRenderer->Views[ViewIndex].GlobalClippingPlane = MirrorPlane;
// Jitter can't be removed completely due to the clipping plane
// Also, this prevents the prefilter pass, which reads from jittered depth, from having to do special handling of it's depth-dependent input
SceneRenderer->Views[ViewIndex].bAllowTemporalJitter = false;
SceneRenderer->Views[ViewIndex].bRenderSceneTwoSided = CaptureComponent->bRenderSceneTwoSided;
CaptureComponent->ProjectionWithExtraFOV[ViewIndex] = SceneCaptureViewInfo[ViewIndex].ProjectionMatrix;
const bool bIsStereo = IStereoRendering::IsStereoEyeView(MainSceneRenderer.Views[0]);
const FMatrix ProjectionMatrix = SceneCaptureViewInfo[ViewIndex].ProjectionMatrix;
FPlanarReflectionSceneProxy* SceneProxy = CaptureComponent->SceneProxy;
ENQUEUE_RENDER_COMMAND(UpdateProxyCommand)(
[ProjectionMatrix, ViewIndex, bIsStereo, SceneProxy](FRHICommandList& RHICmdList)
{
SceneProxy->ProjectionWithExtraFOV[ViewIndex] = ProjectionMatrix;
SceneProxy->bIsStereo = bIsStereo;
});
}
{
const FName OwnerName = CaptureComponent->GetOwner() ? CaptureComponent->GetOwner()->GetFName() : NAME_None;
FSceneRenderer* MainSceneRendererPtr = &MainSceneRenderer;
FPlanarReflectionSceneProxy* SceneProxyPtr = CaptureComponent->SceneProxy;
FPlanarReflectionRenderTarget* RenderTargetPtr = CaptureComponent->RenderTarget;
if (bIsMobilePixelProjectedReflectionEnabled)
{
ENQUEUE_RENDER_COMMAND(CaptureCommand)(
[SceneRenderer, MirrorPlane, OwnerName, MainSceneRendererPtr, SceneProxyPtr, RenderTargetPtr](FRHICommandListImmediate& RHICmdList)
{
UpdatePlanarReflectionContentsWithoutRendering_RenderThread(RHICmdList, MainSceneRendererPtr, SceneRenderer, SceneProxyPtr, RenderTargetPtr, MirrorPlane, OwnerName);
});
}
else
{
ENQUEUE_RENDER_COMMAND(CaptureCommand)(
[SceneRenderer, MirrorPlane, OwnerName, MainSceneRendererPtr, SceneProxyPtr, RenderTargetPtr](FRHICommandListImmediate& RHICmdList)
{
UpdatePlanarReflectionContents_RenderThread(RHICmdList, MainSceneRendererPtr, SceneRenderer, SceneProxyPtr, RenderTargetPtr, MirrorPlane, OwnerName, true);
});
}
}
}
}
void FScene::AddPlanarReflection(UPlanarReflectionComponent* Component)
{
check(Component->SceneProxy);
PlanarReflections_GameThread.Add(Component);
FPlanarReflectionSceneProxy* SceneProxy = Component->SceneProxy;
FScene* Scene = this;
ENQUEUE_RENDER_COMMAND(FAddPlanarReflectionCommand)(
[SceneProxy, Scene](FRHICommandListImmediate& RHICmdList)
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
Scene->PlanarReflections.Add(SceneProxy);
});
}
void FScene::RemovePlanarReflection(UPlanarReflectionComponent* Component)
{
check(Component->SceneProxy);
PlanarReflections_GameThread.Remove(Component);
FPlanarReflectionSceneProxy* SceneProxy = Component->SceneProxy;
FScene* Scene = this;
ENQUEUE_RENDER_COMMAND(FRemovePlanarReflectionCommand)(
[SceneProxy, Scene](FRHICommandListImmediate& RHICmdList)
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
Scene->PlanarReflections.Remove(SceneProxy);
});
}
void FScene::UpdatePlanarReflectionTransform(UPlanarReflectionComponent* Component)
{
check(Component->SceneProxy);
FPlanarReflectionSceneProxy* SceneProxy = Component->SceneProxy;
FMatrix Transform = Component->GetComponentTransform().ToMatrixWithScale();
FScene* Scene = this;
ENQUEUE_RENDER_COMMAND(FUpdatePlanarReflectionCommand)(
[SceneProxy, Transform, Scene](FRHICommandListImmediate& RHICmdList)
{
Scene->ReflectionSceneData.bRegisteredReflectionCapturesHasChanged = true;
SceneProxy->UpdateTransform(Transform);
});
}
class FPlanarReflectionPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FPlanarReflectionPS);
SHADER_USE_PARAMETER_STRUCT(FPlanarReflectionPS, FGlobalShader)
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5);
}
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureParameters, SceneTextures)
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, ViewUniformBuffer)
SHADER_PARAMETER_STRUCT_REF(FPlanarReflectionUniformParameters, PlanarReflectionParameters)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FPlanarReflectionPS, "/Engine/Private/PlanarReflectionShaders.usf", "PlanarReflectionPS", SF_Pixel);
bool FDeferredShadingSceneRenderer::HasDeferredPlanarReflections(const FViewInfo& View) const
{
if (View.bIsPlanarReflection || View.bIsReflectionCapture)
{
return false;
}
// Prevent rendering unsupported views when ViewIndex >= GMaxPlanarReflectionViews
// Planar reflections in those views will fallback to other reflection methods
{
int32 ViewIndex = INDEX_NONE;
ViewFamily.Views.Find(&View, ViewIndex);
if (ViewIndex >= GMaxPlanarReflectionViews)
{
return false;
}
}
bool bAnyVisiblePlanarReflections = false;
for (int32 PlanarReflectionIndex = 0; PlanarReflectionIndex < Scene->PlanarReflections.Num(); PlanarReflectionIndex++)
{
FPlanarReflectionSceneProxy* ReflectionSceneProxy = Scene->PlanarReflections[PlanarReflectionIndex];
if (View.ViewFrustum.IntersectBox(ReflectionSceneProxy->WorldBounds.GetCenter(), ReflectionSceneProxy->WorldBounds.GetExtent()))
{
bAnyVisiblePlanarReflections = true;
break;
}
}
bool bComposePlanarReflections = Scene->PlanarReflections.Num() > 0 && bAnyVisiblePlanarReflections;
return bComposePlanarReflections;
}
void FDeferredShadingSceneRenderer::RenderDeferredPlanarReflections(FRDGBuilder& GraphBuilder, const FSceneTextureParameters& SceneTextures, const FViewInfo& View, FRDGTextureRef& ReflectionsOutputTexture)
{
check(HasDeferredPlanarReflections(View));
// Allocate planar reflection texture
bool bClearReflectionsOutputTexture = false;
if (!ReflectionsOutputTexture)
{
FRDGTextureDesc Desc = FRDGTextureDesc::Create2D(
SceneTextures.SceneDepthTexture->Desc.Extent,
PF_FloatRGBA, FClearValueBinding(FLinearColor(0, 0, 0, 0)),
TexCreate_ShaderResource | TexCreate_RenderTargetable);
ReflectionsOutputTexture = GraphBuilder.CreateTexture(Desc, TEXT("PlanarReflections"));
bClearReflectionsOutputTexture = true;
}
FPlanarReflectionPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FPlanarReflectionPS::FParameters>();
PassParameters->SceneTextures.SceneDepthTexture = SceneTextures.SceneDepthTexture;
PassParameters->SceneTextures.GBufferATexture = SceneTextures.GBufferATexture;
PassParameters->SceneTextures.GBufferBTexture = SceneTextures.GBufferBTexture;
PassParameters->SceneTextures.GBufferCTexture = SceneTextures.GBufferCTexture;
PassParameters->SceneTextures.GBufferDTexture = SceneTextures.GBufferDTexture;
PassParameters->SceneTextures.GBufferETexture = SceneTextures.GBufferETexture;
PassParameters->SceneTextures.GBufferFTexture = SceneTextures.GBufferFTexture;
PassParameters->SceneTextures.GBufferVelocityTexture = SceneTextures.GBufferVelocityTexture;
PassParameters->ViewUniformBuffer = GetShaderBinding(View.ViewUniformBuffer);
PassParameters->RenderTargets[0] = FRenderTargetBinding(
ReflectionsOutputTexture, bClearReflectionsOutputTexture ? ERenderTargetLoadAction::EClear : ERenderTargetLoadAction::ELoad);
GraphBuilder.AddPass(
RDG_EVENT_NAME("CompositePlanarReflections"),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, &View, this](FRHICommandListImmediate& RHICmdList)
{
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
// Blend over previous reflections in the output target (SSR or planar reflections that have already been rendered)
// Planar reflections win over SSR and reflection environment
//@todo - this is order dependent blending, but ordering is coming from registration order
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_InverseSourceAlpha, BO_Max, BF_One, BF_One>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
for (FPlanarReflectionSceneProxy* ReflectionSceneProxy : Scene->PlanarReflections)
{
if (!View.ViewFrustum.IntersectBox(ReflectionSceneProxy->WorldBounds.GetCenter(), ReflectionSceneProxy->WorldBounds.GetExtent()))
{
continue;
}
SCOPED_DRAW_EVENTF(RHICmdList, PlanarReflection, *ReflectionSceneProxy->OwnerName.ToString());
FDeferredLightVS::FPermutationDomain PermutationVector;
PermutationVector.Set<FDeferredLightVS::FRadialLight>(false);
TShaderMapRef<FDeferredLightVS> VertexShader(View.ShaderMap, PermutationVector);
TShaderMapRef<FPlanarReflectionPS> PixelShader(View.ShaderMap);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
FDeferredLightVS::FParameters ParametersVS = FDeferredLightVS::GetParameters(View);
SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), ParametersVS);
{
FPlanarReflectionUniformParameters PlanarReflectionUniformParameters;
SetupPlanarReflectionUniformParameters(View, ReflectionSceneProxy, PlanarReflectionUniformParameters);
FPlanarReflectionPS::FParameters ShaderParameters = *PassParameters;
ShaderParameters.PlanarReflectionParameters = CreateUniformBufferImmediate(PlanarReflectionUniformParameters, UniformBuffer_SingleDraw);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), ShaderParameters);
}
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Size(),
GetSceneTextureExtent(),
VertexShader,
EDRF_UseTriangleOptimization);
}
});
}