You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
========================== MAJOR FEATURES + CHANGES ========================== Change 2975196 on 2016/05/12 by Robert.Manuszewski Garbage Collector will no longer be responsible for generating class token stream, instead the token stream will be generated on startup or when a class has finished loading. - This way we can avoid very long GC times after new blueprints have been loaded. - Temporarily enabled CLASS_TokenStreamAssembled check in development builds (for testing purposes) Change 2993960 on 2016/05/30 by Robert.Manuszewski Fixing leaked linkers created by blocking load requests during async loading. Change 2959398 on 2016/04/28 by Steve.Robb TMap references are strong and cannot be nulled by pending kill. This makes references in values strong too, even though we only really care about keys, which will corrupt the map when nulled. #jira UE-20828 Change 2960723 on 2016/04/29 by Graeme.Thornton Fix for texture asset import data being ignored when async loaded Change 2960938 on 2016/04/29 by Robert.Manuszewski Nulling out sql db handle after closing it. Change 2967127 on 2016/05/05 by Steve.Robb Move constructors explicitly disabled in generated code. Change 2967143 on 2016/05/05 by Steve.Robb Static analysis fixes: warning C6326: Potential comparison of a constant with another constant. Change 2967164 on 2016/05/05 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer Change 2968650 on 2016/05/06 by Steve.Robb Fix for HotReload copying module manager. Change 2968915 on 2016/05/06 by Robert.Manuszewski Fixing spelling of SetImageIntegrityStatus function name. Change 2970406 on 2016/05/09 by Steve.Robb Static analysis fixes: Function uses '...' bytes of stack: exceeds /analyze:stacksize '81940'. Consider moving some data to heap. Change 2970419 on 2016/05/09 by Steve.Robb Static analysis fixes: warning C6326: Potential comparison of a constant with another constant. warning C6011: Dereferencing NULL pointer '...'. warning C6385: Reading invalid data from '...': the readable size is '...' bytes, but '...' bytes may be read. warning C6386: Buffer overrun while writing to '...': the writable size is '...' bytes, but '...' bytes might be written. Change 2970431 on 2016/05/09 by Steve.Robb Static analysis fixes: warning C6299: Explicitly comparing a bit field to a Boolean type will yield unexpected results. Change 2972032 on 2016/05/10 by Steven.Hutton Workflow fixes to bugg / crashgroup filtering. Filters should now correctly persist across queries. Change 2972085 on 2016/05/10 by Steve.Robb Const-correctness fix for FLogCategoryBase::IsSuppressed. Change 2972087 on 2016/05/10 by Steve.Robb ELogVerbosity moved into its own header. Change 2972090 on 2016/05/10 by Steve.Robb Redundant ensure removed. Change 2972103 on 2016/05/10 by Steve.Robb Removal of redundant use of USING_CODE_ANALYSIS. Change 2972139 on 2016/05/10 by Steve.Robb Fix for ensure macros throwing C6326 warnings during static analysis. Change 2972147 on 2016/05/10 by Steve.Robb Fix for UE_LOG_ACTIVE macro throwing C6326 warnings during static analysis. Change 2972162 on 2016/05/10 by Steve.Robb SCOPE_CYCLE_COUNTER_GUARD removed. Change 2972168 on 2016/05/10 by Steve.Robb Compile error fix for logOrEnsureNanError in static analysis builds. Change 2973084 on 2016/05/10 by Chris.Wood Crash Report Server performance tweak Change 2974030 on 2016/05/11 by Steve.Robb Fix for IPropertyHandle::SetValue - used to take a non-const reference to a const UObject*, now it takes const references to both non-const and const UObject*. Change 2974053 on 2016/05/11 by Steve.Robb Static analysis fixes: warning C6326: Potential comparison of a constant with another constant. Change2974191on 2016/05/11 by Steve.Robb Fix for template instantiation error in VS2013. Change 2975298 on 2016/05/12 by Steve.Robb Static analysis fixes: warning C6236: (<expression> || <non-zero constant>) is always a non-zero constant. Change 2975318 on 2016/05/12 by Steve.Robb Fix for hot reload info being reported as warnings. #jira UE-30586 Change 2975447 on 2016/05/12 by Steve.Robb Static analysis fixes: warning C6235: (<non-zero constant> || <expression>) is always a non-zero constant. warning C6239: (<non-zero constant> && <expression>) always evaluates to the result of <expression>. Did you intend to use the bitwise-and operator? warning C6240: (<expression> && <non-zero constant>) always evaluates to the result of <expression>. Did you intend to use the bitwise-and operator? warning C6285: (<non-zero constant> || <non-zero constant>) is always a non-zero constant. Did you intend to use the bitwise-and operator? warning C6286: (<non-zero constant> || <expression>) is always a non-zero constant. <expression> is never evaluated and might have side effects. warning C6289: Incorrect operator: mutual exclusion over || is always a non-zero constant. Did you intend to use && instead? warning C6316: Incorrect operator: tested expression is constant and non-zero. Use bitwise-and to determine whether bits are set. Change 2975478 on 2016/05/12 by Steve.Robb Static analysis fixes for lots of redundant <zero constant> and <non-zero constant> warnings. Change 2975538 on 2016/05/12 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'StaticResource' Change 2976640 on 2016/05/13 by Robert.Manuszewski Fixing crashes caused by token stream generation changes. Making sure the token stream gets re-generated when a class gets re-linked. #jira UE-30675 Change 2978320 on 2016/05/16 by Steve.Robb Fix for static analysis warnings in XNA headers. Change 2978329 on 2016/05/16 by Steve.Robb Static analysis fixes: warning C6334: sizeof operator applied to an expression with an operator might yield unexpected results: Parentheses can be used to disambiguate certain usages. Change 2980222 on 2016/05/17 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'X'. warning C28182: Dereferencing NULL pointer. 'X' contains the same NULL value as 'Y' did. Change 2980458 on 2016/05/17 by Chris.Wood Attempt to fix crash report submission problems from CRP to CR website [UE-30257] - Crashreports are sometimes missing file attachments Passing crash GUID so that website can easily check for duplicates in future Increased request timeout for AddCrash to be longer than website database timeout Logging retries for future visibility CRP v.1.1.6 Change 2980639 on 2016/05/17 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'X'. warning C28182: Dereferencing NULL pointer. 'X' contains the same NULL value as 'Y' did. Change 2981750 on 2016/05/18 by Steve.Robb check()s in ContainerAllocationPolicies.h changed to checkSlow()s, as they only exist to check that the container has been written correctly. Change 2982106 on 2016/05/18 by John.Mahoney Fixed a crash caused by loading two stat capture files simultaneously in the profiler. If the user tries to load a capture file while another load is in progress, the previous load is now cancelled and cleaned up before proceeding with the new load. Made the delegates in FNewStatsReader explicitly specify which profiler instance they are loading data for, instead of relying on the current value of LoadConnection->InstanceId. This also fixes a crash that occurs when selecting a different capture file in the "Stats dump browser" pane of the profiler (after using Load Folder) while another file is still loading. Cleaned up some weak pointer usage in the profiler window. #jira UE-30741 Change 2983366 on 2016/05/19 by Steven.Hutton Changes for passing crash type directly from CRP to CRW. Change 2983394 on 2016/05/19 by Steven.Hutton Minor changes to add crash with more error reporting Change 2984685 on 2016/05/20 by Robert.Manuszewski Merging //UE4/Dev-Main @ 2984626 to Dev-Core (//UE4/Dev-Core) Change 2985143 on 2016/05/20 by Steve.Robb Missing semi-colons. Change 2986463 on 2016/05/23 by Steve.Robb CopyTemp added to make it clear that you want to make a copy (rather than a move, or an accidental copy) at the call site of a function taking rvalue refs. Change 2986475 on 2016/05/23 by Steve.Robb Static analysis fixes: warning C6313: Incorrect operator: zero-valued flag cannot be tested with bitwise-and. Change 2986476 on 2016/05/23 by Steve.Robb Static analysis fixes: warning C6313: Incorrect operator: zero-valued flag cannot be tested with bitwise-and. Change 2986480 on 2016/05/23 by Steve.Robb Static analysis fixes: warning C6326: Potential comparison of a constant with another constant Change 2986515 on 2016/05/23 by Steve.Robb Static analysis fixes: warning C6340: Mismatch on sign: 'X' passed as _Param_(N) when some unsigned type is required in call to 'Func' Change 2986680 on 2016/05/23 by Steve.Robb Static analysis fixes: warning C6386: Buffer overrun while writing to 'Ptr': the writable size is 'X' bytes, but 'Y' bytes might be written. warning C6387: 'Ptr' could be '0': this does not adhere to the specification for the function 'Func' warning C6031: Return value ignored: 'snprintf'. warning C6340: Mismatch on sign: 'const unsigned int' passed as _Param_(4) when some signed type is required in call to 'snprintf'. Change 2986865 on 2016/05/23 by Robert.Manuszewski Removing redundand AddReferencedObjects functions Change 2987968 on 2016/05/24 by Robert.Manuszewski Removing redundant UPROPERTY macros from intrinsic classes. Change 2987979 on 2016/05/24 by Steve.Robb Optimization of some FString and FPaths operations to produce fewer temporaries. Change 2988297 on 2016/05/24 by Steve.Robb Static analysis fixes: warning C6287: Redundant code: the left and right sub-expressions are identical. Change 2988430 on 2016/05/24 by Steve.Robb Static analysis fixes: warning C6385: Reading invalid data from 'var': the readable size is 'X' bytes, but 'Y' bytes may be read. Change2988461on 2016/05/24 by Steve.Robb Static analysis fixes: warning C6235: (<non-zero constant> || <expression>) is always a non-zero constant. warning C6239: (<non-zero constant> && <expression>) always evaluates to the result of <expression>. warning C6240: (<expression> && <non-zero constant>) always evaluates to the result of <expression>. Change 2988464 on 2016/05/24 by Steve.Robb Static analysis fixes: warning C6262: Function uses 'X' bytes of stack: exceeds /analyze:stacksize 'Y'. Consider moving some data to heap. Change 2988494 on 2016/05/24 by Steve.Robb Static analysis fixes: warning C6237: (<zero> && <expression>) is always zero. <expression> is never evaluated and might have side effects. Change 2989411 on 2016/05/25 by Robert.Manuszewski Splitting GC cluster index and intenral object flags to allow more UObjects in editor builds. Change 2989429 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6387: '_Param_(X)' could be '0': this does not adhere to the specification for the function 'Func'. Change 2989982 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6001: Using uninitialized memory 'LODPlanesMin'. Change 2990018 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6386: Buffer overrun while writing to 'X' Change 2990077 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6240: (<expression> && <non-zero constant>) always evaluates to the result of <expression>. warning C6011: Dereferencing NULL pointer 'Ptr'. Change 2990114 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6336: Arithmetic operator has precedence over question operator, use parentheses to clarify intent. Change 2990125 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6239: (<non-zero constant> && <expression>) always evaluates to the result of <expression>. Change 2990162 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C6294: Ill-defined for-loop: initial condition does not satisfy test. Loop body not executed. Change 2990193 on 2016/05/25 by Steve.Robb Static analysis fixes: warning C28182: Dereferencing NULL pointer. 'type' contains the same NULL value as 'type->base_type' did. warning C6011: Dereferencing NULL pointer 'Semantic'. Change 2991006 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C28113: Accessing a local variable dummy via an Interlocked function: This is an unusual usage which could be reconsidered. Change 2991012 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6031: Return value ignored: 'InitializeCriticalSectionAndSpinCount'. Change 2991013 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6287: Redundant code: the left and right sub-expressions are identical. Change 2991016 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6236: (<expression> || <non-zero constant>) is always a non-zero constant. Change 2991017 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6326: Potential comparison of a constant with another constant. Change 2991019 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6292: Ill-defined for-loop: counts up from maximum. Change 2991023 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6322: Empty _except block. warning C28251: Inconsistent annotation for 'WinMain': this instance has no annotations. Change 2991070 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C28182: Dereferencing NULL pointer. 'Ptr1' contains the same NULL value as 'Ptr2' did. Change 2991416 on 2016/05/26 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Ptr'. Change 2992738 on 2016/05/27 by Steve.Robb Revert changes to FString::MatchesWildcard. Change 2992916 on 2016/05/27 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Ptr'. Change 2992960 on 2016/05/27 by Chris.Wood Optimized P4 access in Crash Report Process and MinidumpDiagostics. Change 2992964 on 2016/05/27 by Steve.Robb Static analysis fixes: warning C6011: Dereferencing NULL pointer 'Ptr'. Change 2993956 on 2016/05/30 by Robert.Manuszewski Fixing a crash after adding a new C++ class in the editor - made sure new classes have the token stream assembled after hot-reload. #jira UE-31309 Change 2993977 on 2016/05/30 by Robert.Manuszewski Don't wait for all packages to finish loading before PostLoading those which already have. Change 2994206 on 2016/05/31 by Robert.Manuszewski PR #2429: Three bug fixes required for script support to work properly (Contributed by pluranium) #lockdown Nick.Penwarden [CL 2996251 by Robert Manuszewski in Main branch]
1505 lines
57 KiB
C++
1505 lines
57 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
Functionality for capturing the scene into reflection capture cubemaps, and prefiltering
|
|
=============================================================================*/
|
|
|
|
#include "RendererPrivate.h"
|
|
#include "ScenePrivate.h"
|
|
#include "SceneFilterRendering.h"
|
|
#include "PostProcessing.h"
|
|
#include "UniformBuffer.h"
|
|
#include "ShaderParameters.h"
|
|
#include "ScreenRendering.h"
|
|
#include "ReflectionEnvironment.h"
|
|
#include "ReflectionEnvironmentCapture.h"
|
|
#include "SceneUtils.h"
|
|
|
|
/** Near plane to use when capturing the scene. */
|
|
float GReflectionCaptureNearPlane = 5;
|
|
|
|
int32 GSupersampleCaptureFactor = 1;
|
|
|
|
/**
|
|
* Mip map used by a Roughness of 0, counting down from the lowest resolution mip (MipCount - 1).
|
|
* This has been tweaked along with ReflectionCaptureRoughnessMipScale to make good use of the resolution in each mip, especially the highest resolution mips.
|
|
* This value is duplicated in ReflectionEnvironmentShared.usf!
|
|
*/
|
|
float ReflectionCaptureRoughestMip = 1;
|
|
|
|
/**
|
|
* Scales the log2 of Roughness when computing which mip to use for a given roughness.
|
|
* Larger values make the higher resolution mips sharper.
|
|
* This has been tweaked along with ReflectionCaptureRoughnessMipScale to make good use of the resolution in each mip, especially the highest resolution mips.
|
|
* This value is duplicated in ReflectionEnvironmentShared.usf!
|
|
*/
|
|
float ReflectionCaptureRoughnessMipScale = 1.2f;
|
|
|
|
int32 GDiffuseIrradianceCubemapSize = 32;
|
|
|
|
extern ENGINE_API TAutoConsoleVariable<int32> CVarReflectionCaptureSize;
|
|
|
|
void OnUpdateReflectionCaptures( UWorld* InWorld )
|
|
{
|
|
InWorld->UpdateAllReflectionCaptures();
|
|
}
|
|
|
|
FAutoConsoleCommandWithWorld CaptureConsoleCommand(
|
|
TEXT("r.ReflectionCapture"),
|
|
TEXT("Updates all reflection captures"),
|
|
FConsoleCommandWithWorldDelegate::CreateStatic(OnUpdateReflectionCaptures)
|
|
);
|
|
|
|
|
|
/** Encapsulates render target picking logic for cubemap mip generation. */
|
|
FSceneRenderTargetItem& GetEffectiveRenderTarget(FSceneRenderTargets& SceneContext, bool bDownsamplePass, int32 TargetMipIndex)
|
|
{
|
|
int32 ScratchTextureIndex = TargetMipIndex % 2;
|
|
|
|
if (!bDownsamplePass)
|
|
{
|
|
ScratchTextureIndex = 1 - ScratchTextureIndex;
|
|
}
|
|
|
|
return SceneContext.ReflectionColorScratchCubemap[ScratchTextureIndex]->GetRenderTargetItem();
|
|
}
|
|
|
|
/** Encapsulates source texture picking logic for cubemap mip generation. */
|
|
FSceneRenderTargetItem& GetEffectiveSourceTexture(FSceneRenderTargets& SceneContext, bool bDownsamplePass, int32 TargetMipIndex)
|
|
{
|
|
int32 ScratchTextureIndex = TargetMipIndex % 2;
|
|
|
|
if (bDownsamplePass)
|
|
{
|
|
ScratchTextureIndex = 1 - ScratchTextureIndex;
|
|
}
|
|
|
|
return SceneContext.ReflectionColorScratchCubemap[ScratchTextureIndex]->GetRenderTargetItem();
|
|
}
|
|
|
|
void FullyResolveReflectionScratchCubes(FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
FTextureRHIRef& Scratch0 = SceneContext.ReflectionColorScratchCubemap[0]->GetRenderTargetItem().TargetableTexture;
|
|
FTextureRHIRef& Scratch1 = SceneContext.ReflectionColorScratchCubemap[1]->GetRenderTargetItem().TargetableTexture;
|
|
FResolveParams ResolveParams(FResolveRect(), CubeFace_PosX, -1, -1, -1);
|
|
RHICmdList.CopyToResolveTarget(Scratch0, Scratch0, true, ResolveParams);
|
|
RHICmdList.CopyToResolveTarget(Scratch1, Scratch1, true, ResolveParams);
|
|
}
|
|
|
|
class FDownsamplePS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FDownsamplePS,Global);
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FDownsamplePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FGlobalShader(Initializer)
|
|
{
|
|
CubeFace.Bind(Initializer.ParameterMap,TEXT("CubeFace"));
|
|
SourceMipIndex.Bind(Initializer.ParameterMap,TEXT("SourceMipIndex"));
|
|
SourceTexture.Bind(Initializer.ParameterMap,TEXT("SourceTexture"));
|
|
SourceTextureSampler.Bind(Initializer.ParameterMap,TEXT("SourceTextureSampler"));
|
|
}
|
|
FDownsamplePS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, int32 CubeFaceValue, int32 SourceMipIndexValue, FSceneRenderTargetItem& SourceTextureValue)
|
|
{
|
|
SetShaderValue(RHICmdList, GetPixelShader(), CubeFace, CubeFaceValue);
|
|
SetShaderValue(RHICmdList, GetPixelShader(), SourceMipIndex, SourceMipIndexValue);
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
GetPixelShader(),
|
|
SourceTexture,
|
|
SourceTextureSampler,
|
|
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
|
|
SourceTextureValue.ShaderResourceTexture);
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
Ar << CubeFace;
|
|
Ar << SourceMipIndex;
|
|
Ar << SourceTexture;
|
|
Ar << SourceTextureSampler;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
FShaderParameter CubeFace;
|
|
FShaderParameter SourceMipIndex;
|
|
FShaderResourceParameter SourceTexture;
|
|
FShaderResourceParameter SourceTextureSampler;
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FDownsamplePS,TEXT("ReflectionEnvironmentShaders"),TEXT("DownsamplePS"),SF_Pixel);
|
|
|
|
|
|
/** Pixel shader used for filtering a mip. */
|
|
class FCubeFilterPS : public FDownsamplePS
|
|
{
|
|
DECLARE_SHADER_TYPE(FCubeFilterPS,Global);
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FDownsamplePS::ModifyCompilationEnvironment(Platform, OutEnvironment);
|
|
}
|
|
|
|
FCubeFilterPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FDownsamplePS(Initializer)
|
|
{
|
|
AverageBrightnessTexture.Bind(Initializer.ParameterMap,TEXT("AverageBrightnessTexture"));
|
|
AverageBrightnessSampler.Bind(Initializer.ParameterMap,TEXT("AverageBrightnessSampler"));
|
|
CubemapMaxMipParameter.Bind(Initializer.ParameterMap, TEXT("CubemapMaxMip"));
|
|
}
|
|
FCubeFilterPS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, int32 NumMips, int32 CubeFaceValue, int32 SourceMipIndexValue, FSceneRenderTargetItem& SourceTextureValue)
|
|
{
|
|
FDownsamplePS::SetParameters(RHICmdList, CubeFaceValue, SourceMipIndexValue, SourceTextureValue);
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
GetPixelShader(),
|
|
AverageBrightnessTexture,
|
|
AverageBrightnessSampler,
|
|
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
|
|
FSceneRenderTargets::Get(RHICmdList).GetReflectionBrightnessTarget()->GetRenderTargetItem().ShaderResourceTexture);
|
|
|
|
SetShaderValue(RHICmdList, GetPixelShader(), CubemapMaxMipParameter, NumMips - 1.0f);
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FDownsamplePS::Serialize(Ar);
|
|
Ar << AverageBrightnessTexture;
|
|
Ar << AverageBrightnessSampler;
|
|
Ar << CubemapMaxMipParameter;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
FShaderResourceParameter AverageBrightnessTexture;
|
|
FShaderResourceParameter AverageBrightnessSampler;
|
|
FShaderParameter CubemapMaxMipParameter;
|
|
};
|
|
|
|
template< uint32 bNormalize >
|
|
class TCubeFilterPS : public FCubeFilterPS
|
|
{
|
|
DECLARE_SHADER_TYPE(TCubeFilterPS,Global);
|
|
|
|
public:
|
|
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FCubeFilterPS::ModifyCompilationEnvironment(Platform, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("NORMALIZE"), bNormalize);
|
|
}
|
|
|
|
TCubeFilterPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FCubeFilterPS(Initializer)
|
|
{}
|
|
|
|
TCubeFilterPS() {}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>,TCubeFilterPS<0>,TEXT("ReflectionEnvironmentShaders"),TEXT("FilterPS"),SF_Pixel);
|
|
IMPLEMENT_SHADER_TYPE(template<>,TCubeFilterPS<1>,TEXT("ReflectionEnvironmentShaders"),TEXT("FilterPS"),SF_Pixel);
|
|
|
|
static FGlobalBoundShaderState DownsampleBoundShaderState;
|
|
|
|
/** Computes the average brightness of a 1x1 mip of a cubemap. */
|
|
class FComputeBrightnessPS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FComputeBrightnessPS,Global)
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
|
|
OutEnvironment.SetDefine(TEXT("COMPUTEBRIGHTNESS_PIXELSHADER"), 1);
|
|
OutEnvironment.SetRenderTargetOutputFormat(0, PF_R32_FLOAT);
|
|
}
|
|
|
|
FComputeBrightnessPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
: FGlobalShader(Initializer)
|
|
{
|
|
ReflectionEnvironmentColorTexture.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvironmentColorTexture"));
|
|
ReflectionEnvironmentColorSampler.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvironmentColorSampler"));
|
|
NumCaptureArrayMips.Bind(Initializer.ParameterMap, TEXT("NumCaptureArrayMips"));
|
|
}
|
|
|
|
FComputeBrightnessPS()
|
|
{
|
|
}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, int32 TargetSize)
|
|
{
|
|
const int32 EffectiveTopMipSize = TargetSize;
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
// Read from the smallest mip that was downsampled to
|
|
FSceneRenderTargetItem& Cubemap = GetEffectiveRenderTarget(FSceneRenderTargets::Get(RHICmdList), true, NumMips - 1);
|
|
|
|
if (Cubemap.IsValid())
|
|
{
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
GetPixelShader(),
|
|
ReflectionEnvironmentColorTexture,
|
|
ReflectionEnvironmentColorSampler,
|
|
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
|
|
Cubemap.ShaderResourceTexture);
|
|
}
|
|
|
|
SetShaderValue(RHICmdList, GetPixelShader(), NumCaptureArrayMips, FMath::CeilLogTwo(TargetSize) + 1);
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
Ar << ReflectionEnvironmentColorTexture;
|
|
Ar << ReflectionEnvironmentColorSampler;
|
|
Ar << NumCaptureArrayMips;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
|
|
FShaderResourceParameter ReflectionEnvironmentColorTexture;
|
|
FShaderResourceParameter ReflectionEnvironmentColorSampler;
|
|
FShaderParameter NumCaptureArrayMips;
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FComputeBrightnessPS,TEXT("ReflectionEnvironmentShaders"),TEXT("ComputeBrightnessMain"),SF_Pixel);
|
|
|
|
/** Computes the average brightness of the given reflection capture and stores it in the scene. */
|
|
void ComputeAverageBrightness(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, int32 TargetSize)
|
|
{
|
|
FTextureRHIRef& BrightnessTarget = FSceneRenderTargets::Get(RHICmdList).GetReflectionBrightnessTarget()->GetRenderTargetItem().TargetableTexture;
|
|
SetRenderTarget(RHICmdList, BrightnessTarget, NULL, true);
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
TShaderMapRef<FPostProcessVS> VertexShader(ShaderMap);
|
|
TShaderMapRef<FComputeBrightnessPS> PixelShader(ShaderMap);
|
|
|
|
static FGlobalBoundShaderState BoundShaderState;
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
|
|
|
|
PixelShader->SetParameters(RHICmdList, TargetSize);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
0, 0,
|
|
1, 1,
|
|
0, 0,
|
|
1, 1,
|
|
FIntPoint(1, 1),
|
|
FIntPoint(1, 1),
|
|
*VertexShader);
|
|
|
|
RHICmdList.CopyToResolveTarget(BrightnessTarget, BrightnessTarget, true, FResolveParams());
|
|
}
|
|
|
|
/** Generates mips for glossiness and filters the cubemap for a given reflection. */
|
|
void FilterReflectionEnvironment(FRHICommandListImmediate& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, int32 CubmapSize, FSHVectorRGB3* OutIrradianceEnvironmentMap, bool bNormalize)
|
|
{
|
|
const int32 EffectiveTopMipSize = CubmapSize;
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
|
|
int32 DiffuseConvolutionSourceMip = INDEX_NONE;
|
|
FSceneRenderTargetItem* DiffuseConvolutionSource = NULL;
|
|
|
|
static const auto CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DiffuseFromCaptures"));
|
|
bNormalize = bNormalize && CVar->GetInt() == 0;
|
|
|
|
// necessary to resolve the clears which touched all the mips. scene rendering only resolves mip 0.
|
|
FullyResolveReflectionScratchCubes(RHICmdList);
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, DownsampleCubeMips);
|
|
// Downsample all the mips, each one reads from the mip above it
|
|
for (int32 MipIndex = 1; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, DownsampleCubeMip);
|
|
const int32 SourceMipIndex = FMath::Max(MipIndex - 1, 0);
|
|
const int32 MipSize = 1 << (NumMips - MipIndex - 1);
|
|
|
|
FSceneRenderTargetItem& EffectiveRT = GetEffectiveRenderTarget(SceneContext, true, MipIndex);
|
|
FSceneRenderTargetItem& EffectiveSource = GetEffectiveSourceTexture(SceneContext, true, MipIndex);
|
|
check(EffectiveRT.TargetableTexture != EffectiveSource.ShaderResourceTexture);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
SetRenderTarget(RHICmdList, EffectiveRT.TargetableTexture, MipIndex, CubeFace, NULL, true);
|
|
|
|
const FIntRect ViewRect(0, 0, MipSize, MipSize);
|
|
RHICmdList.SetViewport(0, 0, 0.0f, MipSize, MipSize, 1.0f);
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
TShaderMapRef<FScreenVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
|
|
TShaderMapRef<FDownsamplePS> PixelShader(GetGlobalShaderMap(FeatureLevel));
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, DownsampleBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
|
|
|
|
PixelShader->SetParameters(RHICmdList, CubeFace, SourceMipIndex, EffectiveSource);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
FIntPoint(ViewRect.Width(), ViewRect.Height()),
|
|
FIntPoint(MipSize, MipSize),
|
|
*VertexShader);
|
|
|
|
RHICmdList.CopyToResolveTarget(EffectiveRT.TargetableTexture, EffectiveRT.ShaderResourceTexture, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace, MipIndex));
|
|
}
|
|
|
|
if (MipSize == GDiffuseIrradianceCubemapSize)
|
|
{
|
|
DiffuseConvolutionSourceMip = MipIndex;
|
|
DiffuseConvolutionSource = &EffectiveRT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (OutIrradianceEnvironmentMap)
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, ComputeDiffuseIrradiance);
|
|
check(DiffuseConvolutionSource != NULL);
|
|
ComputeDiffuseIrradiance(RHICmdList, FeatureLevel, DiffuseConvolutionSource->ShaderResourceTexture, DiffuseConvolutionSourceMip, OutIrradianceEnvironmentMap);
|
|
}
|
|
|
|
ComputeAverageBrightness(RHICmdList, FeatureLevel, CubmapSize);
|
|
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, FilterCubeMap);
|
|
// Filter all the mips, each one reads from whichever scratch render target holds the downsampled contents, and writes to the destination cubemap
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, FilterCubeMip);
|
|
FSceneRenderTargetItem& EffectiveRT = GetEffectiveRenderTarget(SceneContext, false, MipIndex);
|
|
FSceneRenderTargetItem& EffectiveSource = GetEffectiveSourceTexture(SceneContext, false, MipIndex);
|
|
check(EffectiveRT.TargetableTexture != EffectiveSource.ShaderResourceTexture);
|
|
const int32 MipSize = 1 << (NumMips - MipIndex - 1);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
SetRenderTarget(RHICmdList, EffectiveRT.TargetableTexture, MipIndex, CubeFace, NULL, true);
|
|
|
|
const FIntRect ViewRect(0, 0, MipSize, MipSize);
|
|
RHICmdList.SetViewport(0, 0, 0.0f, MipSize, MipSize, 1.0f);
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
TShaderMapRef<FScreenVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
|
|
TShaderMapRef< TCubeFilterPS<1> > CaptureCubemapArrayPixelShader(GetGlobalShaderMap(FeatureLevel));
|
|
|
|
FCubeFilterPS* PixelShader;
|
|
if( bNormalize )
|
|
{
|
|
PixelShader = *TShaderMapRef< TCubeFilterPS<1> >(ShaderMap);
|
|
static FGlobalBoundShaderState BoundShaderState;
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, PixelShader);
|
|
}
|
|
else
|
|
{
|
|
PixelShader = *TShaderMapRef< TCubeFilterPS<0> >(ShaderMap);
|
|
static FGlobalBoundShaderState BoundShaderState;
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, PixelShader);
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, PixelShader);
|
|
}
|
|
|
|
PixelShader->SetParameters(RHICmdList, NumMips, CubeFace, MipIndex, EffectiveSource);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
FIntPoint(ViewRect.Width(), ViewRect.Height()),
|
|
FIntPoint(MipSize, MipSize),
|
|
*VertexShader);
|
|
|
|
RHICmdList.CopyToResolveTarget(EffectiveRT.TargetableTexture, EffectiveRT.ShaderResourceTexture, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace, MipIndex));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Vertex shader used when writing to a cubemap. */
|
|
class FCopyToCubeFaceVS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FCopyToCubeFaceVS,Global);
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FCopyToCubeFaceVS() {}
|
|
FCopyToCubeFaceVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FGlobalShader(Initializer)
|
|
{
|
|
}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View)
|
|
{
|
|
FGlobalShader::SetParameters(RHICmdList, GetVertexShader(),View);
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FCopyToCubeFaceVS,TEXT("ReflectionEnvironmentShaders"),TEXT("CopyToCubeFaceVS"),SF_Vertex);
|
|
|
|
/** Pixel shader used when copying scene color from a scene render into a face of a reflection capture cubemap. */
|
|
class FCopySceneColorToCubeFacePS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FCopySceneColorToCubeFacePS,Global);
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
|
|
{
|
|
FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment);
|
|
}
|
|
|
|
FCopySceneColorToCubeFacePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FGlobalShader(Initializer)
|
|
{
|
|
DeferredParameters.Bind(Initializer.ParameterMap);
|
|
InTexture.Bind(Initializer.ParameterMap,TEXT("InTexture"));
|
|
InTextureSampler.Bind(Initializer.ParameterMap,TEXT("InTextureSampler"));
|
|
SkyLightCaptureParameters.Bind(Initializer.ParameterMap,TEXT("SkyLightCaptureParameters"));
|
|
LowerHemisphereColor.Bind(Initializer.ParameterMap,TEXT("LowerHemisphereColor"));
|
|
}
|
|
FCopySceneColorToCubeFacePS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, const FViewInfo& View, bool bCapturingForSkyLight, bool bLowerHemisphereIsBlack, const FLinearColor& LowerHemisphereColorValue)
|
|
{
|
|
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
|
|
|
|
FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View);
|
|
DeferredParameters.Set(RHICmdList, ShaderRHI, View);
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
ShaderRHI,
|
|
InTexture,
|
|
InTextureSampler,
|
|
TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
|
|
FSceneRenderTargets::Get(RHICmdList).GetSceneColor()->GetRenderTargetItem().ShaderResourceTexture);
|
|
|
|
FVector SkyLightParametersValue = FVector::ZeroVector;
|
|
FScene* Scene = (FScene*)View.Family->Scene;
|
|
|
|
if (bCapturingForSkyLight)
|
|
{
|
|
// When capturing reflection captures, support forcing all low hemisphere lighting to be black
|
|
SkyLightParametersValue = FVector(0, 0, bLowerHemisphereIsBlack ? 1.0f : 0.0f);
|
|
}
|
|
else if (Scene->SkyLight && !Scene->SkyLight->bHasStaticLighting)
|
|
{
|
|
// When capturing reflection captures and there's a stationary sky light, mask out any pixels whose depth classify it as part of the sky
|
|
// This will allow changing the stationary sky light at runtime
|
|
SkyLightParametersValue = FVector(1, Scene->SkyLight->SkyDistanceThreshold, 0);
|
|
}
|
|
else
|
|
{
|
|
// When capturing reflection captures and there's no sky light, or only a static sky light, capture all depth ranges
|
|
SkyLightParametersValue = FVector(2, 0, 0);
|
|
}
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, SkyLightCaptureParameters, SkyLightParametersValue);
|
|
SetShaderValue(RHICmdList, ShaderRHI, LowerHemisphereColor, LowerHemisphereColorValue);
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
Ar << DeferredParameters;
|
|
Ar << InTexture;
|
|
Ar << InTextureSampler;
|
|
Ar << SkyLightCaptureParameters;
|
|
Ar << LowerHemisphereColor;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
FDeferredPixelShaderParameters DeferredParameters;
|
|
FShaderResourceParameter InTexture;
|
|
FShaderResourceParameter InTextureSampler;
|
|
FShaderParameter SkyLightCaptureParameters;
|
|
FShaderParameter LowerHemisphereColor;
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FCopySceneColorToCubeFacePS,TEXT("ReflectionEnvironmentShaders"),TEXT("CopySceneColorToCubeFaceColorPS"),SF_Pixel);
|
|
|
|
FGlobalBoundShaderState CopyColorCubemapBoundShaderState;
|
|
|
|
/** Pixel shader used when copying a cubemap into a face of a reflection capture cubemap. */
|
|
class FCopyCubemapToCubeFacePS : public FGlobalShader
|
|
{
|
|
DECLARE_SHADER_TYPE(FCopyCubemapToCubeFacePS,Global);
|
|
public:
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
FCopyCubemapToCubeFacePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer):
|
|
FGlobalShader(Initializer)
|
|
{
|
|
CubeFace.Bind(Initializer.ParameterMap,TEXT("CubeFace"));
|
|
SourceTexture.Bind(Initializer.ParameterMap,TEXT("SourceTexture"));
|
|
SourceTextureSampler.Bind(Initializer.ParameterMap,TEXT("SourceTextureSampler"));
|
|
SkyLightCaptureParameters.Bind(Initializer.ParameterMap,TEXT("SkyLightCaptureParameters"));
|
|
LowerHemisphereColor.Bind(Initializer.ParameterMap,TEXT("LowerHemisphereColor"));
|
|
SinCosSourceCubemapRotation.Bind(Initializer.ParameterMap,TEXT("SinCosSourceCubemapRotation"));
|
|
}
|
|
FCopyCubemapToCubeFacePS() {}
|
|
|
|
void SetParameters(FRHICommandList& RHICmdList, const FTexture* SourceCubemap, uint32 CubeFaceValue, bool bIsSkyLight, bool bLowerHemisphereIsBlack, float SourceCubemapRotation, const FLinearColor& LowerHemisphereColorValue)
|
|
{
|
|
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, CubeFace, CubeFaceValue);
|
|
|
|
SetTextureParameter(
|
|
RHICmdList,
|
|
ShaderRHI,
|
|
SourceTexture,
|
|
SourceTextureSampler,
|
|
SourceCubemap);
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, SkyLightCaptureParameters, FVector(bIsSkyLight ? 1.0f : 0.0f, 0.0f, bLowerHemisphereIsBlack ? 1.0f : 0.0f));
|
|
SetShaderValue(RHICmdList, ShaderRHI, LowerHemisphereColor, LowerHemisphereColorValue);
|
|
|
|
SetShaderValue(RHICmdList, ShaderRHI, SinCosSourceCubemapRotation, FVector2D(FMath::Sin(SourceCubemapRotation), FMath::Cos(SourceCubemapRotation)));
|
|
}
|
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
|
{
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
Ar << CubeFace;
|
|
Ar << SourceTexture;
|
|
Ar << SourceTextureSampler;
|
|
Ar << SkyLightCaptureParameters;
|
|
Ar << LowerHemisphereColor;
|
|
Ar << SinCosSourceCubemapRotation;
|
|
return bShaderHasOutdatedParameters;
|
|
}
|
|
|
|
private:
|
|
FShaderParameter CubeFace;
|
|
FShaderResourceParameter SourceTexture;
|
|
FShaderResourceParameter SourceTextureSampler;
|
|
FShaderParameter SkyLightCaptureParameters;
|
|
FShaderParameter LowerHemisphereColor;
|
|
FShaderParameter SinCosSourceCubemapRotation;
|
|
};
|
|
|
|
IMPLEMENT_SHADER_TYPE(,FCopyCubemapToCubeFacePS,TEXT("ReflectionEnvironmentShaders"),TEXT("CopyCubemapToCubeFaceColorPS"),SF_Pixel);
|
|
|
|
FGlobalBoundShaderState CopyFromCubemapToCubemapBoundShaderState;
|
|
|
|
int32 FindOrAllocateCubemapIndex(FScene* Scene, const UReflectionCaptureComponent* Component)
|
|
{
|
|
int32 CaptureIndex = -1;
|
|
|
|
// Try to find an existing capture index for this component
|
|
FCaptureComponentSceneState* CaptureSceneStatePtr = Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Find(Component);
|
|
|
|
if (CaptureSceneStatePtr)
|
|
{
|
|
CaptureIndex = CaptureSceneStatePtr->CaptureIndex;
|
|
}
|
|
else
|
|
{
|
|
// Reuse a freed index if possible
|
|
for (int32 PotentialIndex = 0; PotentialIndex < Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Num(); PotentialIndex++)
|
|
{
|
|
if (!Scene->ReflectionSceneData.AllocatedReflectionCaptureState.FindKey(FCaptureComponentSceneState(PotentialIndex)))
|
|
{
|
|
CaptureIndex = PotentialIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Allocate a new index if needed
|
|
if (CaptureIndex == -1)
|
|
{
|
|
CaptureIndex = Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Num();
|
|
}
|
|
|
|
Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Add(Component, FCaptureComponentSceneState(CaptureIndex));
|
|
|
|
check(CaptureIndex < GMaxNumReflectionCaptures);
|
|
}
|
|
|
|
check(CaptureIndex >= 0);
|
|
return CaptureIndex;
|
|
}
|
|
|
|
void ClearScratchCubemaps(FRHICommandList& RHICmdList, int32 TargetSize)
|
|
{
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
SceneContext.AllocateReflectionTargets(RHICmdList, TargetSize);
|
|
// Clear scratch render targets to a consistent but noticeable value
|
|
// This makes debugging capture issues much easier, otherwise the random contents from previous captures is shown
|
|
|
|
FSceneRenderTargetItem& RT0 = SceneContext.ReflectionColorScratchCubemap[0]->GetRenderTargetItem();
|
|
int32 NumMips = (int32)RT0.TargetableTexture->GetNumMips();
|
|
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
SetRenderTarget(RHICmdList, RT0.TargetableTexture, MipIndex, CubeFace, NULL, true);
|
|
RHICmdList.Clear(true, FLinearColor(0, 10000, 0, 0), false, (float)ERHIZBuffer::FarPlane, false, 0, FIntRect());
|
|
}
|
|
}
|
|
|
|
FSceneRenderTargetItem& RT1 = SceneContext.ReflectionColorScratchCubemap[1]->GetRenderTargetItem();
|
|
NumMips = (int32)RT1.TargetableTexture->GetNumMips();
|
|
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
SetRenderTarget(RHICmdList, RT1.TargetableTexture, MipIndex, CubeFace, NULL, true);
|
|
RHICmdList.Clear(true, FLinearColor(0, 10000, 0, 0), false, (float)ERHIZBuffer::FarPlane, false, 0, FIntRect());
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Captures the scene for a reflection capture by rendering the scene multiple times and copying into a cubemap texture. */
|
|
void CaptureSceneToScratchCubemap(FRHICommandListImmediate& RHICmdList, FSceneRenderer* SceneRenderer, ECubeFace CubeFace, int32 CubemapSize, bool bCapturingForSkyLight, bool bLowerHemisphereIsBlack, const FLinearColor& LowerHemisphereColor)
|
|
{
|
|
FMemMark MemStackMark(FMemStack::Get());
|
|
// update any resources that needed a deferred update
|
|
FDeferredUpdateResource::UpdateResources(RHICmdList);
|
|
|
|
const auto FeatureLevel = SceneRenderer->FeatureLevel;
|
|
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, CubeMapCapture);
|
|
|
|
// Render the scene normally for one face of the cubemap
|
|
SceneRenderer->Render(RHICmdList);
|
|
check(&RHICmdList == &FRHICommandListExecutor::GetImmediateCommandList());
|
|
check(IsInRenderingThread());
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_CaptureSceneToScratchCubemap_Flush);
|
|
FRHICommandListExecutor::GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThread);
|
|
}
|
|
|
|
#if PLATFORM_PS4 // @todo ps4 - this should be done a different way
|
|
// PS4 needs some code here to process the scene
|
|
extern void TEMP_PostReflectionCaptureRender();
|
|
TEMP_PostReflectionCaptureRender();
|
|
#endif
|
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
const int32 EffectiveSize = CubemapSize;
|
|
FSceneRenderTargetItem& EffectiveColorRT = SceneContext.ReflectionColorScratchCubemap[0]->GetRenderTargetItem();
|
|
|
|
{
|
|
SCOPED_DRAW_EVENT(RHICmdList, CubeMapCopyScene);
|
|
|
|
// Copy the captured scene into the cubemap face
|
|
SetRenderTarget(RHICmdList, EffectiveColorRT.TargetableTexture, 0, CubeFace, NULL);
|
|
|
|
const FIntRect ViewRect(0, 0, EffectiveSize, EffectiveSize);
|
|
RHICmdList.SetViewport(0, 0, 0.0f, EffectiveSize, EffectiveSize, 1.0f);
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
TShaderMapRef<FCopyToCubeFaceVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
|
|
TShaderMapRef<FCopySceneColorToCubeFacePS> PixelShader(GetGlobalShaderMap(FeatureLevel));
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, CopyColorCubemapBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
|
|
|
|
PixelShader->SetParameters(RHICmdList, SceneRenderer->Views[0], bCapturingForSkyLight, bLowerHemisphereIsBlack, LowerHemisphereColor);
|
|
VertexShader->SetParameters(RHICmdList, SceneRenderer->Views[0]);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width() * GSupersampleCaptureFactor, ViewRect.Height() * GSupersampleCaptureFactor,
|
|
FIntPoint(ViewRect.Width(), ViewRect.Height()),
|
|
SceneContext.GetBufferSizeXY(),
|
|
*VertexShader);
|
|
|
|
RHICmdList.CopyToResolveTarget(EffectiveColorRT.TargetableTexture, EffectiveColorRT.ShaderResourceTexture, true, FResolveParams(FResolveRect(), CubeFace));
|
|
}
|
|
}
|
|
|
|
FSceneRenderer::WaitForTasksClearSnapshotsAndDeleteSceneRenderer(RHICmdList, SceneRenderer);
|
|
}
|
|
|
|
void CopyCubemapToScratchCubemap(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, UTextureCube* SourceCubemap, int32 CubemapSize, bool bIsSkyLight, bool bLowerHemisphereIsBlack, float SourceCubemapRotation, const FLinearColor& LowerHemisphereColorValue)
|
|
{
|
|
check(SourceCubemap);
|
|
|
|
const int32 EffectiveSize = CubemapSize;
|
|
FSceneRenderTargetItem& EffectiveColorRT = FSceneRenderTargets::Get(RHICmdList).ReflectionColorScratchCubemap[0]->GetRenderTargetItem();
|
|
|
|
for (uint32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
// Copy the captured scene into the cubemap face
|
|
SetRenderTarget(RHICmdList, EffectiveColorRT.TargetableTexture, 0, CubeFace, NULL, true);
|
|
|
|
const FTexture* SourceCubemapResource = SourceCubemap->Resource;
|
|
const FIntPoint SourceDimensions(SourceCubemapResource->GetSizeX(), SourceCubemapResource->GetSizeY());
|
|
const FIntRect ViewRect(0, 0, EffectiveSize, EffectiveSize);
|
|
RHICmdList.SetViewport(0, 0, 0.0f, EffectiveSize, EffectiveSize, 1.0f);
|
|
RHICmdList.SetRasterizerState(TStaticRasterizerState<FM_Solid, CM_None>::GetRHI());
|
|
RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
|
RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
TShaderMapRef<FScreenVS> VertexShader(GetGlobalShaderMap(FeatureLevel));
|
|
TShaderMapRef<FCopyCubemapToCubeFacePS> PixelShader(GetGlobalShaderMap(FeatureLevel));
|
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, CopyFromCubemapToCubemapBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader);
|
|
PixelShader->SetParameters(RHICmdList, SourceCubemapResource, CubeFace, bIsSkyLight, bLowerHemisphereIsBlack, SourceCubemapRotation, LowerHemisphereColorValue);
|
|
|
|
DrawRectangle(
|
|
RHICmdList,
|
|
ViewRect.Min.X, ViewRect.Min.Y,
|
|
ViewRect.Width(), ViewRect.Height(),
|
|
0, 0,
|
|
SourceDimensions.X, SourceDimensions.Y,
|
|
FIntPoint(ViewRect.Width(), ViewRect.Height()),
|
|
SourceDimensions,
|
|
*VertexShader);
|
|
|
|
RHICmdList.CopyToResolveTarget(EffectiveColorRT.TargetableTexture, EffectiveColorRT.ShaderResourceTexture, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocates reflection captures in the scene's reflection cubemap array and updates them by recapturing the scene.
|
|
* Existing captures will only be updated. Must be called from the game thread.
|
|
*/
|
|
void FScene::AllocateReflectionCaptures(const TArray<UReflectionCaptureComponent*>& NewCaptures)
|
|
{
|
|
if (NewCaptures.Num() > 0)
|
|
{
|
|
if (GetFeatureLevel() >= ERHIFeatureLevel::SM5)
|
|
{
|
|
for (int32 CaptureIndex = 0; CaptureIndex < NewCaptures.Num(); CaptureIndex++)
|
|
{
|
|
bool bAlreadyExists = false;
|
|
|
|
// Try to find an existing allocation
|
|
for (TSparseArray<UReflectionCaptureComponent*>::TIterator It(ReflectionSceneData.AllocatedReflectionCapturesGameThread); It; ++It)
|
|
{
|
|
UReflectionCaptureComponent* OtherComponent = *It;
|
|
|
|
if (OtherComponent == NewCaptures[CaptureIndex])
|
|
{
|
|
bAlreadyExists = true;
|
|
}
|
|
}
|
|
|
|
// Add the capture to the allocated list
|
|
if (!bAlreadyExists && ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num() < GMaxNumReflectionCaptures)
|
|
{
|
|
ReflectionSceneData.AllocatedReflectionCapturesGameThread.Add(NewCaptures[CaptureIndex]);
|
|
}
|
|
}
|
|
|
|
// Request the exact amount needed by default
|
|
int32 DesiredMaxCubemaps = ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num();
|
|
const float MaxCubemapsRoundUpBase = 1.5f;
|
|
|
|
// If this is not the first time the scene has allocated the cubemap array, include slack to reduce reallocations
|
|
if (ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread > 0)
|
|
{
|
|
float Exponent = FMath::LogX(MaxCubemapsRoundUpBase, ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num());
|
|
|
|
// Round up to the next integer exponent to provide stability and reduce reallocations
|
|
DesiredMaxCubemaps = FMath::Pow(MaxCubemapsRoundUpBase, FMath::TruncToInt(Exponent) + 1);
|
|
}
|
|
|
|
DesiredMaxCubemaps = FMath::Min(DesiredMaxCubemaps, GMaxNumReflectionCaptures);
|
|
|
|
if (DesiredMaxCubemaps != ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread || CVarReflectionCaptureSize.GetValueOnGameThread() != ReflectionSceneData.CubemapArray.GetCubemapSize())
|
|
{
|
|
ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread = DesiredMaxCubemaps;
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
ResizeArrayCommand,
|
|
FScene*, Scene, this,
|
|
uint32, MaxSize, ReflectionSceneData.MaxAllocatedReflectionCubemapsGameThread,
|
|
int32, CubemapSize, CVarReflectionCaptureSize.GetValueOnGameThread(),
|
|
{
|
|
// Update the scene's cubemap array, which will reallocate it, so we no longer have the contents of existing entries
|
|
Scene->ReflectionSceneData.CubemapArray.UpdateMaxCubemaps(MaxSize, CubemapSize);
|
|
});
|
|
|
|
// Recapture all reflection captures now that we have reallocated the cubemap array
|
|
UpdateAllReflectionCaptures();
|
|
}
|
|
else
|
|
{
|
|
// No reallocation of the cubemap array was needed, just update the captures that were requested
|
|
|
|
for (TSparseArray<UReflectionCaptureComponent*>::TIterator It(ReflectionSceneData.AllocatedReflectionCapturesGameThread); It; ++It)
|
|
{
|
|
UReflectionCaptureComponent* CurrentComponent = *It;
|
|
|
|
if (NewCaptures.Contains(CurrentComponent))
|
|
{
|
|
UpdateReflectionCaptureContents(CurrentComponent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (GetFeatureLevel() == ERHIFeatureLevel::SM4)
|
|
{
|
|
for (int32 ComponentIndex = 0; ComponentIndex < NewCaptures.Num(); ComponentIndex++)
|
|
{
|
|
UReflectionCaptureComponent* CurrentComponent = NewCaptures[ComponentIndex];
|
|
UpdateReflectionCaptureContents(CurrentComponent);
|
|
}
|
|
}
|
|
|
|
for (int32 CaptureIndex = 0; CaptureIndex < NewCaptures.Num(); CaptureIndex++)
|
|
{
|
|
UReflectionCaptureComponent* Component = NewCaptures[CaptureIndex];
|
|
|
|
Component->SetCaptureCompleted();
|
|
|
|
if (Component->SceneProxy)
|
|
{
|
|
// Update the transform of the reflection capture
|
|
// This is not done earlier by the reflection capture when it detects that it is dirty,
|
|
// To ensure that the RT sees both the new transform and the new contents on the same frame.
|
|
Component->SendRenderTransform_Concurrent();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** Updates the contents of all reflection captures in the scene. Must be called from the game thread. */
|
|
void FScene::UpdateAllReflectionCaptures()
|
|
{
|
|
if (IsReflectionEnvironmentAvailable(GetFeatureLevel()))
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
CaptureCommand,
|
|
FScene*, Scene, this,
|
|
{
|
|
Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Empty();
|
|
});
|
|
|
|
const int32 UpdateDivisor = FMath::Max(ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num() / 20, 1);
|
|
const bool bDisplayStatus = ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num() > 50;
|
|
|
|
if (bDisplayStatus)
|
|
{
|
|
const FText Status = NSLOCTEXT("Engine", "BeginReflectionCapturesTask", "Updating Reflection Captures...");
|
|
GWarn->BeginSlowTask( Status, true );
|
|
GWarn->StatusUpdate(0, ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num(), Status);
|
|
}
|
|
|
|
int32 CaptureIndex = 0;
|
|
|
|
for (TSparseArray<UReflectionCaptureComponent*>::TIterator It(ReflectionSceneData.AllocatedReflectionCapturesGameThread); It; ++It)
|
|
{
|
|
// Update progress occasionally
|
|
if (bDisplayStatus && CaptureIndex % UpdateDivisor == 0)
|
|
{
|
|
GWarn->UpdateProgress(CaptureIndex, ReflectionSceneData.AllocatedReflectionCapturesGameThread.Num());
|
|
}
|
|
|
|
CaptureIndex++;
|
|
UReflectionCaptureComponent* CurrentComponent = *It;
|
|
UpdateReflectionCaptureContents(CurrentComponent);
|
|
}
|
|
|
|
if (bDisplayStatus)
|
|
{
|
|
GWarn->EndSlowTask();
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetReflectionCaptureData_RenderingThread(FRHICommandListImmediate& RHICmdList, FScene* Scene, const UReflectionCaptureComponent* Component, FReflectionCaptureFullHDR* OutDerivedData)
|
|
{
|
|
const FCaptureComponentSceneState* ComponentStatePtr = Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Find(Component);
|
|
|
|
if (ComponentStatePtr)
|
|
{
|
|
FSceneRenderTargetItem& EffectiveDest = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget();
|
|
|
|
const int32 CaptureIndex = ComponentStatePtr->CaptureIndex;
|
|
const int32 NumMips = EffectiveDest.ShaderResourceTexture->GetNumMips();
|
|
const int32 EffectiveTopMipSize = FMath::Pow(2, NumMips - 1);
|
|
|
|
TArray<uint8> CaptureData;
|
|
int32 CaptureDataSize = 0;
|
|
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
const int32 MipSize = 1 << (NumMips - MipIndex - 1);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
CaptureDataSize += MipSize * MipSize * sizeof(FFloat16Color);
|
|
}
|
|
}
|
|
|
|
CaptureData.Empty(CaptureDataSize);
|
|
CaptureData.AddZeroed(CaptureDataSize);
|
|
int32 MipBaseIndex = 0;
|
|
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
check(EffectiveDest.ShaderResourceTexture->GetFormat() == PF_FloatRGBA);
|
|
const int32 MipSize = 1 << (NumMips - MipIndex - 1);
|
|
const int32 CubeFaceBytes = MipSize * MipSize * sizeof(FFloat16Color);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
TArray<FFloat16Color> SurfaceData;
|
|
// Read each mip face
|
|
//@todo - do this without blocking the GPU so many times
|
|
//@todo - pool the temporary textures in RHIReadSurfaceFloatData instead of always creating new ones
|
|
RHICmdList.ReadSurfaceFloatData(EffectiveDest.ShaderResourceTexture, FIntRect(0, 0, MipSize, MipSize), SurfaceData, (ECubeFace)CubeFace, CaptureIndex, MipIndex);
|
|
const int32 DestIndex = MipBaseIndex + CubeFace * CubeFaceBytes;
|
|
uint8* FaceData = &CaptureData[DestIndex];
|
|
check(SurfaceData.Num() * SurfaceData.GetTypeSize() == CubeFaceBytes);
|
|
FMemory::Memcpy(FaceData, SurfaceData.GetData(), CubeFaceBytes);
|
|
}
|
|
|
|
MipBaseIndex += CubeFaceBytes * CubeFace_MAX;
|
|
}
|
|
|
|
OutDerivedData->InitializeFromUncompressedData(CaptureData, EffectiveTopMipSize);
|
|
}
|
|
}
|
|
|
|
void FScene::GetReflectionCaptureData(UReflectionCaptureComponent* Component, FReflectionCaptureFullHDR& OutDerivedData)
|
|
{
|
|
check(GetFeatureLevel() >= ERHIFeatureLevel::SM5);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
GetReflectionDataCommand,
|
|
FScene*,Scene,this,
|
|
const UReflectionCaptureComponent*,Component,Component,
|
|
FReflectionCaptureFullHDR*,OutDerivedData,&OutDerivedData,
|
|
{
|
|
GetReflectionCaptureData_RenderingThread(RHICmdList, Scene, Component, OutDerivedData);
|
|
});
|
|
|
|
// Necessary since the RT is writing to OutDerivedData directly
|
|
FlushRenderingCommands();
|
|
}
|
|
|
|
void UploadReflectionCapture_RenderingThread(FScene* Scene, const FReflectionCaptureFullHDR* FullHDRData, const UReflectionCaptureComponent* CaptureComponent)
|
|
{
|
|
const int32 EffectiveTopMipSize = FullHDRData->CubemapSize;
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
|
|
const int32 CaptureIndex = FindOrAllocateCubemapIndex(Scene, CaptureComponent);
|
|
FTextureCubeRHIRef& CubeMapArray = (FTextureCubeRHIRef&)Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().ShaderResourceTexture;
|
|
check(CubeMapArray->GetFormat() == PF_FloatRGBA);
|
|
|
|
TArray<uint8> CubemapData;
|
|
FullHDRData->GetUncompressedData(CubemapData);
|
|
int32 MipBaseIndex = 0;
|
|
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
const int32 MipSize = 1 << (NumMips - MipIndex - 1);
|
|
const int32 CubeFaceBytes = MipSize * MipSize * sizeof(FFloat16Color);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
uint32 DestStride = 0;
|
|
uint8* DestBuffer = (uint8*)RHILockTextureCubeFace(CubeMapArray, CubeFace, CaptureIndex, MipIndex, RLM_WriteOnly, DestStride, false);
|
|
|
|
// Handle DestStride by copying each row
|
|
for (int32 Y = 0; Y < MipSize; Y++)
|
|
{
|
|
FFloat16Color* DestPtr = (FFloat16Color*)((uint8*)DestBuffer + Y * DestStride);
|
|
const int32 SourceIndex = MipBaseIndex + CubeFace * CubeFaceBytes + Y * MipSize * sizeof(FFloat16Color);
|
|
const uint8* SourcePtr = &CubemapData[SourceIndex];
|
|
FMemory::Memcpy(DestPtr, SourcePtr, MipSize * sizeof(FFloat16Color));
|
|
}
|
|
|
|
RHIUnlockTextureCubeFace(CubeMapArray, CubeFace, CaptureIndex, MipIndex, false);
|
|
}
|
|
|
|
MipBaseIndex += CubeFaceBytes * CubeFace_MAX;
|
|
}
|
|
}
|
|
|
|
/** Creates a transformation for a cubemap face, following the D3D cubemap layout. */
|
|
FMatrix CalcCubeFaceViewRotationMatrix(ECubeFace Face)
|
|
{
|
|
FMatrix Result(FMatrix::Identity);
|
|
|
|
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
|
|
Result = FBasisVectorMatrix( vRight, vUp, vDir, FVector::ZeroVector );
|
|
|
|
return Result;
|
|
}
|
|
|
|
/**
|
|
* Render target class required for rendering the scene.
|
|
* This doesn't actually allocate a render target as we read from scene color to get HDR results directly.
|
|
*/
|
|
class FCaptureRenderTarget : public FRenderResource, public FRenderTarget
|
|
{
|
|
public:
|
|
|
|
FCaptureRenderTarget() :
|
|
Size(0)
|
|
{}
|
|
|
|
virtual const FTexture2DRHIRef& GetRenderTargetTexture() const
|
|
{
|
|
static FTexture2DRHIRef DummyTexture;
|
|
return DummyTexture;
|
|
}
|
|
|
|
void SetSize(int32 TargetSize) { Size = TargetSize; }
|
|
virtual FIntPoint GetSizeXY() const { return FIntPoint(Size, Size); }
|
|
virtual float GetDisplayGamma() const { return 1.0f; }
|
|
|
|
private:
|
|
|
|
int32 Size;
|
|
};
|
|
|
|
TGlobalResource<FCaptureRenderTarget> GReflectionCaptureRenderTarget;
|
|
|
|
void CaptureSceneIntoScratchCubemap(
|
|
FScene* Scene,
|
|
FVector CapturePosition,
|
|
int32 CubemapSize,
|
|
bool bCapturingForSkyLight,
|
|
bool bStaticSceneOnly,
|
|
float SkyLightNearPlane,
|
|
bool bLowerHemisphereIsBlack,
|
|
bool bCaptureEmissiveOnly,
|
|
const FLinearColor& LowerHemisphereColor
|
|
)
|
|
{
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
// Alert the RHI that we're rendering a new frame
|
|
// Not really a new frame, but it will allow pooling mechanisms to update, like the uniform buffer pool
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND(
|
|
BeginFrame,
|
|
{
|
|
GFrameNumberRenderThread++;
|
|
RHICmdList.BeginFrame();
|
|
})
|
|
|
|
GReflectionCaptureRenderTarget.SetSize(CubemapSize);
|
|
|
|
FSceneViewFamilyContext ViewFamily( FSceneViewFamily::ConstructionValues(
|
|
&GReflectionCaptureRenderTarget,
|
|
Scene,
|
|
FEngineShowFlags(ESFIM_Game))
|
|
.SetWorldTimes( 0.0f, 0.0f, 0.0f )
|
|
.SetResolveScene(false) );
|
|
|
|
// Disable features that are not desired when capturing the scene
|
|
ViewFamily.EngineShowFlags.PostProcessing = 0;
|
|
ViewFamily.EngineShowFlags.MotionBlur = 0;
|
|
ViewFamily.EngineShowFlags.SetOnScreenDebug(false);
|
|
ViewFamily.EngineShowFlags.HMDDistortion = 0;
|
|
// Exclude particles and light functions as they are usually dynamic, and can't be captured well
|
|
ViewFamily.EngineShowFlags.Particles = 0;
|
|
ViewFamily.EngineShowFlags.LightFunctions = 0;
|
|
ViewFamily.EngineShowFlags.SetCompositeEditorPrimitives(false);
|
|
// These are highly dynamic and can't be captured effectively
|
|
ViewFamily.EngineShowFlags.LightShafts = 0;
|
|
|
|
// Don't apply sky lighting diffuse when capturing the sky light source, or we would have feedback
|
|
ViewFamily.EngineShowFlags.SkyLighting = !bCapturingForSkyLight;
|
|
|
|
FSceneViewInitOptions ViewInitOptions;
|
|
ViewInitOptions.ViewFamily = &ViewFamily;
|
|
ViewInitOptions.BackgroundColor = FLinearColor::Black;
|
|
ViewInitOptions.OverlayColor = FLinearColor::Black;
|
|
ViewInitOptions.SetViewRectangle(FIntRect(0, 0, CubemapSize * GSupersampleCaptureFactor, CubemapSize * GSupersampleCaptureFactor));
|
|
|
|
const float NearPlane = bCapturingForSkyLight ? SkyLightNearPlane : GReflectionCaptureNearPlane;
|
|
|
|
// Projection matrix based on the fov, near / far clip settings
|
|
// Each face always uses a 90 degree field of view
|
|
if ((bool)ERHIZBuffer::IsInverted)
|
|
{
|
|
ViewInitOptions.ProjectionMatrix = FReversedZPerspectiveMatrix(
|
|
90.0f * (float)PI / 360.0f,
|
|
(float)CubemapSize * GSupersampleCaptureFactor,
|
|
(float)CubemapSize * GSupersampleCaptureFactor,
|
|
NearPlane
|
|
);
|
|
}
|
|
else
|
|
{
|
|
ViewInitOptions.ProjectionMatrix = FPerspectiveMatrix(
|
|
90.0f * (float)PI / 360.0f,
|
|
(float)CubemapSize * GSupersampleCaptureFactor,
|
|
(float)CubemapSize * GSupersampleCaptureFactor,
|
|
NearPlane
|
|
);
|
|
}
|
|
|
|
ViewInitOptions.ViewOrigin = CapturePosition;
|
|
ViewInitOptions.ViewRotationMatrix = CalcCubeFaceViewRotationMatrix((ECubeFace)CubeFace);
|
|
|
|
FSceneView* View = new FSceneView(ViewInitOptions);
|
|
|
|
// Force all surfaces diffuse
|
|
View->RoughnessOverrideParameter = FVector2D( 1.0f, 0.0f );
|
|
|
|
if (bCaptureEmissiveOnly)
|
|
{
|
|
View->DiffuseOverrideParameter = FVector4(0, 0, 0, 0);
|
|
View->SpecularOverrideParameter = FVector4(0, 0, 0, 0);
|
|
}
|
|
|
|
View->bIsReflectionCapture = true;
|
|
View->bStaticSceneOnly = bStaticSceneOnly;
|
|
View->StartFinalPostprocessSettings(CapturePosition);
|
|
View->EndFinalPostprocessSettings(ViewInitOptions);
|
|
|
|
ViewFamily.Views.Add(View);
|
|
|
|
FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(&ViewFamily, NULL);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_SIXPARAMETER(
|
|
CaptureCommand,
|
|
FSceneRenderer*, SceneRenderer, SceneRenderer,
|
|
ECubeFace, CubeFace, (ECubeFace)CubeFace,
|
|
int32, CubemapSize, CubemapSize,
|
|
bool, bCapturingForSkyLight, bCapturingForSkyLight,
|
|
bool, bLowerHemisphereIsBlack, bLowerHemisphereIsBlack,
|
|
FLinearColor, LowerHemisphereColor, LowerHemisphereColor,
|
|
{
|
|
CaptureSceneToScratchCubemap(RHICmdList, SceneRenderer, CubeFace, CubemapSize, bCapturingForSkyLight, bLowerHemisphereIsBlack, LowerHemisphereColor);
|
|
RHICmdList.EndFrame();
|
|
});
|
|
}
|
|
}
|
|
|
|
void CopyToSceneArray(FRHICommandListImmediate& RHICmdList, FScene* Scene, FReflectionCaptureProxy* ReflectionProxy)
|
|
{
|
|
const int32 EffectiveTopMipSize = CVarReflectionCaptureSize.GetValueOnRenderThread();
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
|
|
const int32 CaptureIndex = FindOrAllocateCubemapIndex(Scene, ReflectionProxy->Component);
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
// GPU copy back to the scene's texture array, which is not a render target
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
// The source for this copy is the dest from the filtering pass
|
|
FSceneRenderTargetItem& EffectiveSource = GetEffectiveRenderTarget(SceneContext, false, MipIndex);
|
|
FSceneRenderTargetItem& EffectiveDest = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget();
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
RHICmdList.CopyToResolveTarget(EffectiveSource.ShaderResourceTexture, EffectiveDest.ShaderResourceTexture, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace, MipIndex, 0, CaptureIndex));
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyToComponentTexture(FRHICommandList& RHICmdList, FScene* Scene, FReflectionCaptureProxy* ReflectionProxy)
|
|
{
|
|
check(ReflectionProxy->SM4FullHDRCubemap);
|
|
const int32 EffectiveTopMipSize = CVarReflectionCaptureSize.GetValueOnRenderThread();
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
// GPU copy back to the component's cubemap texture, which is not a render target
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
// The source for this copy is the dest from the filtering pass
|
|
FSceneRenderTargetItem& EffectiveSource = GetEffectiveRenderTarget(SceneContext, false, MipIndex);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
RHICmdList.CopyToResolveTarget(EffectiveSource.ShaderResourceTexture, ReflectionProxy->SM4FullHDRCubemap->TextureRHI, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace, MipIndex, 0, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the contents of the given reflection capture by rendering the scene.
|
|
* This must be called on the game thread.
|
|
*/
|
|
void FScene::UpdateReflectionCaptureContents(UReflectionCaptureComponent* CaptureComponent)
|
|
{
|
|
const bool bCubemapSpecified = CaptureComponent->ReflectionSourceType == EReflectionSourceType::SpecifiedCubemap && CaptureComponent->Cubemap;
|
|
|
|
if (IsReflectionEnvironmentAvailable(GetFeatureLevel()) || bCubemapSpecified)
|
|
{
|
|
const FReflectionCaptureFullHDR* DerivedData = CaptureComponent->GetFullHDRData();
|
|
|
|
// Upload existing derived data if it exists, instead of capturing
|
|
if (DerivedData && DerivedData->CompressedCapturedData.Num() > 0)
|
|
{
|
|
// For other feature levels the reflection textures are stored on the component instead of in a scene-wide texture array
|
|
if (GetFeatureLevel() >= ERHIFeatureLevel::SM5)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
UploadCaptureCommand,
|
|
FScene*, Scene, this,
|
|
const FReflectionCaptureFullHDR*, DerivedData, DerivedData,
|
|
const UReflectionCaptureComponent*, CaptureComponent, CaptureComponent,
|
|
{
|
|
UploadReflectionCapture_RenderingThread(Scene, DerivedData, CaptureComponent);
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CaptureComponent->ReflectionSourceType == EReflectionSourceType::SpecifiedCubemap && !CaptureComponent->Cubemap)
|
|
{
|
|
return;
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
ClearCommand,
|
|
int32, CubemapSize, CVarReflectionCaptureSize.GetValueOnGameThread(),
|
|
{
|
|
ClearScratchCubemaps(RHICmdList, CubemapSize);
|
|
});
|
|
|
|
if (CaptureComponent->ReflectionSourceType == EReflectionSourceType::CapturedScene)
|
|
{
|
|
CaptureSceneIntoScratchCubemap(this, CaptureComponent->GetComponentLocation() + CaptureComponent->CaptureOffset, CVarReflectionCaptureSize.GetValueOnGameThread(), false, true, 0, false, false, FLinearColor());
|
|
}
|
|
else if (CaptureComponent->ReflectionSourceType == EReflectionSourceType::SpecifiedCubemap)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_FOURPARAMETER(
|
|
CopyCubemapCommand,
|
|
UTextureCube*, SourceTexture, CaptureComponent->Cubemap,
|
|
int32, CubemapSize, CVarReflectionCaptureSize.GetValueOnGameThread(),
|
|
float, SourceCubemapRotation, CaptureComponent->SourceCubemapAngle * (PI / 180.f),
|
|
ERHIFeatureLevel::Type, FeatureLevel, GetFeatureLevel(),
|
|
{
|
|
CopyCubemapToScratchCubemap(RHICmdList, FeatureLevel, SourceTexture, CubemapSize, false, false, SourceCubemapRotation, FLinearColor());
|
|
});
|
|
}
|
|
else
|
|
{
|
|
check(!TEXT("Unknown reflection source type"));
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
FilterCommand,
|
|
ERHIFeatureLevel::Type, FeatureLevel, GetFeatureLevel(),
|
|
int32, CubemapSize, CVarReflectionCaptureSize.GetValueOnGameThread(),
|
|
{
|
|
bool bNormalize = true;
|
|
FilterReflectionEnvironment(RHICmdList, FeatureLevel, CubemapSize, NULL, bNormalize);
|
|
});
|
|
|
|
// Create a proxy to represent the reflection capture to the rendering thread
|
|
// The rendering thread will be responsible for deleting this when done with the filtering operation
|
|
// We can't use the component's SceneProxy here because the component may not be registered with the scene
|
|
FReflectionCaptureProxy* ReflectionProxy = new FReflectionCaptureProxy(CaptureComponent);
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
CopyCommand,
|
|
FScene*, Scene, this,
|
|
FReflectionCaptureProxy*, ReflectionProxy, ReflectionProxy,
|
|
ERHIFeatureLevel::Type, FeatureLevel, GetFeatureLevel(),
|
|
{
|
|
if (FeatureLevel == ERHIFeatureLevel::SM5)
|
|
{
|
|
CopyToSceneArray(RHICmdList, Scene, ReflectionProxy);
|
|
}
|
|
else if (FeatureLevel == ERHIFeatureLevel::SM4)
|
|
{
|
|
CopyToComponentTexture(RHICmdList, Scene, ReflectionProxy);
|
|
}
|
|
|
|
// Clean up the proxy now that the rendering thread is done with it
|
|
delete ReflectionProxy;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyToSkyTexture(FRHICommandList& RHICmdList, FScene* Scene, FTexture* ProcessedTexture)
|
|
{
|
|
const int32 EffectiveTopMipSize = ProcessedTexture->GetSizeX();
|
|
const int32 NumMips = FMath::CeilLogTwo(EffectiveTopMipSize) + 1;
|
|
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
|
|
|
|
// GPU copy back to the skylight's texture, which is not a render target
|
|
for (int32 MipIndex = 0; MipIndex < NumMips; MipIndex++)
|
|
{
|
|
// The source for this copy is the dest from the filtering pass
|
|
FSceneRenderTargetItem& EffectiveSource = GetEffectiveRenderTarget(SceneContext, false, MipIndex);
|
|
|
|
for (int32 CubeFace = 0; CubeFace < CubeFace_MAX; CubeFace++)
|
|
{
|
|
RHICmdList.CopyToResolveTarget(EffectiveSource.ShaderResourceTexture, ProcessedTexture->TextureRHI, true, FResolveParams(FResolveRect(), (ECubeFace)CubeFace, MipIndex, 0, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Warning: returns before writes to OutIrradianceEnvironmentMap have completed, as they are queued on the rendering thread
|
|
void FScene::UpdateSkyCaptureContents(const USkyLightComponent* CaptureComponent, bool bCaptureEmissiveOnly, UTextureCube* SourceCubemap, FTexture* OutProcessedTexture, FSHVectorRGB3& OutIrradianceEnvironmentMap)
|
|
{
|
|
if (GSupportsRenderTargetFormat_PF_FloatRGBA || GetFeatureLevel() >= ERHIFeatureLevel::SM4)
|
|
{
|
|
QUICK_SCOPE_CYCLE_COUNTER(STAT_UpdateSkyCaptureContents);
|
|
{
|
|
World = GetWorld();
|
|
if (World)
|
|
{
|
|
//guarantee that all render proxies are up to date before kicking off this render
|
|
World->SendAllEndOfFrameUpdates();
|
|
}
|
|
}
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
|
|
ClearCommand,
|
|
int32, CubemapSize, CaptureComponent->CubemapResolution,
|
|
{
|
|
ClearScratchCubemaps(RHICmdList, CubemapSize);
|
|
});
|
|
|
|
if (CaptureComponent->SourceType == SLS_CapturedScene)
|
|
{
|
|
bool bStaticSceneOnly = CaptureComponent->Mobility != EComponentMobility::Movable;
|
|
CaptureSceneIntoScratchCubemap(this, CaptureComponent->GetComponentLocation(), CaptureComponent->CubemapResolution, true, bStaticSceneOnly, CaptureComponent->SkyDistanceThreshold, CaptureComponent->bLowerHemisphereIsBlack, bCaptureEmissiveOnly, CaptureComponent->LowerHemisphereColor);
|
|
}
|
|
else if (CaptureComponent->SourceType == SLS_SpecifiedCubemap)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_SIXPARAMETER(
|
|
CopyCubemapCommand,
|
|
UTextureCube*, SourceTexture, SourceCubemap,
|
|
int32, CubemapSize, CaptureComponent->CubemapResolution,
|
|
bool, bLowerHemisphereIsBlack, CaptureComponent->bLowerHemisphereIsBlack,
|
|
float, SourceCubemapRotation, CaptureComponent->SourceCubemapAngle * (PI / 180.f),
|
|
ERHIFeatureLevel::Type, FeatureLevel, GetFeatureLevel(),
|
|
FLinearColor, LowerHemisphereColor, CaptureComponent->LowerHemisphereColor,
|
|
{
|
|
CopyCubemapToScratchCubemap(RHICmdList, FeatureLevel, SourceTexture, CubemapSize, true, bLowerHemisphereIsBlack, SourceCubemapRotation, LowerHemisphereColor);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
check(0);
|
|
}
|
|
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_THREEPARAMETER(
|
|
FilterCommand,
|
|
int32, CubemapSize, CaptureComponent->CubemapResolution,
|
|
FSHVectorRGB3*, IrradianceEnvironmentMap, &OutIrradianceEnvironmentMap,
|
|
ERHIFeatureLevel::Type, FeatureLevel, GetFeatureLevel(),
|
|
{
|
|
// Skylight is normalized manually in the shader
|
|
bool bNormalize = false;
|
|
FilterReflectionEnvironment(RHICmdList, FeatureLevel, CubemapSize, IrradianceEnvironmentMap, bNormalize);
|
|
});
|
|
|
|
// Optionally copy the filtered mip chain to the output texture
|
|
if (OutProcessedTexture)
|
|
{
|
|
ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER(
|
|
CopyCommand,
|
|
FScene*, Scene, this,
|
|
FTexture*, ProcessedTexture, OutProcessedTexture,
|
|
{
|
|
CopyToSkyTexture(RHICmdList, Scene, ProcessedTexture);
|
|
});
|
|
}
|
|
}
|
|
}
|