// Copyright 1998-2016 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 "RendererPrivate.h" #include "ScenePrivate.h" #include "SceneFilterRendering.h" #include "PostProcessing.h" #include "UniformBuffer.h" #include "ShaderParameters.h" #include "ScreenRendering.h" #include "ScreenSpaceReflections.h" #include "PostProcessTemporalAA.h" #include "PostProcessDownsample.h" #include "ReflectionEnvironment.h" #include "ShaderParameterUtils.h" #include "LightRendering.h" #include "SceneUtils.h" #include "LightPropagationVolumeBlendable.h" DECLARE_FLOAT_COUNTER_STAT(TEXT("Reflection Environment"), Stat_GPU_ReflectionEnvironment, STATGROUP_GPU); /** Tile size for the reflection environment compute shader, tweaked for 680 GTX. */ const int32 GReflectionEnvironmentTileSizeX = 16; const int32 GReflectionEnvironmentTileSizeY = 16; extern TAutoConsoleVariable CVarLPVMixing; static TAutoConsoleVariable CVarDiffuseFromCaptures( TEXT("r.DiffuseFromCaptures"), 0, TEXT("Apply indirect diffuse lighting from captures instead of lightmaps.\n") TEXT(" 0 is off (default), 1 is on"), ECVF_RenderThreadSafe); static TAutoConsoleVariable 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); static TAutoConsoleVariable 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 CVarSkySpecularOcclusionStrength( TEXT("r.SkySpecularOcclusionStrength"), 1, TEXT("Strength of skylight specular occlusion from DFAO (default is 1.0)"), 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; } 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); } 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(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 FReflectionEnvironmentCubemapArray::UpdateMaxCubemaps(uint32 InMaxCubemaps, int32 InCubemapSize) { MaxCubemaps = InMaxCubemaps; CubemapSize = InCubemapSize; // Reallocate the cubemap array if (IsInitialized()) { UpdateRHI(); } else { InitResource(); } } class FDistanceFieldAOSpecularOcclusionParameters { public: void Bind(const FShaderParameterMap& ParameterMap) { BentNormalAOTexture.Bind(ParameterMap, TEXT("BentNormalAOTexture")); BentNormalAOSampler.Bind(ParameterMap, TEXT("BentNormalAOSampler")); ApplyBentNormalAO.Bind(ParameterMap, TEXT("ApplyBentNormalAO")); InvSkySpecularOcclusionStrength.Bind(ParameterMap, TEXT("InvSkySpecularOcclusionStrength")); OcclusionTintAndMinOcclusion.Bind(ParameterMap, TEXT("OcclusionTintAndMinOcclusion")); } template void SetParameters(TRHICmdList& RHICmdList, const ShaderRHIParamRef& ShaderRHI, const TRefCountPtr& DynamicBentNormalAO, float SkySpecularOcclusionStrength, const FVector4& OcclusionTintAndMinOcclusionValue) { FTextureRHIParamRef BentNormalAO = GWhiteTexture->TextureRHI; bool bApplyBentNormalAO = false; if (DynamicBentNormalAO) { BentNormalAO = DynamicBentNormalAO->GetRenderTargetItem().ShaderResourceTexture; bApplyBentNormalAO = true; } SetTextureParameter(RHICmdList, ShaderRHI, BentNormalAOTexture, BentNormalAOSampler, TStaticSamplerState::GetRHI(), BentNormalAO); SetShaderValue(RHICmdList, ShaderRHI, ApplyBentNormalAO, bApplyBentNormalAO ? 1.0f : 0.0f); SetShaderValue(RHICmdList, ShaderRHI, InvSkySpecularOcclusionStrength, 1.0f / FMath::Max(SkySpecularOcclusionStrength, .1f)); SetShaderValue(RHICmdList, ShaderRHI, OcclusionTintAndMinOcclusion, OcclusionTintAndMinOcclusionValue); } friend FArchive& operator<<(FArchive& Ar,FDistanceFieldAOSpecularOcclusionParameters& P) { Ar << P.BentNormalAOTexture << P.BentNormalAOSampler << P.ApplyBentNormalAO << P.InvSkySpecularOcclusionStrength << P.OcclusionTintAndMinOcclusion; return Ar; } private: FShaderResourceParameter BentNormalAOTexture; FShaderResourceParameter BentNormalAOSampler; FShaderParameter ApplyBentNormalAO; FShaderParameter InvSkySpecularOcclusionStrength; FShaderParameter OcclusionTintAndMinOcclusion; }; struct FReflectionCaptureSortData { uint32 Guid; int32 CaptureIndex; FVector4 PositionAndRadius; FVector4 CaptureProperties; FMatrix BoxTransform; FVector4 BoxScales; FVector4 CaptureOffset; FTexture* SM4FullHDRCubemap; bool operator < (const FReflectionCaptureSortData& Other) const { if( PositionAndRadius.W != Other.PositionAndRadius.W ) { return PositionAndRadius.W < Other.PositionAndRadius.W; } else { return Guid < Other.Guid; } } }; /** Per-reflection capture data needed by the shader. */ BEGIN_UNIFORM_BUFFER_STRUCT(FReflectionCaptureData,) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FVector4,PositionAndRadius,[GMaxNumReflectionCaptures]) // R is brightness, G is array index, B is shape DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FVector4,CaptureProperties,[GMaxNumReflectionCaptures]) // Stores the box transform for a box shape, other data is packed for other shapes DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FMatrix,BoxTransform,[GMaxNumReflectionCaptures]) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FVector4,CaptureOffset,[GMaxNumReflectionCaptures]) DECLARE_UNIFORM_BUFFER_STRUCT_MEMBER_ARRAY(FVector4,BoxScales,[GMaxNumReflectionCaptures]) END_UNIFORM_BUFFER_STRUCT(FReflectionCaptureData) IMPLEMENT_UNIFORM_BUFFER_STRUCT(FReflectionCaptureData,TEXT("ReflectionCapture")); /** Compute shader that does tiled deferred culling of reflection captures, then sorts and composites them. */ class FReflectionEnvironmentTiledDeferredCS : public FGlobalShader { DECLARE_SHADER_TYPE(FReflectionEnvironmentTiledDeferredCS,Global) public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GReflectionEnvironmentTileSizeX); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GReflectionEnvironmentTileSizeY); OutEnvironment.SetDefine(TEXT("MAX_CAPTURES"), GMaxNumReflectionCaptures); OutEnvironment.SetDefine(TEXT("TILED_DEFERRED_CULL_SHADER"), 1); OutEnvironment.CompilerFlags.Add(CFLAG_StandardOptimization); } FReflectionEnvironmentTiledDeferredCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { DeferredParameters.Bind(Initializer.ParameterMap); ReflectionEnvironmentColorTexture.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvironmentColorTexture")); ReflectionEnvironmentColorSampler.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvironmentColorSampler")); ScreenSpaceReflections.Bind(Initializer.ParameterMap, TEXT("ScreenSpaceReflections")); InSceneColor.Bind(Initializer.ParameterMap, TEXT("InSceneColor")); OutSceneColor.Bind(Initializer.ParameterMap, TEXT("OutSceneColor")); NumCaptures.Bind(Initializer.ParameterMap, TEXT("NumCaptures")); ViewDimensionsParameter.Bind(Initializer.ParameterMap, TEXT("ViewDimensions")); PreIntegratedGF.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGF")); PreIntegratedGFSampler.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGFSampler")); SkyLightParameters.Bind(Initializer.ParameterMap); SpecularOcclusionParameters.Bind(Initializer.ParameterMap); } FReflectionEnvironmentTiledDeferredCS() { } void SetParameters( FRHIAsyncComputeCommandListImmediate& RHICmdList, const FSceneView& View, FTextureRHIParamRef SSRTexture, TArray& SortData, FUnorderedAccessViewRHIParamRef OutSceneColorUAV, const TRefCountPtr& DynamicBentNormalAO ) { const FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); DeferredParameters.Set(RHICmdList, ShaderRHI, View); FScene* Scene = (FScene*)View.Family->Scene; check(Scene->ReflectionSceneData.CubemapArray.IsValid()); check(Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().IsValid()); FSceneRenderTargetItem& CubemapArray = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget(); SetTextureParameter( RHICmdList, ShaderRHI, ReflectionEnvironmentColorTexture, ReflectionEnvironmentColorSampler, TStaticSamplerState::GetRHI(), CubemapArray.ShaderResourceTexture); SetTextureParameter(RHICmdList, ShaderRHI, ScreenSpaceReflections, SSRTexture ); SetTextureParameter(RHICmdList, ShaderRHI, InSceneColor, FSceneRenderTargets::Get(RHICmdList).GetSceneColor()->GetRenderTargetItem().ShaderResourceTexture ); RHICmdList.TransitionResource(EResourceTransitionAccess::ERWBarrier, EResourceTransitionPipeline::EGfxToCompute, OutSceneColorUAV); OutSceneColor.SetTexture(RHICmdList, ShaderRHI, NULL, OutSceneColorUAV); SetShaderValue(RHICmdList, ShaderRHI, ViewDimensionsParameter, View.ViewRect); FReflectionCaptureData SamplePositionsBuffer; for (int32 CaptureIndex = 0; CaptureIndex < SortData.Num(); CaptureIndex++) { SamplePositionsBuffer.PositionAndRadius[CaptureIndex] = SortData[CaptureIndex].PositionAndRadius; SamplePositionsBuffer.CaptureProperties[CaptureIndex] = SortData[CaptureIndex].CaptureProperties; SamplePositionsBuffer.BoxTransform[CaptureIndex] = SortData[CaptureIndex].BoxTransform; SamplePositionsBuffer.CaptureOffset[CaptureIndex] = SortData[CaptureIndex].CaptureOffset; SamplePositionsBuffer.BoxScales[CaptureIndex] = SortData[CaptureIndex].BoxScales; } SetUniformBufferParameterImmediate(RHICmdList, ShaderRHI, GetUniformBufferParameter(), SamplePositionsBuffer); SetShaderValue(RHICmdList, ShaderRHI, NumCaptures, SortData.Num()); SetTextureParameter(RHICmdList, ShaderRHI, PreIntegratedGF, PreIntegratedGFSampler, TStaticSamplerState::GetRHI(), GSystemTextures.PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture); SkyLightParameters.SetParameters(RHICmdList, ShaderRHI, Scene, View.Family->EngineShowFlags.SkyLighting); const float MinOcclusion = Scene->SkyLight ? Scene->SkyLight->MinOcclusion : 0; const FVector OcclusionTint = Scene->SkyLight ? (const FVector&)Scene->SkyLight->OcclusionTint : FVector::ZeroVector; SpecularOcclusionParameters.SetParameters(RHICmdList, ShaderRHI, DynamicBentNormalAO, CVarSkySpecularOcclusionStrength.GetValueOnRenderThread(), FVector4(OcclusionTint, MinOcclusion)); } void UnsetParameters(FRHIAsyncComputeCommandListImmediate& RHICmdList, FUnorderedAccessViewRHIParamRef OutSceneColorUAV) { const FComputeShaderRHIParamRef ShaderRHI = GetComputeShader(); OutSceneColor.UnsetUAV(RHICmdList, ShaderRHI); } virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << DeferredParameters; Ar << ReflectionEnvironmentColorTexture; Ar << ReflectionEnvironmentColorSampler; Ar << ScreenSpaceReflections; Ar << InSceneColor; Ar << OutSceneColor; Ar << NumCaptures; Ar << ViewDimensionsParameter; Ar << PreIntegratedGF; Ar << PreIntegratedGFSampler; Ar << SkyLightParameters; Ar << SpecularOcclusionParameters; return bShaderHasOutdatedParameters; } private: FDeferredPixelShaderParameters DeferredParameters; FShaderResourceParameter ReflectionEnvironmentColorTexture; FShaderResourceParameter ReflectionEnvironmentColorSampler; FShaderResourceParameter ScreenSpaceReflections; FShaderResourceParameter InSceneColor; FRWShaderParameter OutSceneColor; FShaderParameter NumCaptures; FShaderParameter ViewDimensionsParameter; FShaderResourceParameter PreIntegratedGF; FShaderResourceParameter PreIntegratedGFSampler; FSkyLightReflectionParameters SkyLightParameters; FDistanceFieldAOSpecularOcclusionParameters SpecularOcclusionParameters; }; template< uint32 bUseLightmaps, uint32 bHasSkyLight, uint32 bBoxCapturesOnly, uint32 bSphereCapturesOnly, uint32 bSupportDFAOIndirectOcclusion > class TReflectionEnvironmentTiledDeferredCS : public FReflectionEnvironmentTiledDeferredCS { DECLARE_SHADER_TYPE(TReflectionEnvironmentTiledDeferredCS, Global); /** Default constructor. */ TReflectionEnvironmentTiledDeferredCS() {} public: TReflectionEnvironmentTiledDeferredCS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FReflectionEnvironmentTiledDeferredCS(Initializer) {} static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FReflectionEnvironmentTiledDeferredCS::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("USE_LIGHTMAPS"), bUseLightmaps); OutEnvironment.SetDefine(TEXT("HAS_SKYLIGHT"), bHasSkyLight); OutEnvironment.SetDefine(TEXT("HAS_BOX_CAPTURES"), bBoxCapturesOnly); OutEnvironment.SetDefine(TEXT("HAS_SPHERE_CAPTURES"), bSphereCapturesOnly); OutEnvironment.SetDefine(TEXT("SUPPORT_DFAO_INDIRECT_OCCLUSION"), bSupportDFAOIndirectOcclusion); } }; // Typedef is necessary because the C preprocessor thinks the comma in the template parameter list is a comma in the macro parameter list. #define IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(A, B, C, D, E) \ typedef TReflectionEnvironmentTiledDeferredCS TReflectionEnvironmentTiledDeferredCS##A##B##C##D##E; \ IMPLEMENT_SHADER_TYPE(template<>,TReflectionEnvironmentTiledDeferredCS##A##B##C##D##E,TEXT("ReflectionEnvironmentComputeShaders"),TEXT("ReflectionEnvironmentTiledDeferredMain"),SF_Compute) IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 0, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 0, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 1, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 1, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 0, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 0, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 1, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 1, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 0, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 0, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 1, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 1, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 0, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 0, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 1, 0, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 1, 1, 0); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 0, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 0, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 1, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 0, 1, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 0, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 0, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 1, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(0, 1, 1, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 0, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 0, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 1, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 0, 1, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 0, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 0, 1, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 1, 0, 1); IMPLEMENT_REFLECTION_COMPUTESHADER_TYPE(1, 1, 1, 1, 1); template< uint32 bSSR, uint32 bReflectionEnv, uint32 bSkylight, uint32 bSupportDFAOIndirectOcclusion > class FReflectionApplyPS : public FGlobalShader { DECLARE_SHADER_TYPE(FReflectionApplyPS, Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("APPLY_SSR"), bSSR); OutEnvironment.SetDefine(TEXT("APPLY_REFLECTION_ENV"), bReflectionEnv); OutEnvironment.SetDefine(TEXT("APPLY_SKYLIGHT"), bSkylight); OutEnvironment.SetDefine(TEXT("SUPPORT_DFAO_INDIRECT_OCCLUSION"), bSupportDFAOIndirectOcclusion); } /** Default constructor. */ FReflectionApplyPS() {} /** Initialization constructor. */ FReflectionApplyPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { DeferredParameters.Bind(Initializer.ParameterMap); SkyLightParameters.Bind(Initializer.ParameterMap); ReflectionEnvTexture.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvTexture")); ReflectionEnvSampler.Bind(Initializer.ParameterMap,TEXT("ReflectionEnvSampler")); ScreenSpaceReflectionsTexture.Bind(Initializer.ParameterMap,TEXT("ScreenSpaceReflectionsTexture")); ScreenSpaceReflectionsSampler.Bind(Initializer.ParameterMap,TEXT("ScreenSpaceReflectionsSampler")); PreIntegratedGF.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGF")); PreIntegratedGFSampler.Bind(Initializer.ParameterMap, TEXT("PreIntegratedGFSampler")); SpecularOcclusionParameters.Bind(Initializer.ParameterMap); } void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, FTextureRHIParamRef ReflectionEnv, FTextureRHIParamRef ScreenSpaceReflections, const TRefCountPtr& DynamicBentNormalAO) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); DeferredParameters.Set(RHICmdList, ShaderRHI, View); SkyLightParameters.SetParameters(RHICmdList, ShaderRHI, (FScene*)View.Family->Scene, true); SetTextureParameter(RHICmdList, ShaderRHI, ReflectionEnvTexture, ReflectionEnvSampler, TStaticSamplerState::GetRHI(), ReflectionEnv ); SetTextureParameter(RHICmdList, ShaderRHI, ScreenSpaceReflectionsTexture, ScreenSpaceReflectionsSampler, TStaticSamplerState::GetRHI(), ScreenSpaceReflections ); SetTextureParameter(RHICmdList, ShaderRHI, PreIntegratedGF, PreIntegratedGFSampler, TStaticSamplerState::GetRHI(), GSystemTextures.PreintegratedGF->GetRenderTargetItem().ShaderResourceTexture ); FScene* Scene = (FScene*)View.Family->Scene; const float MinOcclusion = Scene->SkyLight ? Scene->SkyLight->MinOcclusion : 0; const FVector OcclusionTint = Scene->SkyLight ? (const FVector&)Scene->SkyLight->OcclusionTint : FVector::ZeroVector; SpecularOcclusionParameters.SetParameters(RHICmdList, ShaderRHI, DynamicBentNormalAO, CVarSkySpecularOcclusionStrength.GetValueOnRenderThread(), FVector4(OcclusionTint, MinOcclusion)); } // FShader interface. virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << DeferredParameters; Ar << SkyLightParameters; Ar << ReflectionEnvTexture; Ar << ReflectionEnvSampler; Ar << ScreenSpaceReflectionsTexture; Ar << ScreenSpaceReflectionsSampler; Ar << PreIntegratedGF; Ar << PreIntegratedGFSampler; Ar << SpecularOcclusionParameters; return bShaderHasOutdatedParameters; } private: FDeferredPixelShaderParameters DeferredParameters; FSkyLightReflectionParameters SkyLightParameters; FShaderResourceParameter ReflectionEnvTexture; FShaderResourceParameter ReflectionEnvSampler; FShaderResourceParameter ScreenSpaceReflectionsTexture; FShaderResourceParameter ScreenSpaceReflectionsSampler; FShaderResourceParameter PreIntegratedGF; FShaderResourceParameter PreIntegratedGFSampler; FDistanceFieldAOSpecularOcclusionParameters SpecularOcclusionParameters; }; // Typedef is necessary because the C preprocessor thinks the comma in the template parameter list is a comma in the macro parameter list. #define IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(A, B, C, D) \ typedef FReflectionApplyPS FReflectionApplyPS##A##B##C##D; \ IMPLEMENT_SHADER_TYPE(template<>,FReflectionApplyPS##A##B##C##D,TEXT("ReflectionEnvironmentShaders"),TEXT("ReflectionApplyPS"),SF_Pixel); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,0,0,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,0,1,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,1,0,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,1,1,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,0,0,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,0,1,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,1,0,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,1,1,0); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,0,0,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,0,1,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,1,0,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(0,1,1,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,0,0,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,0,1,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,1,0,1); IMPLEMENT_REFLECTION_APPLY_PIXELSHADER_TYPE(1,1,1,1); class FReflectionCaptureSpecularBouncePS : public FGlobalShader { DECLARE_SHADER_TYPE(FReflectionCaptureSpecularBouncePS, Global); static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform,OutEnvironment); } /** Default constructor. */ FReflectionCaptureSpecularBouncePS() {} public: FDeferredPixelShaderParameters DeferredParameters; /** Initialization constructor. */ FReflectionCaptureSpecularBouncePS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { DeferredParameters.Bind(Initializer.ParameterMap); } void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); DeferredParameters.Set(RHICmdList, ShaderRHI, View); } // FShader interface. virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << DeferredParameters; return bShaderHasOutdatedParameters; } }; IMPLEMENT_SHADER_TYPE(,FReflectionCaptureSpecularBouncePS,TEXT("ReflectionEnvironmentShaders"),TEXT("SpecularBouncePS"),SF_Pixel); template class TStandardDeferredReflectionPS : public FGlobalShader { DECLARE_SHADER_TYPE(TStandardDeferredReflectionPS, Global); public: static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4); } static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment) { FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment); OutEnvironment.SetDefine(TEXT("SPHERE_CAPTURE"), (uint32)bSphereCapture); OutEnvironment.SetDefine(TEXT("BOX_CAPTURE"), (uint32)!bSphereCapture); } /** Default constructor. */ TStandardDeferredReflectionPS() {} /** Initialization constructor. */ TStandardDeferredReflectionPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer) : FGlobalShader(Initializer) { CapturePositionAndRadius.Bind(Initializer.ParameterMap, TEXT("CapturePositionAndRadius")); CaptureProperties.Bind(Initializer.ParameterMap, TEXT("CaptureProperties")); CaptureBoxTransform.Bind(Initializer.ParameterMap, TEXT("CaptureBoxTransform")); CaptureBoxScales.Bind(Initializer.ParameterMap, TEXT("CaptureBoxScales")); CaptureOffset.Bind(Initializer.ParameterMap, TEXT("CaptureOffset")); CaptureArrayIndex.Bind(Initializer.ParameterMap, TEXT("CaptureArrayIndex")); ReflectionEnvironmentColorTexture.Bind(Initializer.ParameterMap, TEXT("ReflectionEnvironmentColorTexture")); ReflectionEnvironmentColorTextureArray.Bind(Initializer.ParameterMap, TEXT("ReflectionEnvironmentColorTextureArray")); ReflectionEnvironmentColorSampler.Bind(Initializer.ParameterMap, TEXT("ReflectionEnvironmentColorSampler")); DeferredParameters.Bind(Initializer.ParameterMap); } void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, const FReflectionCaptureSortData& SortData) { const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader(); FGlobalShader::SetParameters(RHICmdList, ShaderRHI, View); if (View.GetFeatureLevel() >= ERHIFeatureLevel::SM5) { FScene* Scene = (FScene*)View.Family->Scene; check(Scene->ReflectionSceneData.CubemapArray.IsValid()); check(Scene->ReflectionSceneData.CubemapArray.GetRenderTarget().IsValid()); FSceneRenderTargetItem& CubemapArray = Scene->ReflectionSceneData.CubemapArray.GetRenderTarget(); SetTextureParameter(RHICmdList, ShaderRHI, ReflectionEnvironmentColorTextureArray, ReflectionEnvironmentColorSampler, TStaticSamplerState::GetRHI(), CubemapArray.ShaderResourceTexture); SetShaderValue(RHICmdList, ShaderRHI, CaptureArrayIndex, SortData.CaptureIndex); } else { SetTextureParameter(RHICmdList, ShaderRHI, ReflectionEnvironmentColorTexture, ReflectionEnvironmentColorSampler, TStaticSamplerState::GetRHI(), SortData.SM4FullHDRCubemap->TextureRHI); } DeferredParameters.Set(RHICmdList, ShaderRHI, View); SetShaderValue(RHICmdList, ShaderRHI, CapturePositionAndRadius, SortData.PositionAndRadius); SetShaderValue(RHICmdList, ShaderRHI, CaptureProperties, SortData.CaptureProperties); SetShaderValue(RHICmdList, ShaderRHI, CaptureBoxTransform, SortData.BoxTransform); SetShaderValue(RHICmdList, ShaderRHI, CaptureBoxScales, SortData.BoxScales); SetShaderValue(RHICmdList, ShaderRHI, CaptureOffset, FVector(SortData.CaptureOffset)); } // FShader interface. virtual bool Serialize(FArchive& Ar) override { bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar); Ar << CapturePositionAndRadius; Ar << CaptureProperties; Ar << CaptureBoxTransform; Ar << CaptureBoxScales; Ar << CaptureOffset; Ar << CaptureArrayIndex; Ar << ReflectionEnvironmentColorTexture; Ar << ReflectionEnvironmentColorTextureArray; Ar << ReflectionEnvironmentColorSampler; Ar << DeferredParameters; return bShaderHasOutdatedParameters; } private: FShaderParameter CapturePositionAndRadius; FShaderParameter CaptureProperties; FShaderParameter CaptureBoxTransform; FShaderParameter CaptureBoxScales; FShaderParameter CaptureOffset; FShaderParameter CaptureArrayIndex; FShaderResourceParameter ReflectionEnvironmentColorTexture; FShaderResourceParameter ReflectionEnvironmentColorTextureArray; FShaderResourceParameter ReflectionEnvironmentColorSampler; FDeferredPixelShaderParameters DeferredParameters; }; IMPLEMENT_SHADER_TYPE(template<>,TStandardDeferredReflectionPS,TEXT("ReflectionEnvironmentShaders"),TEXT("StandardDeferredReflectionPS"),SF_Pixel); IMPLEMENT_SHADER_TYPE(template<>,TStandardDeferredReflectionPS,TEXT("ReflectionEnvironmentShaders"),TEXT("StandardDeferredReflectionPS"),SF_Pixel); void FDeferredShadingSceneRenderer::RenderReflectionCaptureSpecularBounceForAllViews(FRHICommandListImmediate& RHICmdList) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); SceneContext.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EUninitializedColorExistingDepth, FExclusiveDepthStencil::DepthRead_StencilWrite); RHICmdList.SetRasterizerState(TStaticRasterizerState< FM_Solid, CM_None >::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState< false, CF_Always >::GetRHI()); RHICmdList.SetBlendState(TStaticBlendState< CW_RGB, BO_Add, BF_One, BF_One >::GetRHI()); auto ShaderMap = GetGlobalShaderMap(FeatureLevel); TShaderMapRef< FPostProcessVS > VertexShader(ShaderMap); TShaderMapRef< FReflectionCaptureSpecularBouncePS > PixelShader(ShaderMap); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; 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); 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); } SceneContext.FinishRenderingSceneColor(RHICmdList); } bool FDeferredShadingSceneRenderer::ShouldDoReflectionEnvironment() const { const ERHIFeatureLevel::Type SceneFeatureLevel = Scene->GetFeatureLevel(); return IsReflectionEnvironmentAvailable(SceneFeatureLevel) && Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() && ViewFamily.EngineShowFlags.ReflectionEnvironment && (SceneFeatureLevel == ERHIFeatureLevel::SM4 || Scene->ReflectionSceneData.CubemapArray.IsValid()); } void GatherAndSortReflectionCaptures(const FScene* Scene, TArray& OutSortData, int32& OutNumBoxCaptures, int32& OutNumSphereCaptures) { OutSortData.Reset(Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num()); OutNumBoxCaptures = 0; OutNumSphereCaptures = 0; const int32 MaxCubemaps = Scene->ReflectionSceneData.CubemapArray.GetMaxCubemaps(); // Pack only visible reflection captures into the uniform buffer, each with an index to its cubemap array entry for (int32 ReflectionProxyIndex = 0; ReflectionProxyIndex < Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() && OutSortData.Num() < GMaxNumReflectionCaptures; ReflectionProxyIndex++) { FReflectionCaptureProxy* CurrentCapture = Scene->ReflectionSceneData.RegisteredReflectionCaptures[ReflectionProxyIndex]; // Find the cubemap index this component was allocated with const FCaptureComponentSceneState* ComponentStatePtr = Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Find(CurrentCapture->Component); if (ComponentStatePtr) { int32 CubemapIndex = ComponentStatePtr->CaptureIndex; check(CubemapIndex < MaxCubemaps); FReflectionCaptureSortData NewSortEntry; NewSortEntry.CaptureIndex = CubemapIndex; NewSortEntry.SM4FullHDRCubemap = NULL; NewSortEntry.Guid = CurrentCapture->Guid; NewSortEntry.PositionAndRadius = FVector4(CurrentCapture->Position, CurrentCapture->InfluenceRadius); float ShapeTypeValue = (float)CurrentCapture->Shape; NewSortEntry.CaptureProperties = FVector4(CurrentCapture->Brightness, CubemapIndex, ShapeTypeValue, 0); NewSortEntry.CaptureOffset = FVector4(CurrentCapture->CaptureOffset, 0); if (CurrentCapture->Shape == EReflectionCaptureShape::Plane) { //planes count as boxes in the compute shader. ++OutNumBoxCaptures; NewSortEntry.BoxTransform = FMatrix( FPlane(CurrentCapture->ReflectionPlane), FPlane(CurrentCapture->ReflectionXAxisAndYScale), FPlane(0, 0, 0, 0), FPlane(0, 0, 0, 0)); NewSortEntry.BoxScales = FVector4(0); } else if (CurrentCapture->Shape == EReflectionCaptureShape::Sphere) { ++OutNumSphereCaptures; } else { ++OutNumBoxCaptures; NewSortEntry.BoxTransform = CurrentCapture->BoxTransform; NewSortEntry.BoxScales = FVector4(CurrentCapture->BoxScales, CurrentCapture->BoxTransitionDistance); } OutSortData.Add(NewSortEntry); } } OutSortData.Sort(); } template FReflectionEnvironmentTiledDeferredCS* SelectReflectionEnvironmentTiledDeferredCSInner(TShaderMap* ShaderMap, bool bUseLightmaps, bool bHasSkyLight, bool bHasBoxCaptures, bool bHasSphereCaptures) { FReflectionEnvironmentTiledDeferredCS* ComputeShader = nullptr; if (bUseLightmaps) { if (bHasSkyLight) { if (bHasBoxCaptures && bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 1, 1, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasBoxCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 1, 1, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 1, 0, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 1, 0, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } } else { if (bHasBoxCaptures && bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 0, 1, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasBoxCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 0, 1, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 0, 0, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<1, 0, 0, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } } } else { if (bHasSkyLight) { if (bHasBoxCaptures && bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 1, 1, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasBoxCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 1, 1, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 1, 0, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 1, 0, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } } else { if (bHasBoxCaptures && bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 0, 1, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasBoxCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 0, 1, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else if (bHasSphereCaptures) { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 0, 0, 1, bSuportDFAOIndirectOcclusion> >(ShaderMap); } else { ComputeShader = *TShaderMapRef< TReflectionEnvironmentTiledDeferredCS<0, 0, 0, 0, bSuportDFAOIndirectOcclusion> >(ShaderMap); } } } check(ComputeShader); return ComputeShader; } FReflectionEnvironmentTiledDeferredCS* SelectReflectionEnvironmentTiledDeferredCS(TShaderMap* ShaderMap, bool bUseLightmaps, bool bHasSkyLight, bool bHasBoxCaptures, bool bHasSphereCaptures, bool bSuportDFAOIndirectOcclusion) { if (bSuportDFAOIndirectOcclusion) { return SelectReflectionEnvironmentTiledDeferredCSInner(ShaderMap, bUseLightmaps, bHasSkyLight, bHasBoxCaptures, bHasSphereCaptures); } else { return SelectReflectionEnvironmentTiledDeferredCSInner(ShaderMap, bUseLightmaps, bHasSkyLight, bHasBoxCaptures, bHasSphereCaptures); } } void FDeferredShadingSceneRenderer::RenderTiledDeferredImageBasedReflections(FRHICommandListImmediate& RHICmdList, const TRefCountPtr& DynamicBentNormalAO) { FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); static const auto AllowStaticLightingVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.AllowStaticLighting")); const bool bUseLightmaps = (AllowStaticLightingVar->GetValueOnRenderThread() == 1) && (CVarDiffuseFromCaptures.GetValueOnRenderThread() == 0); TRefCountPtr NewSceneColor; { SceneContext.ResolveSceneColor(RHICmdList, FResolveRect(0, 0, ViewFamily.FamilySizeX, ViewFamily.FamilySizeY)); FPooledRenderTargetDesc Desc = SceneContext.GetSceneColor()->GetDesc(); Desc.TargetableFlags |= TexCreate_UAV; Desc.TargetableFlags |= TexCreate_NoFastClear; Desc.ClearValue = FClearValueBinding::None; // we don't create a new name to make it easier to use "vis SceneColor" and get the last HDRSceneColor GRenderTargetPool.FindFreeElement(RHICmdList, Desc, NewSceneColor, TEXT("SceneColor") ); } // If we are in SM5, use the compute shader gather method for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++) { FViewInfo& View = Views[ViewIndex]; const uint32 bSSR = ShouldRenderScreenSpaceReflections(Views[ViewIndex]); TRefCountPtr SSROutput = GSystemTextures.BlackDummy; if( bSSR ) { RenderScreenSpaceReflections(RHICmdList, View, SSROutput); } SCOPED_GPU_STAT(RHICmdList, Stat_GPU_ReflectionEnvironment) RenderDeferredPlanarReflections(RHICmdList, false, SSROutput); // ReflectionEnv is assumed to be on when going into this method { SetRenderTarget(RHICmdList, NULL, NULL); FReflectionEnvironmentTiledDeferredCS* ComputeShader = NULL; // Render the reflection environment with tiled deferred culling TArray SortData; int32 NumBoxCaptures = 0; int32 NumSphereCaptures = 0; GatherAndSortReflectionCaptures(Scene, SortData, NumBoxCaptures, NumSphereCaptures); bool bHasBoxCaptures = (NumBoxCaptures > 0); bool bHasSphereCaptures = (NumSphereCaptures > 0); bool bHasSkyLight = Scene && Scene->SkyLight && !Scene->SkyLight->bHasStaticLighting; static const FName TiledReflBeginComputeName(TEXT("ReflectionEnvBeginComputeFence")); static const FName TiledReflEndComputeName(TEXT("ReflectionEnvEndComputeFence")); FComputeFenceRHIRef ReflectionBeginFence = RHICmdList.CreateComputeFence(TiledReflBeginComputeName); FComputeFenceRHIRef ReflectionEndFence = RHICmdList.CreateComputeFence(TiledReflEndComputeName); //Grab the async compute commandlist. FRHIAsyncComputeCommandListImmediate& RHICmdListComputeImmediate = FRHICommandListExecutor::GetImmediateAsyncComputeCommandList(); { SCOPED_COMPUTE_EVENTF(RHICmdListComputeImmediate, ReflectionEnvironment, TEXT("ReflectionEnvironment ComputeShader %dx%d Tile:%dx%d Box:%d Sphere:%d SkyLight:%d"), View.ViewRect.Width(), View.ViewRect.Height(), GReflectionEnvironmentTileSizeX, GReflectionEnvironmentTileSizeY, NumBoxCaptures, NumSphereCaptures, bHasSkyLight); ComputeShader = SelectReflectionEnvironmentTiledDeferredCS(View.ShaderMap, bUseLightmaps, bHasSkyLight, bHasBoxCaptures, bHasSphereCaptures, DynamicBentNormalAO != NULL); //Really we should write this fence where we transition the final depedency for the reflections. We may add an RHI command just for writing fences if this //can't be done in the general case. In the meantime, hack this command a bit to write the fence. RHICmdList.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EGfxToCompute, nullptr, 0, ReflectionBeginFence); //we must wait on the fence written from the Gfx pipe to let us know all our dependencies are ready. RHICmdListComputeImmediate.WaitComputeFence(ReflectionBeginFence); //standard compute setup, but on the async commandlist. RHICmdListComputeImmediate.SetComputeShader(ComputeShader->GetComputeShader()); FUnorderedAccessViewRHIParamRef OutUAV = NewSceneColor->GetRenderTargetItem().UAV; ComputeShader->SetParameters(RHICmdListComputeImmediate, View, SSROutput->GetRenderTargetItem().ShaderResourceTexture, SortData, OutUAV, DynamicBentNormalAO); uint32 GroupSizeX = (View.ViewRect.Size().X + GReflectionEnvironmentTileSizeX - 1) / GReflectionEnvironmentTileSizeX; uint32 GroupSizeY = (View.ViewRect.Size().Y + GReflectionEnvironmentTileSizeY - 1) / GReflectionEnvironmentTileSizeY; DispatchComputeShader(RHICmdListComputeImmediate, ComputeShader, GroupSizeX, GroupSizeY, 1); ComputeShader->UnsetParameters(RHICmdListComputeImmediate, OutUAV); //transition the output to readable and write the fence to allow the Gfx pipe to carry on. RHICmdListComputeImmediate.TransitionResources(EResourceTransitionAccess::EReadable, EResourceTransitionPipeline::EComputeToGfx, &OutUAV, 1, ReflectionEndFence); } //immediately dispatch our async compute commands to the RHI thread to be submitted to the GPU as soon as possible. //dispatch after the scope so the drawevent pop is inside the dispatch FRHIAsyncComputeCommandListImmediate::ImmediateDispatch(RHICmdListComputeImmediate); //Gfx pipe must wait for the async compute reflection job to complete. RHICmdList.WaitComputeFence(ReflectionEndFence); } } SceneContext.SetSceneColor(NewSceneColor); check(SceneContext.GetSceneColor()); } void FDeferredShadingSceneRenderer::RenderStandardDeferredImageBasedReflections(FRHICommandListImmediate& RHICmdList, bool bReflectionEnv, const TRefCountPtr& DynamicBentNormalAO) { if(!ViewFamily.EngineShowFlags.Lighting) { return; } const bool bSkyLight = Scene->SkyLight && Scene->SkyLight->ProcessedTexture && !Scene->SkyLight->bHasStaticLighting && ViewFamily.EngineShowFlags.SkyLighting; static TArray SortData; if (bReflectionEnv) { // shared for multiple views SortData.Reset(Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num()); // Gather visible reflection capture data for (int32 ReflectionProxyIndex = 0; ReflectionProxyIndex < Scene->ReflectionSceneData.RegisteredReflectionCaptures.Num() && SortData.Num() < GMaxNumReflectionCaptures; ReflectionProxyIndex++) { FReflectionCaptureProxy* CurrentCapture = Scene->ReflectionSceneData.RegisteredReflectionCaptures[ReflectionProxyIndex]; FReflectionCaptureSortData NewSortEntry; NewSortEntry.CaptureIndex = -1; if (FeatureLevel >= ERHIFeatureLevel::SM5) { const FCaptureComponentSceneState* ComponentStatePtr = Scene->ReflectionSceneData.AllocatedReflectionCaptureState.Find(CurrentCapture->Component); NewSortEntry.CaptureIndex = ComponentStatePtr ? ComponentStatePtr->CaptureIndex : -1; } NewSortEntry.SM4FullHDRCubemap = CurrentCapture->SM4FullHDRCubemap; NewSortEntry.Guid = CurrentCapture->Guid; NewSortEntry.PositionAndRadius = FVector4(CurrentCapture->Position, CurrentCapture->InfluenceRadius); float ShapeTypeValue = (float)CurrentCapture->Shape; NewSortEntry.CaptureProperties = FVector4(CurrentCapture->Brightness, 0, ShapeTypeValue, 0); NewSortEntry.CaptureOffset = FVector4(CurrentCapture->CaptureOffset); if (CurrentCapture->Shape == EReflectionCaptureShape::Plane) { NewSortEntry.BoxTransform = FMatrix( FPlane(CurrentCapture->ReflectionPlane), FPlane(CurrentCapture->ReflectionXAxisAndYScale), FPlane(0, 0, 0, 0), FPlane(0, 0, 0, 0)); NewSortEntry.BoxScales = FVector4(0); } else { NewSortEntry.BoxTransform = CurrentCapture->BoxTransform; NewSortEntry.BoxScales = FVector4(CurrentCapture->BoxScales, CurrentCapture->BoxTransitionDistance); } SortData.Add(NewSortEntry); } SortData.Sort(); } FSceneRenderTargets& SceneContext = FSceneRenderTargets::Get(RHICmdList); // Use standard deferred shading to composite reflection capture contribution for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++) { FViewInfo& View = Views[ViewIndex]; FSceneViewState* ViewState = (FSceneViewState*)View.State; bool bLPV = false; if(ViewState && ViewState->GetLightPropagationVolume(View.GetFeatureLevel())) { const FLightPropagationVolumeSettings& LPVSettings = View.FinalPostProcessSettings.BlendableManager.GetSingleFinalDataConst(); bLPV = LPVSettings.LPVIntensity > 0.0f; } bool bAmbient = View.FinalPostProcessSettings.ContributingCubemaps.Num() > 0; bool bMixing = bLPV && (CVarLPVMixing.GetValueOnRenderThread() != 0); bool bEnvironmentMixing = bMixing && (bAmbient || bLPV); bool bRequiresApply = bSkyLight // If Reflection Environment is active and mixed with indirect lighting (Ambient + LPV), apply is required! || (View.Family->EngineShowFlags.ReflectionEnvironment && (bReflectionEnv || bEnvironmentMixing) ); const bool bSSR = ShouldRenderScreenSpaceReflections(View); TRefCountPtr SSROutput = GSystemTextures.BlackDummy; if (bSSR) { bRequiresApply = true; RenderScreenSpaceReflections(RHICmdList, View, SSROutput); } SCOPED_GPU_STAT(RHICmdList, Stat_GPU_ReflectionEnvironment) bool bApplyFromSSRTexture = bSSR; if (RenderDeferredPlanarReflections(RHICmdList, true, SSROutput)) { bRequiresApply = true; bApplyFromSSRTexture = true; } /* Light Accumulation moved to SceneRenderTargets */ TRefCountPtr LightAccumulation = SceneContext.LightAccumulation; if (!LightAccumulation) { // should never be used but during debugging it can happen ensureMsgf(LightAccumulation, TEXT("White dummy system texture about to be corrupted.")); LightAccumulation = GSystemTextures.WhiteDummy; } if (bReflectionEnv) { bRequiresApply = true; SCOPED_DRAW_EVENTF(RHICmdList, ReflectionEnvironment, TEXT("ReflectionEnvironment PixelShader")); { // Clear to no reflection contribution, alpha of 1 indicates full background contribution ESimpleRenderTargetMode SimpleRenderTargetMode = ESimpleRenderTargetMode::EExistingColorAndDepth; // If Reflection Environment is mixed with indirect lighting (Ambient + LPV), skip clear! if (!bMixing) { SimpleRenderTargetMode = ESimpleRenderTargetMode::EClearColorExistingDepth; } SetRenderTarget(RHICmdList, LightAccumulation->GetRenderTargetItem().TargetableTexture, NULL, SimpleRenderTargetMode); } RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); // rgb accumulates reflection contribution front to back, alpha accumulates (1 - alpha0) * (1 - alpha 1)... RHICmdList.SetBlendState(TStaticBlendState::GetRHI()); for (int32 ReflectionCaptureIndex = 0; ReflectionCaptureIndex < SortData.Num(); ReflectionCaptureIndex++) { const FReflectionCaptureSortData& ReflectionCapture = SortData[ReflectionCaptureIndex]; if (FeatureLevel >= ERHIFeatureLevel::SM5 || ReflectionCapture.SM4FullHDRCubemap) { const FSphere LightBounds(ReflectionCapture.PositionAndRadius, ReflectionCapture.PositionAndRadius.W); TShaderMapRef > VertexShader(View.ShaderMap); // Use the appropriate shader for the capture shape if (ReflectionCapture.CaptureProperties.Z == 0) { TShaderMapRef > PixelShader(View.ShaderMap); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GetVertexDeclarationFVector4(), *VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, ReflectionCapture); } else { TShaderMapRef > PixelShader(View.ShaderMap); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GetVertexDeclarationFVector4(), *VertexShader, *PixelShader); PixelShader->SetParameters(RHICmdList, View, ReflectionCapture); } SetBoundingGeometryRasterizerAndDepthState(RHICmdList, View, LightBounds); VertexShader->SetSimpleLightParameters(RHICmdList, View, LightBounds); StencilingGeometry::DrawSphere(RHICmdList); } } RHICmdList.CopyToResolveTarget(LightAccumulation->GetRenderTargetItem().TargetableTexture, LightAccumulation->GetRenderTargetItem().ShaderResourceTexture, false, FResolveParams()); GRenderTargetPool.VisualizeTexture.SetCheckPoint(RHICmdList, LightAccumulation); } if (bRequiresApply) { // Apply reflections to screen SCOPED_DRAW_EVENT(RHICmdList, ReflectionApply); SCOPED_GPU_STAT(RHICmdList, Stat_GPU_ReflectionEnvironment); SceneContext.BeginRenderingSceneColor(RHICmdList, ESimpleRenderTargetMode::EUninitializedColorExistingDepth, FExclusiveDepthStencil::DepthRead_StencilWrite, true); RHICmdList.SetViewport(View.ViewRect.Min.X, View.ViewRect.Min.Y, 0.0f, View.ViewRect.Max.X, View.ViewRect.Max.Y, 1.0f); RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI()); RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI()); if (GetReflectionEnvironmentCVar() == 2) { // override scene color for debugging RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); } else { // additive to scene color RHICmdList.SetBlendState(TStaticBlendState::GetRHI()); } TShaderMapRef< FPostProcessVS > VertexShader(View.ShaderMap); // Activate Reflection Environment if we choose to mix it with indirect lighting (Ambient + LPV) // todo: refactor (we abuse another boolean to pass the data through) bReflectionEnv = bReflectionEnv || bEnvironmentMixing; const bool bSupportDFAOIndirectShadowing = DynamicBentNormalAO != NULL; #define CASE(A,B,C,D) \ case ((A << 3) | (B << 2) | (C << 1) | D) : \ { \ TShaderMapRef< FReflectionApplyPS > PixelShader(View.ShaderMap); \ static FGlobalBoundShaderState BoundShaderState; \ SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); \ PixelShader->SetParameters(RHICmdList, View, LightAccumulation->GetRenderTargetItem().ShaderResourceTexture, SSROutput->GetRenderTargetItem().ShaderResourceTexture, DynamicBentNormalAO); \ }; \ break switch (((uint32)bApplyFromSSRTexture << 3) | ((uint32)bReflectionEnv << 2) | ((uint32)bSkyLight << 1) | (uint32)bSupportDFAOIndirectShadowing) { CASE(0, 0, 0, 0); CASE(0, 0, 1, 0); CASE(0, 1, 0, 0); CASE(0, 1, 1, 0); CASE(1, 0, 0, 0); CASE(1, 0, 1, 0); CASE(1, 1, 0, 0); CASE(1, 1, 1, 0); CASE(0, 0, 0, 1); CASE(0, 0, 1, 1); CASE(0, 1, 0, 1); CASE(0, 1, 1, 1); CASE(1, 0, 0, 1); CASE(1, 0, 1, 1); CASE(1, 1, 0, 1); CASE(1, 1, 1, 1); } #undef CASE 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); } } } void FDeferredShadingSceneRenderer::RenderDeferredReflections(FRHICommandListImmediate& RHICmdList, const TRefCountPtr& DynamicBentNormalAO) { if (ViewFamily.EngineShowFlags.VisualizeLightCulling) { return; } bool bAnyViewIsReflectionCapture = false; for (int32 ViewIndex = 0, Num = Views.Num(); ViewIndex < Num; ViewIndex++) { const FViewInfo& View = Views[ViewIndex]; bAnyViewIsReflectionCapture = bAnyViewIsReflectionCapture || View.bIsReflectionCapture; } // 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 if (bAnyViewIsReflectionCapture) { RenderReflectionCaptureSpecularBounceForAllViews(RHICmdList); } else { const uint32 bDoTiledReflections = CVarDoTiledReflections.GetValueOnRenderThread() != 0; const bool bReflectionEnvironment = ShouldDoReflectionEnvironment(); const bool bReflectionsWithCompute = bDoTiledReflections && (FeatureLevel >= ERHIFeatureLevel::SM5) && bReflectionEnvironment && Scene->ReflectionSceneData.CubemapArray.IsValid(); if (bReflectionsWithCompute) { RenderTiledDeferredImageBasedReflections(RHICmdList, DynamicBentNormalAO); } else { RenderStandardDeferredImageBasedReflections(RHICmdList, bReflectionEnvironment, DynamicBentNormalAO); } } }