Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/SceneCaptureRendering.cpp
jason hoerner 00a17f45d1 Scene Capture: Fix for additive / composite blending. Need to skip render target clear when compositing.
#jira UE-151360
#rb mihnea.balta
#preflight 6290b3a9b974ae8eb0391726
#lockdown mihnea.balta

#ROBOMERGE-OWNER: jason.hoerner
#ROBOMERGE-AUTHOR: jason.hoerner
#ROBOMERGE-SOURCE: CL 20391763 in //UE5/Release-5.0/... via CL 20393665
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v949-20362246)

[CL 20398598 by jason hoerner in ue5-main branch]
2022-05-27 16:47:05 -04:00

1290 lines
46 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
=============================================================================*/
#include "SceneCaptureRendering.h"
#include "Containers/ArrayView.h"
#include "Misc/MemStack.h"
#include "EngineDefines.h"
#include "RHIDefinitions.h"
#include "RHI.h"
#include "RenderingThread.h"
#include "Engine/Scene.h"
#include "SceneInterface.h"
#include "LegacyScreenPercentageDriver.h"
#include "GameFramework/Actor.h"
#include "GameFramework/WorldSettings.h"
#include "RHIStaticStates.h"
#include "SceneView.h"
#include "Shader.h"
#include "TextureResource.h"
#include "SceneUtils.h"
#include "Components/PrimitiveComponent.h"
#include "Components/SceneCaptureComponent.h"
#include "Components/SceneCaptureComponent2D.h"
#include "Components/SceneCaptureComponentCube.h"
#include "Engine/TextureRenderTarget2D.h"
#include "Engine/TextureRenderTargetCube.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 "ScreenRendering.h"
#include "PipelineStateCache.h"
#include "RendererModule.h"
#include "Rendering/MotionVectorSimulation.h"
#include "SceneViewExtension.h"
#include "GenerateMips.h"
#include "RectLightTextureManager.h"
/** A pixel shader for capturing a component of the rendered scene for a scene capture.*/
class FSceneCapturePS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FSceneCapturePS);
SHADER_USE_PARAMETER_STRUCT(FSceneCapturePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View)
SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
enum class ESourceMode : uint32
{
ColorAndOpacity,
ColorNoAlpha,
ColorAndSceneDepth,
SceneDepth,
DeviceDepth,
Normal,
BaseColor,
MAX
};
class FSourceModeDimension : SHADER_PERMUTATION_ENUM_CLASS("SOURCE_MODE", ESourceMode);
class FEnable128BitRT : SHADER_PERMUTATION_BOOL("ENABLE_128_BIT");
using FPermutationDomain = TShaderPermutationDomain<FSourceModeDimension, FEnable128BitRT>;
static FPermutationDomain GetPermutationVector(ESceneCaptureSource CaptureSource, bool bUse128BitRT, bool bIsMobilePlatform)
{
ESourceMode SourceMode = ESourceMode::MAX;
switch (CaptureSource)
{
case SCS_SceneColorHDR:
SourceMode = ESourceMode::ColorAndOpacity;
break;
case SCS_SceneColorHDRNoAlpha:
SourceMode = ESourceMode::ColorNoAlpha;
break;
case SCS_SceneColorSceneDepth:
SourceMode = ESourceMode::ColorAndSceneDepth;
break;
case SCS_SceneDepth:
SourceMode = ESourceMode::SceneDepth;
break;
case SCS_DeviceDepth:
SourceMode = ESourceMode::DeviceDepth;
break;
case SCS_Normal:
SourceMode = ESourceMode::Normal;
break;
case SCS_BaseColor:
SourceMode = ESourceMode::BaseColor;
break;
default:
checkf(false, TEXT("SceneCaptureSource not implemented."));
}
if (bIsMobilePlatform && (SourceMode == ESourceMode::Normal || SourceMode == ESourceMode::BaseColor))
{
SourceMode = ESourceMode::ColorAndOpacity;
}
FPermutationDomain PermutationVector;
PermutationVector.Set<FSourceModeDimension>(SourceMode);
PermutationVector.Set<FEnable128BitRT>(bUse128BitRT);
return PermutationVector;
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
FPermutationDomain PermutationVector(Parameters.PermutationId);
auto SourceModeDim = PermutationVector.Get<FSourceModeDimension>();
bool bPlatformRequiresExplicit128bitRT = FDataDrivenShaderPlatformInfo::GetRequiresExplicit128bitRT(Parameters.Platform);
return (!PermutationVector.Get<FEnable128BitRT>() || bPlatformRequiresExplicit128bitRT) && (!IsMobilePlatform(Parameters.Platform) || (SourceModeDim != ESourceMode::Normal && SourceModeDim != ESourceMode::BaseColor));
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
static const TCHAR* ShaderSourceModeDefineName[] =
{
TEXT("SOURCE_MODE_SCENE_COLOR_AND_OPACITY"),
TEXT("SOURCE_MODE_SCENE_COLOR_NO_ALPHA"),
TEXT("SOURCE_MODE_SCENE_COLOR_SCENE_DEPTH"),
TEXT("SOURCE_MODE_SCENE_DEPTH"),
TEXT("SOURCE_MODE_DEVICE_DEPTH"),
TEXT("SOURCE_MODE_NORMAL"),
TEXT("SOURCE_MODE_BASE_COLOR")
};
static_assert(UE_ARRAY_COUNT(ShaderSourceModeDefineName) == (uint32)ESourceMode::MAX, "ESourceMode doesn't match define table.");
const FPermutationDomain PermutationVector(Parameters.PermutationId);
const uint32 SourceModeIndex = static_cast<uint32>(PermutationVector.Get<FSourceModeDimension>());
OutEnvironment.SetDefine(ShaderSourceModeDefineName[SourceModeIndex], 1u);
if (PermutationVector.Get<FEnable128BitRT>())
{
OutEnvironment.SetRenderTargetOutputFormat(0, PF_A32B32G32R32F);
}
}
};
IMPLEMENT_GLOBAL_SHADER(FSceneCapturePS, "/Engine/Private/SceneCapturePixelShader.usf", "Main", SF_Pixel);
class FODSCapturePS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FODSCapturePS);
SHADER_USE_PARAMETER_STRUCT(FODSCapturePS, FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_RDG_TEXTURE(TextureCube, LeftEyeTexture)
SHADER_PARAMETER_RDG_TEXTURE(TextureCube, RightEyeTexture)
SHADER_PARAMETER_SAMPLER(SamplerState, LeftEyeTextureSampler)
SHADER_PARAMETER_SAMPLER(SamplerState, RightEyeTextureSampler)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
};
IMPLEMENT_GLOBAL_SHADER(FODSCapturePS, "/Engine/Private/ODSCapture.usf", "MainPS", SF_Pixel);
static bool CaptureNeedsSceneColor(ESceneCaptureSource CaptureSource)
{
return CaptureSource != SCS_FinalColorLDR && CaptureSource != SCS_FinalColorHDR && CaptureSource != SCS_FinalToneCurveHDR;
}
static TFunction<void(FRHICommandList& RHICmdList)> CopyCaptureToTargetSetViewportFn = [](FRHICommandList& RHICmdList) {};
void CopySceneCaptureComponentToTarget(
FRDGBuilder& GraphBuilder,
const FMinimalSceneTextures& SceneTextures,
FRDGTextureRef ViewFamilyTexture,
const FSceneViewFamily& ViewFamily,
const TArrayView<FViewInfo>& Views,
bool bNeedsFlippedRenderTarget)
{
ESceneCaptureSource SceneCaptureSource = ViewFamily.SceneCaptureSource;
if (IsAnyForwardShadingEnabled(ViewFamily.GetShaderPlatform()) && (SceneCaptureSource == SCS_Normal || SceneCaptureSource == SCS_BaseColor))
{
SceneCaptureSource = SCS_SceneColorHDR;
}
if (CaptureNeedsSceneColor(SceneCaptureSource))
{
RDG_EVENT_SCOPE(GraphBuilder, "CaptureSceneComponent[%d]", SceneCaptureSource);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
if (SceneCaptureSource == SCS_SceneColorHDR && ViewFamily.SceneCaptureCompositeMode == SCCM_Composite)
{
// Blend with existing render target color. Scene capture color is already pre-multiplied by alpha.
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_SourceAlpha, BO_Add, BF_Zero, BF_SourceAlpha>::GetRHI();
}
else if (SceneCaptureSource == SCS_SceneColorHDR && ViewFamily.SceneCaptureCompositeMode == SCCM_Additive)
{
// Add to existing render target color. Scene capture color is already pre-multiplied by alpha.
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_Zero, BF_SourceAlpha>::GetRHI();
}
else
{
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
}
const bool bUse128BitRT = PlatformRequires128bitRT(ViewFamilyTexture->Desc.Format);
const FSceneCapturePS::FPermutationDomain PixelPermutationVector = FSceneCapturePS::GetPermutationVector(SceneCaptureSource, bUse128BitRT, IsMobilePlatform(ViewFamily.GetShaderPlatform()));
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
FSceneCapturePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FSceneCapturePS::FParameters>();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = SceneTextures.GetSceneTextureShaderParameters(ViewFamily.GetFeatureLevel());
PassParameters->RenderTargets[0] = FRenderTargetBinding(ViewFamilyTexture, ERenderTargetLoadAction::ENoAction);
TShaderMapRef<FScreenVS> VertexShader(View.ShaderMap);
TShaderMapRef<FSceneCapturePS> PixelShader(View.ShaderMap, PixelPermutationVector);
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
GraphBuilder.AddPass(
RDG_EVENT_NAME("View(%d)", ViewIndex),
PassParameters,
ERDGPassFlags::Raster,
[PassParameters, GraphicsPSOInit, VertexShader, PixelShader, &View, bNeedsFlippedRenderTarget] (FRHICommandList& RHICmdList)
{
FGraphicsPipelineStateInitializer LocalGraphicsPSOInit = GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(LocalGraphicsPSOInit);
SetGraphicsPipelineState(RHICmdList, LocalGraphicsPSOInit, 0);
SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters);
CopyCaptureToTargetSetViewportFn(RHICmdList);
if (bNeedsFlippedRenderTarget)
{
DrawRectangle(
RHICmdList,
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Height() - View.ViewRect.Min.Y,
View.ViewRect.Width(), -View.ViewRect.Height(),
View.UnconstrainedViewRect.Size(),
View.GetSceneTexturesConfig().Extent,
VertexShader,
EDRF_UseTriangleOptimization);
}
else
{
DrawRectangle(
RHICmdList,
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
View.UnconstrainedViewRect.Size(),
View.GetSceneTexturesConfig().Extent,
VertexShader,
EDRF_UseTriangleOptimization);
}
});
}
}
}
static void UpdateSceneCaptureContentDeferred_RenderThread(
FRHICommandListImmediate& RHICmdList,
FSceneRenderer* SceneRenderer,
FRenderTarget* RenderTarget,
FTexture* RenderTargetTexture,
const FString& EventName,
const FRHICopyTextureInfo& CopyInfo,
bool bGenerateMips,
const FGenerateMipsParams& GenerateMipsParams,
bool bClearRenderTarget,
bool bOrthographicCamera
)
{
// We need to execute the pre-render view extensions before we do any view dependent work.
FSceneRenderer::ViewExtensionPreRender_RenderThread(RHICmdList, SceneRenderer);
SceneRenderer->RenderThreadBegin(RHICmdList);
// update any resources that needed a deferred update
FDeferredUpdateResource::UpdateResources(RHICmdList);
{
const ERHIFeatureLevel::Type FeatureLevel = SceneRenderer->FeatureLevel;
#if WANTS_DRAW_MESH_EVENTS
SCOPED_DRAW_EVENTF(RHICmdList, SceneCapture, TEXT("SceneCapture %s"), *EventName);
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("SceneCapture %s", *EventName), FSceneRenderer::GetRDGParalelExecuteFlags(FeatureLevel));
#else
SCOPED_DRAW_EVENT(RHICmdList, UpdateSceneCaptureContent_RenderThread);
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("SceneCapture"), FSceneRenderer::GetRDGParalelExecuteFlags(FeatureLevel));
#endif
FRDGTextureRef TargetTexture = RegisterExternalTexture(GraphBuilder, RenderTarget->GetRenderTargetTexture(), TEXT("SceneCaptureTarget"));
FRDGTextureRef ShaderResourceTexture = RegisterExternalTexture(GraphBuilder, RenderTargetTexture->TextureRHI, TEXT("SceneCaptureTexture"));
if (bClearRenderTarget)
{
AddClearRenderTargetPass(GraphBuilder, TargetTexture, FLinearColor::Black, SceneRenderer->Views[0].UnscaledViewRect);
}
const FIntRect CopyDestRect = CopyInfo.GetDestRect();
if (!CopyDestRect.IsEmpty())
{
CopyCaptureToTargetSetViewportFn = [CopyDestRect](FRHICommandList& RHICmdList)
{
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
RHICmdList.SetViewport
(
float(CopyDestRect.Min.X),
float(CopyDestRect.Min.Y),
0.0f,
float(CopyDestRect.Max.X),
float(CopyDestRect.Max.Y),
1.0f
);
};
}
else
{
CopyCaptureToTargetSetViewportFn = [](FRHICommandList& RHICmdList) {};
}
// Disable occlusion queries when in orthographic mode
if (bOrthographicCamera)
{
FViewInfo& View = SceneRenderer->Views[0];
View.bDisableQuerySubmissions = true;
View.bIgnoreExistingQueries = true;
}
// Render the scene normally
{
RDG_RHI_EVENT_SCOPE(GraphBuilder, RenderScene);
SceneRenderer->Render(GraphBuilder);
}
if (bGenerateMips)
{
FGenerateMips::Execute(GraphBuilder, TargetTexture, GenerateMipsParams);
}
AddCopyTexturePass(GraphBuilder, TargetTexture, ShaderResourceTexture, CopyInfo);
GraphBuilder.Execute();
}
SceneRenderer->RenderThreadEnd(RHICmdList);
}
void UpdateSceneCaptureContentMobile_RenderThread(
FRHICommandListImmediate& RHICmdList,
FSceneRenderer* SceneRenderer,
FRenderTarget* RenderTarget,
FTexture* RenderTargetTexture,
const FString& EventName,
const FRHICopyTextureInfo& CopyInfo,
bool bGenerateMips,
const FGenerateMipsParams& GenerateMipsParams,
bool bDisableFlipCopyGLES)
{
// We need to execute the pre-render view extensions before we do any view dependent work.
FSceneRenderer::ViewExtensionPreRender_RenderThread(RHICmdList, SceneRenderer);
SceneRenderer->RenderThreadBegin(RHICmdList);
// update any resources that needed a deferred update
FDeferredUpdateResource::UpdateResources(RHICmdList);
bool bUseSceneTextures = SceneRenderer->ActiveViewFamily->SceneCaptureSource != SCS_FinalColorLDR &&
SceneRenderer->ActiveViewFamily->SceneCaptureSource != SCS_FinalColorHDR;
{
#if WANTS_DRAW_MESH_EVENTS
SCOPED_DRAW_EVENTF(RHICmdList, SceneCaptureMobile, TEXT("SceneCaptureMobile %s"), *EventName);
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("SceneCaptureMobile %s", *EventName));
#else
SCOPED_DRAW_EVENT(RHICmdList, UpdateSceneCaptureContentMobile_RenderThread);
FRDGBuilder GraphBuilder(RHICmdList, RDG_EVENT_NAME("SceneCaptureMobile"));
#endif
FViewInfo& View = SceneRenderer->Views[0];
const bool bIsMobileHDR = IsMobileHDR();
const bool bRHINeedsFlip = RHINeedsToSwitchVerticalAxis(GMaxRHIShaderPlatform) && !bDisableFlipCopyGLES;
// note that GLES code will flip the image when:
// bIsMobileHDR && SceneCaptureSource == SCS_FinalColorLDR (flip performed during post processing)
// !bIsMobileHDR (rendering is flipped by vertex shader)
// they need flipping again so it is correct for texture addressing.
const bool bNeedsFlippedCopy = (!bIsMobileHDR || !bUseSceneTextures) && bRHINeedsFlip;
const bool bNeedsFlippedFinalColor = bNeedsFlippedCopy && !bUseSceneTextures;
// Intermediate render target that will need to be flipped (needed on !IsMobileHDR())
FRDGTextureRef FlippedOutputTexture{};
const FRenderTarget* Target = SceneRenderer->ActiveViewFamily->RenderTarget;
if (bNeedsFlippedFinalColor)
{
// We need to use an intermediate render target since the result will be flipped
auto& RenderTargetRHI = Target->GetRenderTargetTexture();
FRDGTextureDesc Desc(FRDGTextureDesc::Create2D(
Target->GetSizeXY(),
RenderTargetRHI.GetReference()->GetFormat(),
RenderTargetRHI.GetReference()->GetClearBinding(),
TexCreate_RenderTargetable));
FlippedOutputTexture = GraphBuilder.CreateTexture(Desc, TEXT("SceneCaptureFlipped"));
}
// We don't support screen percentage in scene capture.
FIntRect ViewRect = View.UnscaledViewRect;
FIntRect UnconstrainedViewRect = View.UnconstrainedViewRect;
if (bNeedsFlippedFinalColor)
{
AddClearRenderTargetPass(GraphBuilder, FlippedOutputTexture, FLinearColor::Black, ViewRect);
}
const FIntRect CopyDestRect = CopyInfo.GetDestRect();
if (!CopyDestRect.IsEmpty())
{
CopyCaptureToTargetSetViewportFn = [CopyDestRect, bNeedsFlippedFinalColor, ViewRect, FlippedOutputTexture](FRHICommandList& RHICmdList)
{
RHICmdList.SetScissorRect(false, 0, 0, 0, 0);
if (bNeedsFlippedFinalColor)
{
FIntRect DestRect = CopyDestRect;
int32 TileYID = DestRect.Min.Y / ViewRect.Height();
int32 TileYCount = (FlippedOutputTexture->Desc.GetSize().Y / ViewRect.Height()) - 1;
DestRect.Min.Y = (TileYCount - TileYID) * ViewRect.Height();
DestRect.Max.Y = DestRect.Min.Y + ViewRect.Height();
RHICmdList.SetViewport
(
float(DestRect.Min.X),
float(DestRect.Min.Y),
0.0f,
float(DestRect.Max.X),
float(DestRect.Max.Y),
1.0f
);
}
else
{
RHICmdList.SetViewport
(
float(CopyDestRect.Min.X),
float(CopyDestRect.Min.Y),
0.0f,
float(CopyDestRect.Max.X),
float(CopyDestRect.Max.Y),
1.0f
);
}
};
}
else
{
CopyCaptureToTargetSetViewportFn = [](FRHICommandList& RHICmdList) {};
}
// Render the scene normally
{
RDG_RHI_EVENT_SCOPE(GraphBuilder, RenderScene);
if (bNeedsFlippedFinalColor)
{
// Helper class to allow setting render target
struct FRenderTargetOverride : public FRenderTarget
{
FRenderTargetOverride(const FRenderTarget* TargetIn, FRHITexture2D* OverrideTexture)
{
RenderTargetTextureRHI = OverrideTexture;
OriginalTarget = TargetIn;
}
virtual FIntPoint GetSizeXY() const override { return FIntPoint(RenderTargetTextureRHI->GetSizeX(), RenderTargetTextureRHI->GetSizeY()); }
virtual float GetDisplayGamma() const override { return OriginalTarget->GetDisplayGamma(); }
FTexture2DRHIRef GetTextureParamRef() { return RenderTargetTextureRHI; }
const FRenderTarget* OriginalTarget;
};
// Hijack the render target
FRHITexture2D* FlippedOutputTextureRHI = GraphBuilder.ConvertToExternalTexture(FlippedOutputTexture)->GetRHI()->GetTexture2D();
SceneRenderer->ActiveViewFamily->RenderTarget = GraphBuilder.AllocObject<FRenderTargetOverride>(Target, FlippedOutputTextureRHI); //-V506
}
SceneRenderer->Render(GraphBuilder);
if (bNeedsFlippedFinalColor)
{
// And restore it
SceneRenderer->ActiveViewFamily->RenderTarget = Target;
}
}
FRDGTextureRef OutputTexture = RegisterExternalTexture(GraphBuilder, Target->GetRenderTargetTexture(), TEXT("OutputTexture"));
const FMinimalSceneTextures& SceneTextures = SceneRenderer->GetActiveSceneTextures();
const FIntPoint TargetSize(UnconstrainedViewRect.Width(), UnconstrainedViewRect.Height());
{
// We need to flip this texture upside down (since we depended on tonemapping to fix this on the hdr path)
RDG_EVENT_SCOPE(GraphBuilder, "CaptureSceneColor");
CopySceneCaptureComponentToTarget(
GraphBuilder,
SceneTextures,
OutputTexture,
*SceneRenderer->ActiveViewFamily,
SceneRenderer->Views,
bNeedsFlippedFinalColor);
}
if (bGenerateMips)
{
FGenerateMips::Execute(GraphBuilder, OutputTexture, GenerateMipsParams);
}
GraphBuilder.Execute();
}
SceneRenderer->RenderThreadEnd(RHICmdList);
}
static void ODSCapture_RenderThread(
FRDGBuilder& GraphBuilder,
FRDGTextureRef LeftEyeTexture,
FRDGTextureRef RightEyeTexture,
FRDGTextureRef OutputTexture,
const ERHIFeatureLevel::Type FeatureLevel)
{
FODSCapturePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FODSCapturePS::FParameters>();
PassParameters->LeftEyeTexture = LeftEyeTexture;
PassParameters->RightEyeTexture = RightEyeTexture;
PassParameters->LeftEyeTextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
PassParameters->RightEyeTextureSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
PassParameters->RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::ELoad);
const auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
TShaderMapRef<FScreenVS> VertexShader(ShaderMap);
TShaderMapRef<FODSCapturePS> PixelShader(ShaderMap);
GraphBuilder.AddPass(
RDG_EVENT_NAME("ODSCapture"),
PassParameters,
ERDGPassFlags::Raster,
[VertexShader, PixelShader, OutputTexture](FRHICommandList& RHICmdList)
{
FGraphicsPipelineStateInitializer GraphicsPSOInit;
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
extern TGlobalResource<FFilterVertexDeclaration> GFilterVertexDeclaration;
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = PixelShader.GetPixelShader();
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit, 0);
const FIntPoint& TargetSize = OutputTexture->Desc.Extent;
RHICmdList.SetViewport(0, 0, 0.0f, TargetSize.X, TargetSize.Y, 1.0f);
DrawRectangle(
RHICmdList,
0, 0,
static_cast<float>(TargetSize.X), static_cast<float>(TargetSize.Y),
0, 0,
TargetSize.X, TargetSize.Y,
TargetSize,
TargetSize,
VertexShader,
EDRF_UseTriangleOptimization);
});
}
static void UpdateSceneCaptureContent_RenderThread(
FRHICommandListImmediate& RHICmdList,
FSceneRenderer* SceneRenderer,
FRenderTarget* RenderTarget,
FTexture* RenderTargetTexture,
const FString& EventName,
const FRHICopyTextureInfo& CopyInfo,
bool bGenerateMips,
const FGenerateMipsParams& GenerateMipsParams,
const bool bDisableFlipCopyLDRGLES,
bool bClearRenderTarget,
bool bOrthographicCamera)
{
FMaterialRenderProxy::UpdateDeferredCachedUniformExpressions();
switch (SceneRenderer->Scene->GetShadingPath())
{
case EShadingPath::Mobile:
{
UpdateSceneCaptureContentMobile_RenderThread(
RHICmdList,
SceneRenderer,
RenderTarget,
RenderTargetTexture,
EventName,
CopyInfo,
bGenerateMips,
GenerateMipsParams,
bDisableFlipCopyLDRGLES);
break;
}
case EShadingPath::Deferred:
{
UpdateSceneCaptureContentDeferred_RenderThread(
RHICmdList,
SceneRenderer,
RenderTarget,
RenderTargetTexture,
EventName,
CopyInfo,
bGenerateMips,
GenerateMipsParams,
bClearRenderTarget,
bOrthographicCamera);
break;
}
default:
checkNoEntry();
break;
}
RHICmdList.Transition(FRHITransitionInfo(RenderTargetTexture->TextureRHI, ERHIAccess::Unknown, ERHIAccess::SRVMask));
}
static void BuildOrthoMatrix(FIntPoint InRenderTargetSize, float InOrthoWidth, int32 InTileID, int32 InNumXTiles, int32 InNumYTiles, FMatrix& OutProjectionMatrix)
{
check((int32)ERHIZBuffer::IsInverted);
float const XAxisMultiplier = 1.0f;
float const YAxisMultiplier = InRenderTargetSize.X / float(InRenderTargetSize.Y);
const float OrthoWidth = InOrthoWidth / 2.0f;
const float OrthoHeight = InOrthoWidth / 2.0f * XAxisMultiplier / YAxisMultiplier;
const float NearPlane = 0;
const float FarPlane = WORLD_MAX / 8.0f;
const float ZScale = 1.0f / (FarPlane - NearPlane);
const float ZOffset = -NearPlane;
if (InTileID == -1)
{
OutProjectionMatrix = FReversedZOrthoMatrix(
OrthoWidth,
OrthoHeight,
ZScale,
ZOffset
);
return;
}
#if DO_CHECK
check(InNumXTiles != 0 && InNumYTiles != 0);
if (InNumXTiles == 0 || InNumYTiles == 0)
{
OutProjectionMatrix = FMatrix(EForceInit::ForceInitToZero);
return;
}
#endif
const float XTileDividerRcp = 1.0f / float(InNumXTiles);
const float YTileDividerRcp = 1.0f / float(InNumYTiles);
const float TileX = float(InTileID % InNumXTiles);
const float TileY = float(InTileID / InNumXTiles);
float l = -OrthoWidth + TileX * InOrthoWidth * XTileDividerRcp;
float r = l + InOrthoWidth * XTileDividerRcp;
float t = OrthoHeight - TileY * InOrthoWidth * YTileDividerRcp;
float b = t - InOrthoWidth * YTileDividerRcp;
OutProjectionMatrix = FMatrix(
FPlane(2.0f / (r-l), 0.0f, 0.0f, 0.0f),
FPlane(0.0f, 2.0f / (t-b), 0.0f, 0.0f),
FPlane(0.0f, 0.0f, -ZScale, 0.0f),
FPlane(-((r+l)/(r-l)), -((t+b)/(t-b)), 1.0f - ZOffset * ZScale, 1.0f)
);
}
void BuildProjectionMatrix(FIntPoint InRenderTargetSize, float InFOV, float InNearClippingPlane, FMatrix& OutProjectionMatrix)
{
float const XAxisMultiplier = 1.0f;
float const YAxisMultiplier = InRenderTargetSize.X / float(InRenderTargetSize.Y);
if ((int32)ERHIZBuffer::IsInverted)
{
OutProjectionMatrix = FReversedZPerspectiveMatrix(
InFOV,
InFOV,
XAxisMultiplier,
YAxisMultiplier,
InNearClippingPlane,
InNearClippingPlane
);
}
else
{
OutProjectionMatrix = FPerspectiveMatrix(
InFOV,
InFOV,
XAxisMultiplier,
YAxisMultiplier,
InNearClippingPlane,
InNearClippingPlane
);
}
}
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)
{
check(!ViewFamily.GetScreenPercentageInterface());
for (int32 ViewIndex = 0; ViewIndex < Views.Num(); ++ViewIndex)
{
const FSceneCaptureViewInfo& SceneCaptureViewInfo = Views[ViewIndex];
FSceneViewInitOptions ViewInitOptions;
ViewInitOptions.SetViewRectangle(SceneCaptureViewInfo.ViewRect);
ViewInitOptions.ViewFamily = &ViewFamily;
ViewInitOptions.ViewActor = ViewActor;
ViewInitOptions.ViewOrigin = SceneCaptureViewInfo.ViewLocation;
ViewInitOptions.ViewRotationMatrix = SceneCaptureViewInfo.ViewRotationMatrix;
ViewInitOptions.BackgroundColor = FLinearColor::Black;
ViewInitOptions.OverrideFarClippingPlaneDistance = MaxViewDistance;
ViewInitOptions.StereoPass = SceneCaptureViewInfo.StereoPass;
ViewInitOptions.SceneViewStateInterface = SceneCaptureComponent->GetViewState(ViewIndex);
ViewInitOptions.ProjectionMatrix = SceneCaptureViewInfo.ProjectionMatrix;
ViewInitOptions.LODDistanceFactor = FMath::Clamp(SceneCaptureComponent->LODDistanceFactor, .01f, 100.0f);
ViewInitOptions.bUseFauxOrthoViewPos = bUseFauxOrthoViewPos;
if (ViewFamily.Scene->GetWorld() != nullptr && ViewFamily.Scene->GetWorld()->GetWorldSettings() != nullptr)
{
ViewInitOptions.WorldToMetersScale = ViewFamily.Scene->GetWorld()->GetWorldSettings()->WorldToMeters;
}
ViewInitOptions.StereoIPD = SceneCaptureViewInfo.StereoIPD * (ViewInitOptions.WorldToMetersScale / 100.0f);
if (bCaptureSceneColor)
{
ViewFamily.EngineShowFlags.PostProcessing = 0;
ViewInitOptions.OverlayColor = FLinearColor::Black;
}
FSceneView* View = new FSceneView(ViewInitOptions);
View->bIsSceneCapture = true;
View->bSceneCaptureUsesRayTracing = SceneCaptureComponent->bUseRayTracingIfEnabled;
// Note: this has to be set before EndFinalPostprocessSettings
View->bIsPlanarReflection = bIsPlanarReflection;
// Needs to be reconfigured now that bIsPlanarReflection has changed.
View->SetupAntiAliasingMethod();
check(SceneCaptureComponent);
for (auto It = SceneCaptureComponent->HiddenComponents.CreateConstIterator(); It; ++It)
{
// If the primitive component was destroyed, the weak pointer will return NULL.
UPrimitiveComponent* PrimitiveComponent = It->Get();
if (PrimitiveComponent)
{
View->HiddenPrimitives.Add(PrimitiveComponent->ComponentId);
}
}
for (auto It = SceneCaptureComponent->HiddenActors.CreateConstIterator(); It; ++It)
{
AActor* Actor = *It;
if (Actor)
{
for (UActorComponent* Component : Actor->GetComponents())
{
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(Component))
{
View->HiddenPrimitives.Add(PrimComp->ComponentId);
}
}
}
}
if (SceneCaptureComponent->PrimitiveRenderMode == ESceneCapturePrimitiveRenderMode::PRM_UseShowOnlyList)
{
View->ShowOnlyPrimitives.Emplace();
for (auto It = SceneCaptureComponent->ShowOnlyComponents.CreateConstIterator(); It; ++It)
{
// If the primitive component was destroyed, the weak pointer will return NULL.
UPrimitiveComponent* PrimitiveComponent = It->Get();
if (PrimitiveComponent)
{
View->ShowOnlyPrimitives->Add(PrimitiveComponent->ComponentId);
}
}
for (auto It = SceneCaptureComponent->ShowOnlyActors.CreateConstIterator(); It; ++It)
{
AActor* Actor = *It;
if (Actor)
{
for (UActorComponent* Component : Actor->GetComponents())
{
if (UPrimitiveComponent* PrimComp = Cast<UPrimitiveComponent>(Component))
{
View->ShowOnlyPrimitives->Add(PrimComp->ComponentId);
}
}
}
}
}
else if (SceneCaptureComponent->ShowOnlyComponents.Num() > 0 || SceneCaptureComponent->ShowOnlyActors.Num() > 0)
{
static bool bWarned = false;
if (!bWarned)
{
UE_LOG(LogRenderer, Log, TEXT("Scene Capture has ShowOnlyComponents or ShowOnlyActors ignored by the PrimitiveRenderMode setting! %s"), *SceneCaptureComponent->GetPathName());
bWarned = true;
}
}
ViewFamily.Views.Add(View);
View->StartFinalPostprocessSettings(SceneCaptureViewInfo.ViewLocation);
View->OverridePostProcessSettings(*PostProcessSettings, PostProcessBlendWeight);
View->EndFinalPostprocessSettings(ViewInitOptions);
}
}
static FSceneRenderer* CreateSceneRendererForSceneCapture(
FScene* Scene,
USceneCaptureComponent* SceneCaptureComponent,
FRenderTarget* RenderTarget,
FIntPoint RenderTargetSize,
const FMatrix& ViewRotationMatrix,
const FVector& ViewLocation,
const FMatrix& ProjectionMatrix,
bool bUseFauxOrthoViewPos,
float MaxViewDistance,
bool bCaptureSceneColor,
FPostProcessSettings* PostProcessSettings,
float PostProcessBlendWeight,
const AActor* ViewActor,
const float StereoIPD = 0.0f)
{
FSceneCaptureViewInfo SceneCaptureViewInfo;
SceneCaptureViewInfo.ViewRotationMatrix = ViewRotationMatrix;
SceneCaptureViewInfo.ViewLocation = ViewLocation;
SceneCaptureViewInfo.ProjectionMatrix = ProjectionMatrix;
SceneCaptureViewInfo.StereoPass = EStereoscopicPass::eSSP_FULL;
SceneCaptureViewInfo.StereoViewIndex = INDEX_NONE;
SceneCaptureViewInfo.StereoIPD = StereoIPD;
SceneCaptureViewInfo.ViewRect = FIntRect(0, 0, RenderTargetSize.X, RenderTargetSize.Y);
FSceneViewFamilyContext ViewFamily(FSceneViewFamily::ConstructionValues(
RenderTarget,
Scene,
SceneCaptureComponent->ShowFlags)
.SetResolveScene(!bCaptureSceneColor)
.SetRealtimeUpdate(SceneCaptureComponent->bCaptureEveryFrame || SceneCaptureComponent->bAlwaysPersistRenderingState));
FSceneViewExtensionContext ViewExtensionContext(Scene);
ViewFamily.ViewExtensions = GEngine->ViewExtensions->GatherActiveExtensions(ViewExtensionContext);
SetupViewFamilyForSceneCapture(
ViewFamily,
SceneCaptureComponent,
MakeArrayView(&SceneCaptureViewInfo, 1),
MaxViewDistance,
bUseFauxOrthoViewPos,
bCaptureSceneColor,
/* bIsPlanarReflection = */ false,
PostProcessSettings,
PostProcessBlendWeight,
ViewActor);
// Screen percentage is still not supported in scene capture.
ViewFamily.EngineShowFlags.ScreenPercentage = false;
ViewFamily.SetScreenPercentageInterface(new FLegacyScreenPercentageDriver(
ViewFamily, /* GlobalResolutionFraction = */ 1.0f));
return FSceneRenderer::CreateSceneRenderer(&ViewFamily, nullptr);
}
void FScene::UpdateSceneCaptureContents(USceneCaptureComponent2D* CaptureComponent)
{
check(CaptureComponent);
if (UTextureRenderTarget2D* TextureRenderTarget = CaptureComponent->TextureTarget)
{
FTransform Transform = CaptureComponent->GetComponentToWorld();
FVector ViewLocation = Transform.GetTranslation();
// Remove the translation from Transform because we only need rotation.
Transform.SetTranslation(FVector::ZeroVector);
Transform.SetScale3D(FVector::OneVector);
FMatrix ViewRotationMatrix = Transform.ToInverseMatrixWithScale();
// swap axis st. x=z,y=x,z=y (unreal coord space) so that z is up
ViewRotationMatrix = ViewRotationMatrix * FMatrix(
FPlane(0, 0, 1, 0),
FPlane(1, 0, 0, 0),
FPlane(0, 1, 0, 0),
FPlane(0, 0, 0, 1));
const float FOV = CaptureComponent->FOVAngle * (float)PI / 360.0f;
FIntPoint CaptureSize(TextureRenderTarget->GetSurfaceWidth(), TextureRenderTarget->GetSurfaceHeight());
const bool bUseSceneColorTexture = CaptureNeedsSceneColor(CaptureComponent->CaptureSource);
const bool bEnableOrthographicTiling = (CaptureComponent->GetEnableOrthographicTiling() && CaptureComponent->ProjectionType == ECameraProjectionMode::Orthographic && bUseSceneColorTexture);
bool bUseFauxOrthoViewPos = false;
if (CaptureComponent->GetEnableOrthographicTiling() && CaptureComponent->ProjectionType == ECameraProjectionMode::Orthographic && !bUseSceneColorTexture)
{
UE_LOG(LogRenderer, Warning, TEXT("SceneCapture - Orthographic and tiling with CaptureSource not using SceneColor (i.e FinalColor) not compatible. SceneCapture render will not be tiled"));
}
const int32 TileID = CaptureComponent->TileID;
const int32 NumXTiles = CaptureComponent->GetNumXTiles();
const int32 NumYTiles = CaptureComponent->GetNumYTiles();
FMatrix ProjectionMatrix;
if (CaptureComponent->bUseCustomProjectionMatrix)
{
ProjectionMatrix = CaptureComponent->CustomProjectionMatrix;
}
else
{
if (CaptureComponent->ProjectionType == ECameraProjectionMode::Perspective)
{
const float ClippingPlane = (CaptureComponent->bOverride_CustomNearClippingPlane) ? CaptureComponent->CustomNearClippingPlane : GNearClippingPlane;
BuildProjectionMatrix(CaptureSize, FOV, ClippingPlane, ProjectionMatrix);
}
else
{
bUseFauxOrthoViewPos = CaptureComponent->bUseFauxOrthoViewPos;
if (bEnableOrthographicTiling)
{
BuildOrthoMatrix(CaptureSize, CaptureComponent->OrthoWidth, CaptureComponent->TileID, NumXTiles, NumYTiles, ProjectionMatrix);
CaptureSize /= FIntPoint(NumXTiles, NumYTiles);
}
else
{
BuildOrthoMatrix(CaptureSize, CaptureComponent->OrthoWidth, -1, 0, 0, ProjectionMatrix);
}
}
}
FSceneRenderer* SceneRenderer = CreateSceneRendererForSceneCapture(
this,
CaptureComponent,
TextureRenderTarget->GameThread_GetRenderTargetResource(),
CaptureSize,
ViewRotationMatrix,
ViewLocation,
ProjectionMatrix,
bUseFauxOrthoViewPos,
CaptureComponent->MaxViewDistanceOverride,
bUseSceneColorTexture,
&CaptureComponent->PostProcessSettings,
CaptureComponent->PostProcessBlendWeight,
CaptureComponent->GetViewOwner());
check(SceneRenderer != nullptr);
SceneRenderer->Views[0].bFogOnlyOnRenderedOpaque = CaptureComponent->bConsiderUnrenderedOpaquePixelAsFullyTranslucent;
SceneRenderer->ActiveViewFamily->SceneCaptureSource = CaptureComponent->CaptureSource;
SceneRenderer->ActiveViewFamily->SceneCaptureCompositeMode = CaptureComponent->CompositeMode;
// Ensure that the views for this scene capture reflect any simulated camera motion for this frame
TOptional<FTransform> PreviousTransform = FMotionVectorSimulation::Get().GetPreviousTransform(CaptureComponent);
// Process Scene View extensions for the capture component
{
FSceneViewExtensionContext ViewExtensionContext(SceneRenderer->Scene);
for (int32 Index = 0; Index < CaptureComponent->SceneViewExtensions.Num(); ++Index)
{
TSharedPtr<ISceneViewExtension, ESPMode::ThreadSafe> Extension = CaptureComponent->SceneViewExtensions[Index].Pin();
if (Extension.IsValid())
{
if (Extension->IsActiveThisFrame(ViewExtensionContext))
{
SceneRenderer->ActiveViewFamily->ViewExtensions.Add(Extension.ToSharedRef());
}
}
else
{
CaptureComponent->SceneViewExtensions.RemoveAt(Index, 1, false);
--Index;
}
}
for (const TSharedRef<ISceneViewExtension, ESPMode::ThreadSafe>& Extension : SceneRenderer->ActiveViewFamily->ViewExtensions)
{
Extension->SetupViewFamily(*SceneRenderer->ActiveViewFamily);
}
}
{
FPlane ClipPlane = FPlane(CaptureComponent->ClipPlaneBase, CaptureComponent->ClipPlaneNormal.GetSafeNormal());
for (FSceneView& View : SceneRenderer->Views)
{
if (PreviousTransform.IsSet())
{
View.PreviousViewTransform = PreviousTransform.GetValue();
}
View.bCameraCut = CaptureComponent->bCameraCutThisFrame;
if (CaptureComponent->bEnableClipPlane)
{
View.GlobalClippingPlane = ClipPlane;
// Jitter can't be removed completely due to the clipping plane
View.bAllowTemporalJitter = false;
}
for (const FSceneViewExtensionRef& Extension : SceneRenderer->ActiveViewFamily->ViewExtensions)
{
Extension->SetupView(*SceneRenderer->ActiveViewFamily, View);
}
}
}
// Reset scene capture's camera cut.
CaptureComponent->bCameraCutThisFrame = false;
FTextureRenderTargetResource* TextureRenderTargetResource = TextureRenderTarget->GameThread_GetRenderTargetResource();
FString EventName;
if (!CaptureComponent->ProfilingEventName.IsEmpty())
{
EventName = CaptureComponent->ProfilingEventName;
}
else if (CaptureComponent->GetOwner())
{
CaptureComponent->GetOwner()->GetFName().ToString(EventName);
}
const bool bGenerateMips = TextureRenderTarget->bAutoGenerateMips;
FGenerateMipsParams GenerateMipsParams{TextureRenderTarget->MipsSamplerFilter == TF_Nearest ? SF_Point : (TextureRenderTarget->MipsSamplerFilter == TF_Trilinear ? SF_Trilinear : SF_Bilinear),
TextureRenderTarget->MipsAddressU == TA_Wrap ? AM_Wrap : (TextureRenderTarget->MipsAddressU == TA_Mirror ? AM_Mirror : AM_Clamp),
TextureRenderTarget->MipsAddressV == TA_Wrap ? AM_Wrap : (TextureRenderTarget->MipsAddressV == TA_Mirror ? AM_Mirror : AM_Clamp)};
const bool bDisableFlipCopyGLES = CaptureComponent->bDisableFlipCopyGLES;
const bool bOrthographicCamera = CaptureComponent->ProjectionType == ECameraProjectionMode::Orthographic;
// If capturing every frame, only render to the GPUs that are actually being used
// this frame. Otherwise we will get poor performance in AFR. We can only determine
// this by querying the viewport back buffer on the render thread, so pass that
// along if it exists.
FRenderTarget* GameViewportRT = nullptr;
if (CaptureComponent->bCaptureEveryFrame)
{
if (GEngine->GameViewport != nullptr)
{
GameViewportRT = GEngine->GameViewport->Viewport;
}
}
UTexture* TexturePtrNotDeferenced = TextureRenderTarget;
// Compositing feature is only active when using SceneColor as the source
bool bIsCompositing = (CaptureComponent->CompositeMode != SCCM_Overwrite) && (CaptureComponent->CaptureSource == SCS_SceneColorHDR);
ENQUEUE_RENDER_COMMAND(CaptureCommand)(
[SceneRenderer, TextureRenderTargetResource, TexturePtrNotDeferenced, EventName, bGenerateMips, GenerateMipsParams, bDisableFlipCopyGLES, GameViewportRT, bEnableOrthographicTiling, bIsCompositing, bOrthographicCamera, NumXTiles, NumYTiles, TileID](FRHICommandListImmediate& RHICmdList)
{
if (GameViewportRT != nullptr)
{
const FRHIGPUMask GPUMask = AFRUtils::GetGPUMaskForGroup(GameViewportRT->GetGPUMask(RHICmdList));
TextureRenderTargetResource->SetActiveGPUMask(GPUMask);
}
else
{
TextureRenderTargetResource->SetActiveGPUMask(FRHIGPUMask::All());
}
FRHICopyTextureInfo CopyInfo;
if (bEnableOrthographicTiling)
{
const uint32 RTSizeX = TextureRenderTargetResource->GetSizeX() / NumXTiles;
const uint32 RTSizeY = TextureRenderTargetResource->GetSizeY() / NumYTiles;
const uint32 TileX = TileID % NumXTiles;
const uint32 TileY = TileID / NumXTiles;
CopyInfo.DestPosition.X = TileX * RTSizeX;
CopyInfo.DestPosition.Y = TileY * RTSizeY;
CopyInfo.Size.X = RTSizeX;
CopyInfo.Size.Y = RTSizeY;
}
RectLightAtlas::FAtlasTextureInvalidationScope Invalidation(TexturePtrNotDeferenced);
// Don't clear the render target when compositing, or in a tiling mode that fills in the render target in multiple passes.
bool bClearRenderTarget = !bIsCompositing && !bEnableOrthographicTiling;
UpdateSceneCaptureContent_RenderThread(RHICmdList, SceneRenderer, TextureRenderTargetResource, TextureRenderTargetResource, EventName, CopyInfo, bGenerateMips, GenerateMipsParams, bDisableFlipCopyGLES, bClearRenderTarget, bOrthographicCamera);
}
);
}
}
void FScene::UpdateSceneCaptureContents(USceneCaptureComponentCube* CaptureComponent)
{
struct FLocal
{
/** Creates a transformation for a cubemap face, following the D3D cubemap layout. */
static FMatrix CalcCubeFaceTransform(ECubeFace Face)
{
static const FVector XAxis(1.f, 0.f, 0.f);
static const FVector YAxis(0.f, 1.f, 0.f);
static const FVector ZAxis(0.f, 0.f, 1.f);
// vectors we will need for our basis
FVector vUp(YAxis);
FVector vDir;
switch (Face)
{
case CubeFace_PosX:
vDir = XAxis;
break;
case CubeFace_NegX:
vDir = -XAxis;
break;
case CubeFace_PosY:
vUp = -ZAxis;
vDir = YAxis;
break;
case CubeFace_NegY:
vUp = ZAxis;
vDir = -YAxis;
break;
case CubeFace_PosZ:
vDir = ZAxis;
break;
case CubeFace_NegZ:
vDir = -ZAxis;
break;
}
// derive right vector
FVector vRight(vUp ^ vDir);
// create matrix from the 3 axes
return FBasisVectorMatrix(vRight, vUp, vDir, FVector::ZeroVector);
}
} ;
check(CaptureComponent);
const bool bIsODS = CaptureComponent->TextureTargetLeft && CaptureComponent->TextureTargetRight && CaptureComponent->TextureTargetODS;
const uint32 StartIndex = (bIsODS) ? 1 : 0;
const uint32 EndIndex = (bIsODS) ? 3 : 1;
UTextureRenderTargetCube* const TextureTargets[] = {
CaptureComponent->TextureTarget,
CaptureComponent->TextureTargetLeft,
CaptureComponent->TextureTargetRight
};
FTransform Transform = CaptureComponent->GetComponentToWorld();
const FVector ViewLocation = Transform.GetTranslation();
if (CaptureComponent->bCaptureRotation)
{
// Remove the translation from Transform because we only need rotation.
Transform.SetTranslation(FVector::ZeroVector);
Transform.SetScale3D(FVector::OneVector);
}
for (uint32 CaptureIter = StartIndex; CaptureIter < EndIndex; ++CaptureIter)
{
UTextureRenderTargetCube* const TextureTarget = TextureTargets[CaptureIter];
if (TextureTarget)
{
const float FOV = 90 * (float)PI / 360.0f;
for (int32 faceidx = 0; faceidx < (int32)ECubeFace::CubeFace_MAX; faceidx++)
{
const ECubeFace TargetFace = (ECubeFace)faceidx;
const FVector Location = CaptureComponent->GetComponentToWorld().GetTranslation();
FMatrix ViewRotationMatrix;
if (CaptureComponent->bCaptureRotation)
{
ViewRotationMatrix = Transform.ToInverseMatrixWithScale() * FLocal::CalcCubeFaceTransform(TargetFace);
}
else
{
ViewRotationMatrix = FLocal::CalcCubeFaceTransform(TargetFace);
}
FIntPoint CaptureSize(TextureTarget->GetSurfaceWidth(), TextureTarget->GetSurfaceHeight());
FMatrix ProjectionMatrix;
BuildProjectionMatrix(CaptureSize, FOV, GNearClippingPlane, ProjectionMatrix);
FPostProcessSettings PostProcessSettings;
float StereoIPD = 0.0f;
if (bIsODS)
{
StereoIPD = (CaptureIter == 1) ? CaptureComponent->IPD * -0.5f : CaptureComponent->IPD * 0.5f;
}
bool bCaptureSceneColor = CaptureNeedsSceneColor(CaptureComponent->CaptureSource);
FSceneRenderer* SceneRenderer = CreateSceneRendererForSceneCapture(this, CaptureComponent,
TextureTarget->GameThread_GetRenderTargetResource(), CaptureSize, ViewRotationMatrix,
Location, ProjectionMatrix, false, CaptureComponent->MaxViewDistanceOverride,
bCaptureSceneColor, &PostProcessSettings, 0, CaptureComponent->GetViewOwner(), StereoIPD);
SceneRenderer->ActiveViewFamily->SceneCaptureSource = CaptureComponent->CaptureSource;
FTextureRenderTargetCubeResource* TextureRenderTarget = static_cast<FTextureRenderTargetCubeResource*>(TextureTarget->GameThread_GetRenderTargetResource());
FString EventName;
if (!CaptureComponent->ProfilingEventName.IsEmpty())
{
EventName = CaptureComponent->ProfilingEventName;
}
else if (CaptureComponent->GetOwner())
{
CaptureComponent->GetOwner()->GetFName().ToString(EventName);
}
ENQUEUE_RENDER_COMMAND(CaptureCommand)(
[SceneRenderer, TextureRenderTarget, EventName, TargetFace](FRHICommandListImmediate& RHICmdList)
{
FRHICopyTextureInfo CopyInfo;
CopyInfo.DestSliceIndex = TargetFace;
UpdateSceneCaptureContent_RenderThread(RHICmdList, SceneRenderer, TextureRenderTarget, TextureRenderTarget, EventName, CopyInfo, false, FGenerateMipsParams(), false, true, false);
}
);
}
}
}
if (bIsODS)
{
const FTextureRenderTargetCubeResource* const LeftEye = static_cast<FTextureRenderTargetCubeResource*>(CaptureComponent->TextureTargetLeft->GameThread_GetRenderTargetResource());
const FTextureRenderTargetCubeResource* const RightEye = static_cast<FTextureRenderTargetCubeResource*>(CaptureComponent->TextureTargetRight->GameThread_GetRenderTargetResource());
FTextureRenderTargetResource* const RenderTarget = CaptureComponent->TextureTargetODS->GameThread_GetRenderTargetResource();
const ERHIFeatureLevel::Type InFeatureLevel = FeatureLevel;
ENQUEUE_RENDER_COMMAND(ODSCaptureCommand)(
[LeftEye, RightEye, RenderTarget, InFeatureLevel](FRHICommandListImmediate& RHICmdList)
{
const ERHIAccess FinalAccess = ERHIAccess::RTV;
FMemMark MemMark(FMemStack::Get());
FRDGBuilder GraphBuilder(RHICmdList);
FRDGTextureRef OutputTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(RenderTarget->GetRenderTargetTexture(), TEXT("Output")));
FRDGTextureRef LeftEyeTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(LeftEye->TextureRHI, TEXT("LeftEye")));
FRDGTextureRef RightEyeTexture = GraphBuilder.RegisterExternalTexture(CreateRenderTarget(RightEye->TextureRHI, TEXT("RightEye")));
ODSCapture_RenderThread(GraphBuilder, LeftEyeTexture, RightEyeTexture, OutputTexture, InFeatureLevel);
GraphBuilder.SetTextureAccessFinal(LeftEyeTexture, FinalAccess);
GraphBuilder.SetTextureAccessFinal(RightEyeTexture, FinalAccess);
GraphBuilder.Execute();
});
}
}