Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/ReflectionEnvironment.cpp
sebastien hillaire 139c0e2e9d Fixed many issues related to reflection
- Clarified top and bottom layers. Default material only has top layer.
- Reverted clear coat code doing an extra loop for sky sampling causing issues on other materials (wrongly receiving sky color)
- Fixed clear coat appearance when MaxDistance!=-1
- Fixed black reflexions due to R pointing into the surface using the clear coat normal (assuming it is smooth so only a half kind of a fix, a proper fix would involve sending the geometry normal in the payload)
- Tested: works with fog, with/without reflection volume, and with/without deferred material evaluation

#rb Charles.DeRousiers
#jira none, but agreed with Juan Canada and Nick Penwarden that it should go in (for correctenss of reflections and entreprise coated material)
#lockdown nick.penwarden

#ROBOMERGE-OWNER: ryan.vance
#ROBOMERGE-AUTHOR: sebastien.hillaire
#ROBOMERGE-SOURCE: CL 5491314 in //UE4/Release-4.22/... via CL 5491315
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)

[CL 5514611 by sebastien hillaire in Dev-VR branch]
2019-03-22 17:55:47 -04:00

939 lines
37 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
Reflection Environment - feature that provides HDR glossy reflections on any surfaces, leveraging precomputation to prefilter cubemaps of the scene
=============================================================================*/
#include "ReflectionEnvironment.h"
#include "Stats/Stats.h"
#include "HAL/IConsoleManager.h"
#include "RHI.h"
#include "UniformBuffer.h"
#include "ShaderParameters.h"
#include "RendererInterface.h"
#include "Shader.h"
#include "StaticBoundShaderState.h"
#include "SceneUtils.h"
#include "RHIStaticStates.h"
#include "PostProcess/SceneRenderTargets.h"
#include "GlobalShader.h"
#include "SceneRenderTargetParameters.h"
#include "DeferredShadingRenderer.h"
#include "BasePassRendering.h"
#include "ScenePrivate.h"
#include "PostProcess/SceneFilterRendering.h"
#include "PostProcess/PostProcessing.h"
#include "PostProcess/PostProcessSubsurface.h"
#include "PostProcess/ScreenSpaceReflections.h"
#include "LightRendering.h"
#include "LightPropagationVolumeSettings.h"
#include "PipelineStateCache.h"
#include "DistanceFieldAmbientOcclusion.h"
#include "SceneViewFamilyBlackboard.h"
#include "ScreenSpaceDenoise.h"
#include "RayTracing/RaytracingOptions.h"
DECLARE_GPU_STAT_NAMED(ReflectionEnvironment, TEXT("Reflection Environment"));
DECLARE_GPU_STAT_NAMED(RayTracingReflections, TEXT("Ray Tracing Reflections"));
DECLARE_GPU_STAT(SkyLightDiffuse);
extern TAutoConsoleVariable<int32> CVarLPVMixing;
static TAutoConsoleVariable<int32> CVarReflectionEnvironment(
TEXT("r.ReflectionEnvironment"),
1,
TEXT("Whether to render the reflection environment feature, which implements local reflections through Reflection Capture actors.\n")
TEXT(" 0: off\n")
TEXT(" 1: on and blend with scene (default)")
TEXT(" 2: on and overwrite scene (only in non-shipping builds)"),
ECVF_RenderThreadSafe | ECVF_Scalability);
int32 GReflectionEnvironmentLightmapMixing = 1;
FAutoConsoleVariableRef CVarReflectionEnvironmentLightmapMixing(
TEXT("r.ReflectionEnvironmentLightmapMixing"),
GReflectionEnvironmentLightmapMixing,
TEXT("Whether to mix indirect specular from reflection captures with indirect diffuse from lightmaps for rough surfaces."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GReflectionEnvironmentLightmapMixBasedOnRoughness = 1;
FAutoConsoleVariableRef CVarReflectionEnvironmentLightmapMixBasedOnRoughness(
TEXT("r.ReflectionEnvironmentLightmapMixBasedOnRoughness"),
GReflectionEnvironmentLightmapMixBasedOnRoughness,
TEXT("Whether to reduce lightmap mixing with reflection captures for very smooth surfaces. This is useful to make sure reflection captures match SSR / planar reflections in brightness."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GReflectionEnvironmentBeginMixingRoughness = .1f;
FAutoConsoleVariableRef CVarReflectionEnvironmentBeginMixingRoughness(
TEXT("r.ReflectionEnvironmentBeginMixingRoughness"),
GReflectionEnvironmentBeginMixingRoughness,
TEXT("Min roughness value at which to begin mixing reflection captures with lightmap indirect diffuse."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
float GReflectionEnvironmentEndMixingRoughness = .3f;
FAutoConsoleVariableRef CVarReflectionEnvironmentEndMixingRoughness(
TEXT("r.ReflectionEnvironmentEndMixingRoughness"),
GReflectionEnvironmentEndMixingRoughness,
TEXT("Min roughness value at which to end mixing reflection captures with lightmap indirect diffuse."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
int32 GReflectionEnvironmentLightmapMixLargestWeight = 10000;
FAutoConsoleVariableRef CVarReflectionEnvironmentLightmapMixLargestWeight(
TEXT("r.ReflectionEnvironmentLightmapMixLargestWeight"),
GReflectionEnvironmentLightmapMixLargestWeight,
TEXT("When set to 1 can be used to clamp lightmap mixing such that only darkening from lightmaps are applied to reflection captures."),
ECVF_Scalability | ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarDoTiledReflections(
TEXT("r.DoTiledReflections"),
1,
TEXT("Compute Reflection Environment with Tiled compute shader..\n")
TEXT(" 0: off\n")
TEXT(" 1: on (default)"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarSkySpecularOcclusionStrength(
TEXT("r.SkySpecularOcclusionStrength"),
1,
TEXT("Strength of skylight specular occlusion from DFAO (default is 1.0)"),
ECVF_RenderThreadSafe);
static int32 GRayTracingReflections = -1;
static FAutoConsoleVariableRef CVarReflectionsMethod(
TEXT("r.RayTracing.Reflections"),
GRayTracingReflections,
TEXT("-1: Value driven by postprocess volume (default) \n")
TEXT("0: use traditional rasterized SSR\n")
TEXT("1: use ray traced reflections\n")
);
static TAutoConsoleVariable<float> CVarReflectionScreenPercentage(
TEXT("r.RayTracing.Reflections.ScreenPercentage"),
100.0f,
TEXT("Screen percentage the reflections should be ray traced at (default = 100)."),
ECVF_RenderThreadSafe);
static int32 GRayTracingReflectionsSamplesPerPixel = -1;
static FAutoConsoleVariableRef CVarRayTracingReflectionsSamplesPerPixel(
TEXT("r.RayTracing.Reflections.SamplesPerPixel"),
GRayTracingReflectionsSamplesPerPixel,
TEXT("Sets the samples-per-pixel for reflections (default = -1 (driven by postprocesing volume))"));
static int32 GRayTracingReflectionsHeightFog = 1;
static FAutoConsoleVariableRef CVarRayTracingReflectionsHeightFog(
TEXT("r.RayTracing.Reflections.HeightFog"),
GRayTracingReflectionsHeightFog,
TEXT("Enables height fog in ray traced reflections (default = 1)"));
static TAutoConsoleVariable<int32> CVarUseReflectionDenoiser(
TEXT("r.Reflections.Denoiser"),
2,
TEXT("Choose the denoising algorithm.\n")
TEXT(" 0: Disabled;\n")
TEXT(" 1: Forces the default denoiser of the renderer;\n")
TEXT(" 2: GScreenSpaceDenoiser witch may be overriden by a third party plugin (default)."),
ECVF_RenderThreadSafe);
// to avoid having direct access from many places
static int GetReflectionEnvironmentCVar()
{
int32 RetVal = CVarReflectionEnvironment.GetValueOnAnyThread();
#if (UE_BUILD_SHIPPING || UE_BUILD_TEST)
// Disabling the debug part of this CVar when in shipping
if (RetVal == 2)
{
RetVal = 1;
}
#endif
return RetVal;
}
FVector GetReflectionEnvironmentRoughnessMixingScaleBiasAndLargestWeight()
{
float RoughnessMixingRange = 1.0f / FMath::Max(GReflectionEnvironmentEndMixingRoughness - GReflectionEnvironmentBeginMixingRoughness, .001f);
if (GReflectionEnvironmentLightmapMixing == 0)
{
return FVector(0, 0, GReflectionEnvironmentLightmapMixLargestWeight);
}
if (GReflectionEnvironmentEndMixingRoughness == 0.0f && GReflectionEnvironmentBeginMixingRoughness == 0.0f)
{
// Make sure a Roughness of 0 results in full mixing when disabling roughness-based mixing
return FVector(0, 1, GReflectionEnvironmentLightmapMixLargestWeight);
}
if (!GReflectionEnvironmentLightmapMixBasedOnRoughness)
{
return FVector(0, 1, GReflectionEnvironmentLightmapMixLargestWeight);
}
return FVector(RoughnessMixingRange, -GReflectionEnvironmentBeginMixingRoughness * RoughnessMixingRange, GReflectionEnvironmentLightmapMixLargestWeight);
}
bool IsReflectionEnvironmentAvailable(ERHIFeatureLevel::Type InFeatureLevel)
{
return (InFeatureLevel >= ERHIFeatureLevel::SM4) && (GetReflectionEnvironmentCVar() != 0);
}
bool IsReflectionCaptureAvailable()
{
static IConsoleVariable* AllowStaticLightingVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.AllowStaticLighting"));
return (!AllowStaticLightingVar || AllowStaticLightingVar->GetInt() != 0);
}
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FReflectionUniformParameters, "ReflectionStruct");
void SetupReflectionUniformParameters(const FViewInfo& View, FReflectionUniformParameters& OutParameters)
{
FTexture* SkyLightTextureResource = GBlackTextureCube;
FTexture* SkyLightBlendDestinationTextureResource = GBlackTextureCube;
float ApplySkyLightMask = 0;
float BlendFraction = 0;
bool bSkyLightIsDynamic = false;
float SkyAverageBrightness = 1.0f;
const bool bApplySkyLight = View.Family->EngineShowFlags.SkyLighting;
const FScene* Scene = (const FScene*)View.Family->Scene;
if (Scene
&& Scene->SkyLight
&& Scene->SkyLight->ProcessedTexture
&& bApplySkyLight)
{
const FSkyLightSceneProxy& SkyLight = *Scene->SkyLight;
SkyLightTextureResource = SkyLight.ProcessedTexture;
BlendFraction = SkyLight.BlendFraction;
if (SkyLight.BlendFraction > 0.0f && SkyLight.BlendDestinationProcessedTexture)
{
if (SkyLight.BlendFraction < 1.0f)
{
SkyLightBlendDestinationTextureResource = SkyLight.BlendDestinationProcessedTexture;
}
else
{
SkyLightTextureResource = SkyLight.BlendDestinationProcessedTexture;
BlendFraction = 0;
}
}
ApplySkyLightMask = 1;
bSkyLightIsDynamic = !SkyLight.bHasStaticLighting && !SkyLight.bWantsStaticShadowing;
SkyAverageBrightness = SkyLight.AverageBrightness;
}
const int32 CubemapWidth = SkyLightTextureResource->GetSizeX();
const float SkyMipCount = FMath::Log2(CubemapWidth) + 1.0f;
OutParameters.SkyLightCubemap = SkyLightTextureResource->TextureRHI;
OutParameters.SkyLightCubemapSampler = SkyLightTextureResource->SamplerStateRHI;
OutParameters.SkyLightBlendDestinationCubemap = SkyLightBlendDestinationTextureResource->TextureRHI;
OutParameters.SkyLightBlendDestinationCubemapSampler = SkyLightBlendDestinationTextureResource->SamplerStateRHI;
OutParameters.SkyLightParameters = FVector4(SkyMipCount - 1.0f, ApplySkyLightMask, bSkyLightIsDynamic ? 1.0f : 0.0f, BlendFraction);
OutParameters.SkyLightCubemapBrightness = SkyAverageBrightness;
// Note: GBlackCubeArrayTexture has an alpha of 0, which is needed to represent invalid data so the sky cubemap can still be applied
FTextureRHIParamRef CubeArrayTexture = View.FeatureLevel >= ERHIFeatureLevel::SM5 ? GBlackCubeArrayTexture->TextureRHI : GBlackTextureCube->TextureRHI;
if (View.Family->EngineShowFlags.ReflectionEnvironment
&& View.FeatureLevel >= ERHIFeatureLevel::SM5
&& Scene
&& Scene->ReflectionSceneData.CubemapArray.IsValid())
{
CubeArrayTexture = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().ShaderResourceTexture;
}
OutParameters.ReflectionCubemap = CubeArrayTexture;
OutParameters.ReflectionCubemapSampler = TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
OutParameters.PreIntegratedGF = GSystemTextures.PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture;
OutParameters.PreIntegratedGFSampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI();
}
TUniformBufferRef<FReflectionUniformParameters> CreateReflectionUniformBuffer(const class FViewInfo& View, EUniformBufferUsage Usage)
{
FReflectionUniformParameters ReflectionStruct;
SetupReflectionUniformParameters(View, ReflectionStruct);
return CreateUniformBufferImmediate(ReflectionStruct, Usage);
}
void FReflectionEnvironmentCubemapArray::InitDynamicRHI()
{
if (GetFeatureLevel() >= ERHIFeatureLevel::SM5)
{
const int32 NumReflectionCaptureMips = FMath::CeilLogTwo(CubemapSize) + 1;
ReleaseCubeArray();
FPooledRenderTargetDesc Desc(
FPooledRenderTargetDesc::CreateCubemapDesc(
CubemapSize,
// Alpha stores sky mask
PF_FloatRGBA,
FClearValueBinding::None,
TexCreate_None,
TexCreate_None,
false,
// Cubemap array of 1 produces a regular cubemap, so guarantee it will be allocated as an array
FMath::Max<uint32>(MaxCubemaps, 2),
NumReflectionCaptureMips
)
);
Desc.AutoWritable = false;
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
// Allocate TextureCubeArray for the scene's reflection captures
GRenderTargetPool.FindFreeElement(RHICmdList, Desc, ReflectionEnvs, TEXT("ReflectionEnvs"));
}
}
void FReflectionEnvironmentCubemapArray::ReleaseCubeArray()
{
// it's unlikely we can reuse the TextureCubeArray so when we release it we want to really remove it
GRenderTargetPool.FreeUnusedResource(ReflectionEnvs);
}
void FReflectionEnvironmentCubemapArray::ReleaseDynamicRHI()
{
ReleaseCubeArray();
}
void FReflectionEnvironmentSceneData::ResizeCubemapArrayGPU(uint32 InMaxCubemaps, int32 InCubemapSize)
{
check(IsInRenderingThread());
// If the cubemap array isn't setup yet then no copying/reallocation is necessary. Just go through the old path
if (!CubemapArray.IsInitialized())
{
CubemapArraySlotsUsed.Init(false, InMaxCubemaps);
CubemapArray.UpdateMaxCubemaps(InMaxCubemaps, InCubemapSize);
return;
}
// Generate a remapping table for the elements
TArray<int32> IndexRemapping;
int32 Count = 0;
for (int i = 0; i < CubemapArray.GetMaxCubemaps(); i++)
{
bool bUsed = i < CubemapArraySlotsUsed.Num() ? CubemapArraySlotsUsed[i] : false;
if (bUsed)
{
IndexRemapping.Add(Count);
Count++;
}
else
{
IndexRemapping.Add(-1);
}
}
// Reset the CubemapArraySlotsUsed array (we'll recompute it below)
CubemapArraySlotsUsed.Init(false, InMaxCubemaps);
// Spin through the AllocatedReflectionCaptureState map and remap the indices based on the LUT
TArray<const UReflectionCaptureComponent*> Components;
AllocatedReflectionCaptureState.GetKeys(Components);
int32 UsedCubemapCount = 0;
for (int32 i=0; i<Components.Num(); i++ )
{
FCaptureComponentSceneState* ComponentStatePtr = AllocatedReflectionCaptureState.Find(Components[i]);
check(ComponentStatePtr->CubemapIndex < IndexRemapping.Num());
int32 NewIndex = IndexRemapping[ComponentStatePtr->CubemapIndex];
CubemapArraySlotsUsed[NewIndex] = true;
ComponentStatePtr->CubemapIndex = NewIndex;
check(ComponentStatePtr->CubemapIndex > -1);
UsedCubemapCount = FMath::Max(UsedCubemapCount, ComponentStatePtr->CubemapIndex + 1);
}
// Clear elements in the remapping array which are outside the range of the used components (these were allocated but not used)
for (int i = 0; i < IndexRemapping.Num(); i++)
{
if (IndexRemapping[i] >= UsedCubemapCount)
{
IndexRemapping[i] = -1;
}
}
CubemapArray.ResizeCubemapArrayGPU(InMaxCubemaps, InCubemapSize, IndexRemapping);
}
void FReflectionEnvironmentCubemapArray::ResizeCubemapArrayGPU(uint32 InMaxCubemaps, int32 InCubemapSize, const TArray<int32>& IndexRemapping)
{
check(IsInRenderingThread());
check(GetFeatureLevel() >= ERHIFeatureLevel::SM5);
check(IsInitialized());
check(InCubemapSize == CubemapSize);
// Take a reference to the old cubemap array and then release it to prevent it getting destroyed during InitDynamicRHI
TRefCountPtr<IPooledRenderTarget> OldReflectionEnvs = ReflectionEnvs;
ReflectionEnvs = nullptr;
int OldMaxCubemaps = MaxCubemaps;
MaxCubemaps = InMaxCubemaps;
InitDynamicRHI();
FTextureRHIRef TexRef = OldReflectionEnvs->GetRenderTargetItem().TargetableTexture;
FRHICommandListImmediate& RHICmdList = FRHICommandListExecutor::GetImmediateCommandList();
const int32 NumMips = FMath::CeilLogTwo(InCubemapSize) + 1;
{
SCOPED_DRAW_EVENT(RHICmdList, ReflectionEnvironment_ResizeCubemapArray);
SCOPED_GPU_STAT(RHICmdList, ReflectionEnvironment)
// Copy the cubemaps, remapping the elements as necessary
FResolveParams ResolveParams;
ResolveParams.Rect = FResolveRect();
for (int32 SourceCubemapIndex = 0; SourceCubemapIndex < OldMaxCubemaps; SourceCubemapIndex++)
{
int32 DestCubemapIndex = IndexRemapping[SourceCubemapIndex];
if (DestCubemapIndex != -1)
{
ResolveParams.SourceArrayIndex = SourceCubemapIndex;
ResolveParams.DestArrayIndex = DestCubemapIndex;
check(SourceCubemapIndex < OldMaxCubemaps);
check(DestCubemapIndex < (int32)MaxCubemaps);
for (int Face = 0; Face < 6; Face++)
{
ResolveParams.CubeFace = (ECubeFace)Face;
for (int Mip = 0; Mip < NumMips; Mip++)
{
ResolveParams.MipIndex = Mip;
//@TODO: We should use an explicit copy method for this rather than CopyToResolveTarget, but that doesn't exist right now.
// For now, we'll just do this on RHIs where we know CopyToResolveTarget does the right thing. In future we should look to
// add a a new RHI method
check(GRHISupportsResolveCubemapFaces);
RHICmdList.CopyToResolveTarget(OldReflectionEnvs->GetRenderTargetItem().ShaderResourceTexture, ReflectionEnvs->GetRenderTargetItem().ShaderResourceTexture, ResolveParams);
}
}
}
}
}
GRenderTargetPool.FreeUnusedResource(OldReflectionEnvs);
}
void FReflectionEnvironmentCubemapArray::UpdateMaxCubemaps(uint32 InMaxCubemaps, int32 InCubemapSize)
{
MaxCubemaps = InMaxCubemaps;
CubemapSize = InCubemapSize;
// Reallocate the cubemap array
if (IsInitialized())
{
UpdateRHI();
}
else
{
InitResource();
}
}
class FSkyLightParameters
{
public:
void Bind(const FShaderParameterMap& ParameterMap)
{
ApplyBentNormalAO.Bind(ParameterMap, TEXT("ApplyBentNormalAO"));
InvSkySpecularOcclusionStrength.Bind(ParameterMap, TEXT("InvSkySpecularOcclusionStrength"));
OcclusionTintAndMinOcclusion.Bind(ParameterMap, TEXT("OcclusionTintAndMinOcclusion"));
ContrastAndNormalizeMulAdd.Bind(ParameterMap, TEXT("ContrastAndNormalizeMulAdd"));
OcclusionExponent.Bind(ParameterMap, TEXT("OcclusionExponent"));
OcclusionCombineMode.Bind(ParameterMap, TEXT("OcclusionCombineMode"));
}
template<typename ShaderRHIParamRef, typename TRHICmdList>
void SetParameters(TRHICmdList& RHICmdList, const ShaderRHIParamRef& ShaderRHI, bool bApplyBentNormalAO, float SkySpecularOcclusionStrength, const FSkyLightSceneProxy* SkyLight)
{
float SkyLightContrast = 0.01f;
float SkyLightOcclusionExponent = 1.0f;
FVector4 SkyLightOcclusionTintAndMinOcclusion(0.0f, 0.0f, 0.0f, 0.0f);
EOcclusionCombineMode SkyLightOcclusionCombineMode = EOcclusionCombineMode::OCM_MAX;
if (SkyLight)
{
FDistanceFieldAOParameters Parameters(SkyLight->OcclusionMaxDistance, SkyLight->Contrast);
SkyLightContrast = Parameters.Contrast;
SkyLightOcclusionExponent = SkyLight->OcclusionExponent;
SkyLightOcclusionTintAndMinOcclusion = FVector4(SkyLight->OcclusionTint);
SkyLightOcclusionTintAndMinOcclusion.W = SkyLight->MinOcclusion;
SkyLightOcclusionCombineMode = SkyLight->OcclusionCombineMode;
}
// Scale and bias to remap the contrast curve to [0,1]
const float Min = 1 / (1 + FMath::Exp(-SkyLightContrast * (0 * 10 - 5)));
const float Max = 1 / (1 + FMath::Exp(-SkyLightContrast * (1 * 10 - 5)));
const float Mul = 1.0f / (Max - Min);
const float Add = -Min / (Max - Min);
SetShaderValue(RHICmdList, ShaderRHI, ContrastAndNormalizeMulAdd, FVector(SkyLightContrast, Mul, Add));
SetShaderValue(RHICmdList, ShaderRHI, OcclusionExponent, SkyLightOcclusionExponent);
SetShaderValue(RHICmdList, ShaderRHI, OcclusionTintAndMinOcclusion, SkyLightOcclusionTintAndMinOcclusion);
SetShaderValue(RHICmdList, ShaderRHI, OcclusionCombineMode, SkyLightOcclusionCombineMode == OCM_Minimum ? 0.0f : 1.0f);
SetShaderValue(RHICmdList, ShaderRHI, ApplyBentNormalAO, bApplyBentNormalAO ? 1.0f : 0.0f);
SetShaderValue(RHICmdList, ShaderRHI, InvSkySpecularOcclusionStrength, 1.0f / FMath::Max(SkySpecularOcclusionStrength, .1f));
}
friend FArchive& operator<<(FArchive& Ar, FSkyLightParameters& P)
{
Ar << P.ApplyBentNormalAO;
Ar << P.InvSkySpecularOcclusionStrength;
Ar << P.OcclusionTintAndMinOcclusion;
Ar << P.ContrastAndNormalizeMulAdd;
Ar << P.OcclusionExponent;
Ar << P.OcclusionCombineMode;
return Ar;
}
private:
FShaderParameter ApplyBentNormalAO;
FShaderParameter InvSkySpecularOcclusionStrength;
FShaderParameter OcclusionTintAndMinOcclusion;
FShaderParameter ContrastAndNormalizeMulAdd;
FShaderParameter OcclusionExponent;
FShaderParameter OcclusionCombineMode;
};
IMPLEMENT_GLOBAL_SHADER_PARAMETER_STRUCT(FReflectionCaptureShaderData, "ReflectionCapture");
/** Pixel shader that does tiled deferred culling of reflection captures, then sorts and composites them. */
class FReflectionEnvironmentSkyLightingPS : public FGlobalShader
{
DECLARE_GLOBAL_SHADER(FReflectionEnvironmentSkyLightingPS);
class FHasBoxCaptures : SHADER_PERMUTATION_BOOL("REFLECTION_COMPOSITE_HAS_BOX_CAPTURES");
class FHasSphereCaptures : SHADER_PERMUTATION_BOOL("REFLECTION_COMPOSITE_HAS_SPHERE_CAPTURES");
class FDFAOIndirectOcclusion : SHADER_PERMUTATION_BOOL("SUPPORT_DFAO_INDIRECT_OCCLUSION");
class FSpecularBounce : SHADER_PERMUTATION_BOOL("SPECULAR_BOUNCE");
class FSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_SKY_LIGHT");
class FDynamicSkyLight : SHADER_PERMUTATION_BOOL("ENABLE_DYNAMIC_SKY_LIGHT");
class FSkyShadowing : SHADER_PERMUTATION_BOOL("APPLY_SKY_SHADOWING");
class FRayTracedReflections : SHADER_PERMUTATION_BOOL("RAY_TRACED_REFLECTIONS");
using FPermutationDomain = TShaderPermutationDomain<
FHasBoxCaptures,
FHasSphereCaptures,
FDFAOIndirectOcclusion,
FSpecularBounce,
FSkyLight,
FDynamicSkyLight,
FSkyShadowing,
FRayTracedReflections>;
static FPermutationDomain RemapPermutation(FPermutationDomain PermutationVector)
{
// Environment captures have simple specular bounce without reflection captures.
if (PermutationVector.Get<FSpecularBounce>())
{
PermutationVector.Set<FSkyLight>(false);
PermutationVector.Set<FDFAOIndirectOcclusion>(false);
PermutationVector.Set<FHasBoxCaptures>(false);
PermutationVector.Set<FHasSphereCaptures>(false);
}
// FSkyLightingDynamicSkyLight requires FSkyLightingSkyLight.
if (!PermutationVector.Get<FSkyLight>())
{
PermutationVector.Set<FDynamicSkyLight>(false);
}
// FSkyLightingSkyShadowing requires FSkyLightingDynamicSkyLight.
if (!PermutationVector.Get<FDynamicSkyLight>())
{
PermutationVector.Set<FSkyShadowing>(false);
}
return PermutationVector;
}
static FPermutationDomain BuildPermutationVector(const FViewInfo& View, bool bBoxCapturesOnly, bool bSphereCapturesOnly, bool bSupportDFAOIndirectOcclusion, bool bSpecularBounce, bool bEnableSkyLight, bool bEnableDynamicSkyLight, bool bApplySkyShadowing, bool bRayTracedReflections)
{
FPermutationDomain PermutationVector;
PermutationVector.Set<FHasBoxCaptures>(bBoxCapturesOnly);
PermutationVector.Set<FHasSphereCaptures>(bSphereCapturesOnly);
PermutationVector.Set<FDFAOIndirectOcclusion>(bSupportDFAOIndirectOcclusion);
PermutationVector.Set<FSpecularBounce>(bSpecularBounce);
PermutationVector.Set<FSkyLight>(bEnableSkyLight);
PermutationVector.Set<FDynamicSkyLight>(bEnableDynamicSkyLight);
PermutationVector.Set<FSkyShadowing>(bApplySkyShadowing);
PermutationVector.Set<FRayTracedReflections>(bRayTracedReflections);
return RemapPermutation(PermutationVector);
}
static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters)
{
if (!IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM4))
{
return false;
}
FPermutationDomain PermutationVector(Parameters.PermutationId);
return PermutationVector == RemapPermutation(PermutationVector);
}
static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Parameters, OutEnvironment);
OutEnvironment.SetDefine(TEXT("MAX_CAPTURES"), GMaxNumReflectionCaptures);
OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization);
FForwardLightingParameters::ModifyCompilationEnvironment(Parameters.Platform, OutEnvironment);
}
public:
FReflectionEnvironmentSkyLightingPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
{
SceneTextureParameters.Bind(Initializer);
ReflectionCubemap.Bind(Initializer.ParameterMap,TEXT("ReflectionCubemap"));
ReflectionCubemapSampler.Bind(Initializer.ParameterMap,TEXT("ReflectionCubemapSampler"));
ScreenSpaceReflectionsTexture.Bind(Initializer.ParameterMap, TEXT("ScreenSpaceReflectionsTexture"));
ScreenSpaceReflectionsSampler.Bind(Initializer.ParameterMap, TEXT("ScreenSpaceReflectionsSampler"));
PreIntegratedGF.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGF"));
PreIntegratedGFSampler.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGFSampler"));
DFAOUpsampleParameters.Bind(Initializer.ParameterMap);
SkyLightParameters.Bind(Initializer.ParameterMap);
}
FReflectionEnvironmentSkyLightingPS()
{
}
template<typename TRHICommandList>
void SetParameters(
TRHICommandList& RHICmdList,
const FViewInfo& View,
FTextureRHIParamRef SSRTexture,
const TRefCountPtr<IPooledRenderTarget>& DynamicBentNormalAO
)
{
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
FGlobalShader::SetParameters<FViewUniformShaderParameters>(RHICmdList, ShaderRHI, View.ViewUniformBuffer);
SceneTextureParameters.Set(RHICmdList, ShaderRHI, View.FeatureLevel, ESceneTextureSetupMode::All);
FScene* Scene = (FScene*)View.Family->Scene;
FTextureRHIParamRef CubemapArray;
if (Scene->ReflectionSceneData.CubemapArray.IsValid() &&
Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().IsValid())
{
CubemapArray = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().ShaderResourceTexture;
}
else
{
CubemapArray = GBlackCubeArrayTexture->TextureRHI;
}
SetTextureParameter(
RHICmdList,
ShaderRHI,
ReflectionCubemap,
ReflectionCubemapSampler,
TStaticSamplerState<SF_Trilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(),
CubemapArray);
SetTextureParameter(RHICmdList, ShaderRHI, ScreenSpaceReflectionsTexture, ScreenSpaceReflectionsSampler, TStaticSamplerState<SF_Point>::GetRHI(), SSRTexture);
SetUniformBufferParameter(RHICmdList, ShaderRHI, GetUniformBufferParameter<FReflectionCaptureShaderData>(), View.ReflectionCaptureUniformBuffer);
SetTextureParameter(RHICmdList, ShaderRHI, PreIntegratedGF, PreIntegratedGFSampler, TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp>::GetRHI(), GSystemTextures.PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture);
FReflectionUniformParameters ReflectionUniformParameters;
SetupReflectionUniformParameters(View, ReflectionUniformParameters);
SetUniformBufferParameterImmediate(RHICmdList, ShaderRHI, GetUniformBufferParameter<FReflectionUniformParameters>(), ReflectionUniformParameters);
bool const bApplyBentNormalAO = DynamicBentNormalAO ? true : false;
SkyLightParameters.SetParameters(RHICmdList, ShaderRHI, bApplyBentNormalAO, CVarSkySpecularOcclusionStrength.GetValueOnRenderThread(), Scene->SkyLight);
SetUniformBufferParameter(RHICmdList, ShaderRHI, GetUniformBufferParameter<FForwardLightData>(), View.ForwardLightingResources->ForwardLightDataUniformBuffer);
DFAOUpsampleParameters.Set(RHICmdList, ShaderRHI, View, DynamicBentNormalAO);
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
Ar << SceneTextureParameters;
Ar << ReflectionCubemap;
Ar << ReflectionCubemapSampler;
Ar << ScreenSpaceReflectionsTexture;
Ar << ScreenSpaceReflectionsSampler;
Ar << PreIntegratedGF;
Ar << PreIntegratedGFSampler;
Ar << DFAOUpsampleParameters;
Ar << SkyLightParameters;
return bShaderHasOutdatedParameters;
}
private:
FSceneTextureShaderParameters SceneTextureParameters;
FShaderResourceParameter ReflectionCubemap;
FShaderResourceParameter ReflectionCubemapSampler;
FShaderResourceParameter ScreenSpaceReflectionsTexture;
FShaderResourceParameter ScreenSpaceReflectionsSampler;
FShaderResourceParameter PreIntegratedGF;
FShaderResourceParameter PreIntegratedGFSampler;
FDFAOUpsampleParameters DFAOUpsampleParameters;
FSkyLightParameters SkyLightParameters;
};
IMPLEMENT_GLOBAL_SHADER(FReflectionEnvironmentSkyLightingPS, "/Engine/Private/ReflectionEnvironmentPixelShader.usf", "ReflectionEnvironmentSkyLighting", SF_Pixel);
bool FDeferredShadingSceneRenderer::ShouldDoReflectionEnvironment() const
{
const ERHIFeatureLevel::Type SceneFeatureLevel = Scene->GetFeatureLevel();
return IsReflectionEnvironmentAvailable(SceneFeatureLevel)
&& Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num()
&& ViewFamily.EngineShowFlags.ReflectionEnvironment;
}
void FDeferredShadingSceneRenderer::RenderDeferredReflectionsAndSkyLighting(FRHICommandListImmediate& RHICmdList, TRefCountPtr<IPooledRenderTarget>& DynamicBentNormalAO, TRefCountPtr<IPooledRenderTarget>& VelocityRT)
{
check(RHICmdList.IsOutsideRenderPass());
if (ViewFamily.EngineShowFlags.VisualizeLightCulling || !ViewFamily.EngineShowFlags.Lighting)
{
return;
}
// If we're currently capturing a reflection capture, output SpecularColor * IndirectIrradiance for metals so they are not black in reflections,
// Since we don't have multiple bounce specular reflections
bool bReflectionCapture = false;
bool bAnyViewWithRaytracingReflections = false;
for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++)
{
const FViewInfo& View = Views[ViewIndex];
bReflectionCapture = bReflectionCapture || View.bIsReflectionCapture;
//#dxr_todo: multiview case
bAnyViewWithRaytracingReflections = bAnyViewWithRaytracingReflections || (View.FinalPostProcessSettings.ReflectionsType == EReflectionsType::RayTracing);
}
const bool bRayTracedReflections = IsRayTracingEnabled() && (GRayTracingReflections < 0 ? bAnyViewWithRaytracingReflections : GRayTracingReflections);
// The specular sky light contribution is also needed by RT Reflections as a fallback.
const bool bSkyLight = Scene->SkyLight
&& Scene->SkyLight->ProcessedTexture
&& !Scene->SkyLight->bHasStaticLighting;
bool bDynamicSkyLight = ShouldRenderDeferredDynamicSkyLight(Scene, ViewFamily);
bool bApplySkyShadowing = false;
if (bDynamicSkyLight)
{
SCOPED_DRAW_EVENT(RHICmdList, SkyLightDiffuse);
SCOPED_GPU_STAT(RHICmdList, SkyLightDiffuse);
FDistanceFieldAOParameters Parameters(Scene->SkyLight->OcclusionMaxDistance, Scene->SkyLight->Contrast);
extern int32 GDistanceFieldAOApplyToStaticIndirect;
if (Scene->SkyLight->bCastShadows
&& !GDistanceFieldAOApplyToStaticIndirect
&& ShouldRenderDistanceFieldAO()
&& ViewFamily.EngineShowFlags.AmbientOcclusion)
{
bApplySkyShadowing = RenderDistanceFieldLighting(RHICmdList, Parameters, VelocityRT, DynamicBentNormalAO, false, false);
}
}
check(RHICmdList.IsOutsideRenderPass());
FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList);
const bool bReflectionEnv = ShouldDoReflectionEnvironment();
for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++)
{
FViewInfo& View = Views[ViewIndex];
const bool bScreenSpaceReflections = !bRayTracedReflections && ShouldRenderScreenSpaceReflections(Views[ViewIndex]);
TRefCountPtr<IPooledRenderTarget> ReflectionsColor = GSystemTextures.BlackDummy;
if (bRayTracedReflections)
{
#if RHI_RAYTRACING
SCOPED_DRAW_EVENT(RHICmdList, RayTracingReflections);
SCOPED_GPU_STAT(RHICmdList, RayTracingReflections);
FRDGBuilder GraphBuilder(RHICmdList); // TODO: convert the entire reflections to render graph.
FSceneViewFamilyBlackboard SceneBlackboard;
SetupSceneViewFamilyBlackboard(GraphBuilder, &SceneBlackboard);
IScreenSpaceDenoiser::FReflectionsRayTracingConfig RayTracingConfig;
RayTracingConfig.ResolutionFraction = FMath::Clamp(CVarReflectionScreenPercentage.GetValueOnRenderThread() / 100.0f, 0.25f, 1.0f);
int32 RayTracingReflectionsSPP = GRayTracingReflectionsSamplesPerPixel > -1 ? GRayTracingReflectionsSamplesPerPixel : View.FinalPostProcessSettings.RayTracingReflectionsSamplesPerPixel;
int32 DenoiserMode = CVarUseReflectionDenoiser.GetValueOnRenderThread();
const bool bDenoise = DenoiserMode != 0 && RayTracingReflectionsSPP == 1;
if (!bDenoise)
{
RayTracingConfig.ResolutionFraction = 1.0f;
}
// Ray trace the reflection.
IScreenSpaceDenoiser::FReflectionsInputs DenoiserInputs;
RenderRayTracingReflections(
GraphBuilder,
View, &DenoiserInputs.Color, &DenoiserInputs.RayHitDistance, &DenoiserInputs.RayImaginaryDepth,
RayTracingReflectionsSPP, GRayTracingReflectionsHeightFog, RayTracingConfig.ResolutionFraction);
// Denoise the reflections.
if (bDenoise)
{
const IScreenSpaceDenoiser* DefaultDenoiser = IScreenSpaceDenoiser::GetDefaultDenoiser();
const IScreenSpaceDenoiser* DenoiserToUse = DenoiserMode == 1 ? DefaultDenoiser : GScreenSpaceDenoiser;
// Standard event scope for denoiser to have all profiling information not matter what, and with explicit detection of third party.
RDG_EVENT_SCOPE(GraphBuilder, "%s%s(Reflections) %dx%d",
DenoiserToUse != DefaultDenoiser ? TEXT("ThirdParty ") : TEXT(""),
DenoiserToUse->GetDebugName(),
View.ViewRect.Width(), View.ViewRect.Height());
IScreenSpaceDenoiser::FReflectionsOutputs DenoiserOutputs = DenoiserToUse->DenoiseReflections(
GraphBuilder,
View,
&View.PrevViewInfo,
SceneBlackboard,
DenoiserInputs,
RayTracingConfig);
GraphBuilder.QueueTextureExtraction(DenoiserOutputs.Color, &ReflectionsColor);
}
else
{
// The performance of ray tracing does not allow to run without a denoiser in real time.
// Multiple rays per pixel is unsupported by the denoiser that will most likely more bound by to
// many rays than exporting the hit distance buffer. Therefore no permutation of the ray generation
// shader has been judged required to be supported.
GraphBuilder.RemoveUnusedTextureWarning(DenoiserInputs.RayHitDistance);
GraphBuilder.QueueTextureExtraction(DenoiserInputs.Color, &ReflectionsColor);
}
GraphBuilder.Execute();
#endif
}
else if (bScreenSpaceReflections)
{
RenderScreenSpaceReflections(RHICmdList, View, ReflectionsColor, VelocityRT);
}
bool bPlanarReflections = false;
if (!bRayTracedReflections)
{
bPlanarReflections = RenderDeferredPlanarReflections(RHICmdList, View, false, ReflectionsColor);
}
bool bRequiresApply = bSkyLight || bDynamicSkyLight || bReflectionEnv || bScreenSpaceReflections || bPlanarReflections || bRayTracedReflections;
if (bRequiresApply)
{
SCOPED_GPU_STAT(RHICmdList, ReflectionEnvironment);
SCOPED_DRAW_EVENTF(RHICmdList, ReflectionEnvironment, TEXT("ReflectionEnvironmentAndSky"));
// Render the reflection environment with tiled deferred culling
bool bHasBoxCaptures = (View.NumBoxReflectionCaptures > 0);
bool bHasSphereCaptures = (View.NumSphereReflectionCaptures > 0);
TShaderMapRef<FPostProcessVS> VertexShader(View.ShaderMap);
auto PermutationVector = FReflectionEnvironmentSkyLightingPS::BuildPermutationVector(View, bHasBoxCaptures, bHasSphereCaptures, DynamicBentNormalAO != NULL, bReflectionCapture, bSkyLight, bDynamicSkyLight, bApplySkyShadowing, bRayTracedReflections);
TShaderMapRef<FReflectionEnvironmentSkyLightingPS> PixelShader(View.ShaderMap, PermutationVector);
FGraphicsPipelineStateInitializer GraphicsPSOInit;
SceneContext.BeginRenderingSceneColor(
RHICmdList,
bReflectionCapture ? ESimpleRenderTargetMode::EUninitializedColorExistingDepth : ESimpleRenderTargetMode::EExistingColorAndDepth,
FExclusiveDepthStencil::DepthRead_StencilWrite);
RHICmdList.ApplyCachedRenderTargets(GraphicsPSOInit);
extern int32 GAOOverwriteSceneColor;
if (bReflectionCapture)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One>::GetRHI();
}
else if (GetReflectionEnvironmentCVar() == 2 || GAOOverwriteSceneColor)
{
// override scene color for debugging
GraphicsPSOInit.BlendState = TStaticBlendState<>::GetRHI();
}
else
{
const bool bCheckerboardSubsurfaceRendering = FRCPassPostProcessSubsurface::RequiresCheckerboardSubsurfaceRendering(SceneContext.GetSceneColorFormat());
if (bCheckerboardSubsurfaceRendering)
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGB, BO_Add, BF_One, BF_One>::GetRHI();
}
else
{
GraphicsPSOInit.BlendState = TStaticBlendState<CW_RGBA, BO_Add, BF_One, BF_One, BO_Add, BF_One, BF_One>::GetRHI();
}
}
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<FM_Solid, CM_None>::GetRHI();
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GFilterVertexDeclaration.VertexDeclarationRHI;
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = GETSAFERHISHADER_VERTEX(*VertexShader);
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = GETSAFERHISHADER_PIXEL(*PixelShader);
GraphicsPSOInit.PrimitiveType = PT_TriangleList;
SetGraphicsPipelineState(RHICmdList, GraphicsPSOInit);
RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f);
PixelShader->SetParameters(RHICmdList, View, ReflectionsColor->GetRenderTargetItem().ShaderResourceTexture, DynamicBentNormalAO);
if (bReflectionCapture)
{
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()),
SceneContext.GetBufferSizeXY(),
*VertexShader,
EDRF_UseTriangleOptimization);
}
else
{
DrawRectangle(
RHICmdList,
0, 0,
View.ViewRect.Width(), View.ViewRect.Height(),
View.ViewRect.Min.X, View.ViewRect.Min.Y,
View.ViewRect.Width(), View.ViewRect.Height(),
FIntPoint(View.ViewRect.Width(), View.ViewRect.Height()),
SceneContext.GetBufferSizeXY(),
*VertexShader);
}
SceneContext.FinishRenderingSceneColor(RHICmdList);
ResolveSceneColor(RHICmdList);
}
}
}