// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= PostProcessTonemap.cpp: Post processing tone mapping implementation. =============================================================================*/ #include "PostProcess/PostProcessTonemap.h" #include "EngineGlobals.h" #include "ScenePrivate.h" #include "RendererModule.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/SceneFilterRendering.h" #include "PostProcess/PostProcessCombineLUTs.h" #include "PostProcess/PostProcessMobile.h" #include "PostProcess/PostProcessing.h" #include "ClearQuad.h" #include "PipelineStateCache.h" namespace { TAutoConsoleVariable CVarTonemapperSharpen( TEXT("r.Tonemapper.Sharpen"), 0, TEXT("Sharpening in the tonemapper (not for mobile), actual implementation is work in progress, clamped at 10\n") TEXT(" 0: off(default)\n") TEXT(" 0.5: half strength\n") TEXT(" 1: full strength"), ECVF_Scalability | ECVF_RenderThreadSafe); // Note: Enables or disables HDR support for a project. Typically this would be set on a per-project/per-platform basis in defaultengine.ini TAutoConsoleVariable CVarAllowHDR( TEXT("r.AllowHDR"), 0, TEXT("Creates an HDR compatible swap-chain and enables HDR display output.") TEXT("0: Disabled (default)\n") TEXT("1: Allow HDR, if supported by the platform and display \n"), ECVF_ReadOnly); // Note: These values are directly referenced in code. They are set in code at runtime and therefore cannot be set via ini files // Please update all paths if changing TAutoConsoleVariable CVarDisplayColorGamut( TEXT("r.HDR.Display.ColorGamut"), 0, TEXT("Color gamut of the output display:\n") TEXT("0: Rec709 / sRGB, D65 (default)\n") TEXT("1: DCI-P3, D65\n") TEXT("2: Rec2020 / BT2020, D65\n") TEXT("3: ACES, D60\n") TEXT("4: ACEScg, D60\n"), ECVF_Scalability | ECVF_RenderThreadSafe); TAutoConsoleVariable CVarDisplayOutputDevice( TEXT("r.HDR.Display.OutputDevice"), 0, TEXT("Device format of the output display:\n") TEXT("0: sRGB (LDR)\n") TEXT("1: Rec709 (LDR)\n") TEXT("2: Explicit gamma mapping (LDR)\n") TEXT("3: ACES 1000 nit ST-2084 (Dolby PQ) (HDR)\n") TEXT("4: ACES 2000 nit ST-2084 (Dolby PQ) (HDR)\n") TEXT("5: ACES 1000 nit ScRGB (HDR)\n") TEXT("6: ACES 2000 nit ScRGB (HDR)\n") TEXT("7: Linear EXR (HDR)\n") TEXT("8: Linear final color, no tone curve (HDR)\n") TEXT("9: Linear final color with tone curve\n"), ECVF_Scalability | ECVF_RenderThreadSafe); TAutoConsoleVariable CVarHDROutputEnabled( TEXT("r.HDR.EnableHDROutput"), 0, TEXT("Creates an HDR compatible swap-chain and enables HDR display output.") TEXT("0: Disabled (default)\n") TEXT("1: Enable hardware-specific implementation\n"), ECVF_RenderThreadSafe ); TAutoConsoleVariable CVarTonemapperGamma( TEXT("r.TonemapperGamma"), 0.0f, TEXT("0: Default behavior\n") TEXT("#: Use fixed gamma # instead of sRGB or Rec709 transform"), ECVF_Scalability | ECVF_RenderThreadSafe); TAutoConsoleVariable CVarGamma( TEXT("r.Gamma"), 1.0f, TEXT("Gamma on output"), ECVF_RenderThreadSafe); const int32 GTonemapComputeTileSizeX = 8; const int32 GTonemapComputeTileSizeY = 8; namespace TonemapperPermutation { // Shared permutation dimensions between deferred and mobile renderer. class FTonemapperBloomDim : SHADER_PERMUTATION_BOOL("USE_BLOOM"); class FTonemapperGammaOnlyDim : SHADER_PERMUTATION_BOOL("USE_GAMMA_ONLY"); class FTonemapperGrainIntensityDim : SHADER_PERMUTATION_BOOL("USE_GRAIN_INTENSITY"); class FTonemapperVignetteDim : SHADER_PERMUTATION_BOOL("USE_VIGNETTE"); class FTonemapperSharpenDim : SHADER_PERMUTATION_BOOL("USE_SHARPEN"); class FTonemapperGrainJitterDim : SHADER_PERMUTATION_BOOL("USE_GRAIN_JITTER"); class FTonemapperSwitchAxis : SHADER_PERMUTATION_BOOL("NEEDTOSWITCHVERTICLEAXIS"); class FTonemapperMsaaDim : SHADER_PERMUTATION_BOOL("METAL_MSAA_HDR_DECODE"); class FTonemapperEyeAdaptationDim : SHADER_PERMUTATION_BOOL("EYEADAPTATION_EXPOSURE_FIX"); class FTonemapperUseFXAA : SHADER_PERMUTATION_BOOL("USE_FXAA"); using FCommonDomain = TShaderPermutationDomain< FTonemapperBloomDim, FTonemapperGammaOnlyDim, FTonemapperGrainIntensityDim, FTonemapperVignetteDim, FTonemapperSharpenDim, FTonemapperGrainJitterDim, FTonemapperSwitchAxis, FTonemapperMsaaDim, FTonemapperUseFXAA>; bool ShouldCompileCommonPermutation(const FGlobalShaderPermutationParameters& Parameters, const FCommonDomain& PermutationVector) { // Prevent switch axis permutation on platforms that dont require it. if (PermutationVector.Get() && !RHINeedsToSwitchVerticalAxis(Parameters.Platform)) { return false; } // MSAA pre-resolve step only used on iOS atm if (PermutationVector.Get() && !IsMetalMobilePlatform(Parameters.Platform)) { return false; } if (PermutationVector.Get() && !IsMobilePlatform(Parameters.Platform)) { return false; } // If GammaOnly, don't compile any other dimmension == true. if (PermutationVector.Get()) { return !PermutationVector.Get() && !PermutationVector.Get() && !PermutationVector.Get() && !PermutationVector.Get() && !PermutationVector.Get() && !PermutationVector.Get(); } return true; } // Common conversion of engine settings into. FCommonDomain BuildCommonPermutationDomain(const FViewInfo& View, bool bGammaOnly, bool bSwitchVerticalAxis, bool bMetalMSAAHDRDecode) { const FSceneViewFamily* Family = View.Family; FCommonDomain PermutationVector; // Gamma if (bGammaOnly || (Family->EngineShowFlags.Tonemapper == 0) || (Family->EngineShowFlags.PostProcessing == 0)) { PermutationVector.Set(true); return PermutationVector; } const FPostProcessSettings& Settings = View.FinalPostProcessSettings; PermutationVector.Set(Settings.GrainIntensity > 0.0f); PermutationVector.Set(Settings.VignetteIntensity > 0.0f); PermutationVector.Set(Settings.BloomIntensity > 0.0); PermutationVector.Set(Settings.GrainJitter > 0.0f); PermutationVector.Set(CVarTonemapperSharpen.GetValueOnRenderThread() > 0.0f); PermutationVector.Set(bSwitchVerticalAxis); PermutationVector.Set(bMetalMSAAHDRDecode); if (IsMobilePlatform(View.GetShaderPlatform())) { PermutationVector.Set(View.AntiAliasingMethod == AAM_FXAA); } return PermutationVector; } // Desktop renderer permutation dimensions. class FTonemapperColorFringeDim : SHADER_PERMUTATION_BOOL("USE_COLOR_FRINGE"); class FTonemapperGrainQuantizationDim : SHADER_PERMUTATION_BOOL("USE_GRAIN_QUANTIZATION"); class FTonemapperOutputDeviceDim : SHADER_PERMUTATION_ENUM_CLASS("DIM_OUTPUT_DEVICE", ETonemapperOutputDevice); using FDesktopDomain = TShaderPermutationDomain< FCommonDomain, FTonemapperColorFringeDim, FTonemapperGrainQuantizationDim, FTonemapperOutputDeviceDim>; FDesktopDomain RemapPermutation(FDesktopDomain PermutationVector, ERHIFeatureLevel::Type FeatureLevel) { FCommonDomain CommonPermutationVector = PermutationVector.Get(); // No remapping if gamma only. if (CommonPermutationVector.Get()) { return PermutationVector; } // Grain jitter or intensity looks bad anyway. bool bFallbackToSlowest = false; bFallbackToSlowest = bFallbackToSlowest || CommonPermutationVector.Get(); bFallbackToSlowest = bFallbackToSlowest || CommonPermutationVector.Get(); if (bFallbackToSlowest) { CommonPermutationVector.Set(true); CommonPermutationVector.Set(true); CommonPermutationVector.Set(true); PermutationVector.Set(true); } // You most likely need Bloom anyway. CommonPermutationVector.Set(true); // Mobile supports only sRGB and LinearNoToneCurve output if (FeatureLevel <= ERHIFeatureLevel::ES3_1 && PermutationVector.Get() != ETonemapperOutputDevice::LinearNoToneCurve) { PermutationVector.Set(ETonemapperOutputDevice::sRGB); } // Disable grain quantization for LinearNoToneCurve and LinearWithToneCurve output device if (PermutationVector.Get() == ETonemapperOutputDevice::LinearNoToneCurve || PermutationVector.Get() == ETonemapperOutputDevice::LinearWithToneCurve) PermutationVector.Set(false); else PermutationVector.Set(true); PermutationVector.Set(CommonPermutationVector); return PermutationVector; } bool ShouldCompileDesktopPermutation(const FGlobalShaderPermutationParameters& Parameters, FDesktopDomain PermutationVector) { auto CommonPermutationVector = PermutationVector.Get(); if (RemapPermutation(PermutationVector, GetMaxSupportedFeatureLevel(Parameters.Platform)) != PermutationVector) { return false; } if (!ShouldCompileCommonPermutation(Parameters, CommonPermutationVector)) { return false; } if (CommonPermutationVector.Get()) { return !PermutationVector.Get() && !PermutationVector.Get(); } return true; } } // namespace TonemapperPermutation } // namespace void GrainPostSettings(FVector3f* RESTRICT const Constant, const FPostProcessSettings* RESTRICT const Settings) { float GrainJitter = Settings->GrainJitter; float GrainIntensity = Settings->GrainIntensity; Constant->X = GrainIntensity; Constant->Y = 1.0f + (-0.5f * GrainIntensity); Constant->Z = GrainJitter; } FTonemapperOutputDeviceParameters GetTonemapperOutputDeviceParameters(const FSceneViewFamily& Family) { static TConsoleVariableData* CVarOutputGamut = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.ColorGamut")); static TConsoleVariableData* CVarOutputDevice = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.HDR.Display.OutputDevice")); static TConsoleVariableData* CVarOutputGamma = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma")); ETonemapperOutputDevice OutputDeviceValue; if (Family.SceneCaptureSource == SCS_FinalColorHDR) { OutputDeviceValue = ETonemapperOutputDevice::LinearNoToneCurve; } else if (Family.SceneCaptureSource == SCS_FinalToneCurveHDR) { OutputDeviceValue = ETonemapperOutputDevice::LinearWithToneCurve; } else if (Family.bIsHDR) { OutputDeviceValue = ETonemapperOutputDevice::ACES1000nitST2084; } else { OutputDeviceValue = static_cast(CVarOutputDevice->GetValueOnRenderThread()); OutputDeviceValue = static_cast(FMath::Clamp(static_cast(OutputDeviceValue), 0, static_cast(ETonemapperOutputDevice::MAX) - 1)); } float Gamma = CVarOutputGamma->GetValueOnRenderThread(); if (PLATFORM_APPLE && Gamma == 0.0f) { Gamma = 2.2f; } // Enforce user-controlled ramp over sRGB or Rec709 if (Gamma > 0.0f && (OutputDeviceValue == ETonemapperOutputDevice::sRGB || OutputDeviceValue == ETonemapperOutputDevice::Rec709)) { OutputDeviceValue = ETonemapperOutputDevice::ExplicitGammaMapping; } FVector InvDisplayGammaValue; InvDisplayGammaValue.X = 1.0f / Family.RenderTarget->GetDisplayGamma(); InvDisplayGammaValue.Y = 2.2f / Family.RenderTarget->GetDisplayGamma(); InvDisplayGammaValue.Z = 1.0f / FMath::Max(Gamma, 1.0f); FTonemapperOutputDeviceParameters Parameters; Parameters.InverseGamma = InvDisplayGammaValue; Parameters.OutputDevice = static_cast(OutputDeviceValue); Parameters.OutputGamut = CVarOutputGamut->GetValueOnRenderThread(); return Parameters; } BEGIN_SHADER_PARAMETER_STRUCT(FFilmGrainParameters, ) SHADER_PARAMETER(FVector3f, GrainRandomFull) SHADER_PARAMETER(FVector3f, GrainScaleBiasJitter) END_SHADER_PARAMETER_STRUCT() FFilmGrainParameters GetFilmGrainParameters(const FViewInfo& View) { FVector3f GrainRandomFullValue; { uint8 FrameIndexMod8 = 0; if (View.State) { FrameIndexMod8 = View.ViewState->GetFrameIndex(8); } GrainRandomFromFrame(&GrainRandomFullValue, FrameIndexMod8); } FVector3f GrainScaleBiasJitter; GrainPostSettings(&GrainScaleBiasJitter, &View.FinalPostProcessSettings); FFilmGrainParameters Parameters; Parameters.GrainRandomFull = GrainRandomFullValue; Parameters.GrainScaleBiasJitter = GrainScaleBiasJitter; return Parameters; } BEGIN_SHADER_PARAMETER_STRUCT(FTonemapParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_STRUCT_INCLUDE(FFilmGrainParameters, FilmGrain) SHADER_PARAMETER_STRUCT_INCLUDE(FTonemapperOutputDeviceParameters, OutputDevice) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Color) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Bloom) SHADER_PARAMETER_STRUCT(FScreenPassTextureViewportParameters, Output) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ColorTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, BloomTexture) // SM5 and above use Texture2D for EyeAdaptationTexture SHADER_PARAMETER_RDG_TEXTURE(Texture2D, EyeAdaptationTexture) SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ColorGradingLUT) SHADER_PARAMETER_TEXTURE(Texture2D, BloomDirtMaskTexture) SHADER_PARAMETER_SAMPLER(SamplerState, ColorSampler) SHADER_PARAMETER_SAMPLER(SamplerState, BloomSampler) SHADER_PARAMETER_SAMPLER(SamplerState, ColorGradingLUTSampler) SHADER_PARAMETER_SAMPLER(SamplerState, BloomDirtMaskSampler) SHADER_PARAMETER(FScreenTransform, ColorToBloom) SHADER_PARAMETER(FVector4, ColorScale0) SHADER_PARAMETER(FVector4, ColorScale1) SHADER_PARAMETER(FVector4, BloomDirtMaskTint) SHADER_PARAMETER(FVector4, ChromaticAberrationParams) SHADER_PARAMETER(FVector4, TonemapperParams) SHADER_PARAMETER(FVector4, LensPrincipalPointOffsetScale) SHADER_PARAMETER(FVector4, LensPrincipalPointOffsetScaleInverse) SHADER_PARAMETER(float, SwitchVerticalAxis) SHADER_PARAMETER(float, DefaultEyeExposure) SHADER_PARAMETER(float, EditorNITLevel) SHADER_PARAMETER(uint32, bOutputInHDR) // ES3_1 uses EyeAdaptationBuffer SHADER_PARAMETER_RDG_BUFFER_SRV(Buffer, EyeAdaptationBuffer) END_SHADER_PARAMETER_STRUCT() class FTonemapVS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTonemapVS); // FDrawRectangleParameters is filled by DrawScreenPass. SHADER_USE_PARAMETER_STRUCT_WITH_LEGACY_BASE(FTonemapVS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain; using FParameters = FTonemapParameters; static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { FPermutationDomain PermutationVector(Parameters.PermutationId); // Prevent switch axis permutation on platforms that dont require it. if (PermutationVector.Get() && !RHINeedsToSwitchVerticalAxis(Parameters.Platform)) { return false; } return true; } }; IMPLEMENT_GLOBAL_SHADER(FTonemapVS, "/Engine/Private/PostProcessTonemap.usf", "MainVS", SF_Vertex); class FTonemapPS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTonemapPS); SHADER_USE_PARAMETER_STRUCT(FTonemapPS, FGlobalShader); using FPermutationDomain = TonemapperPermutation::FDesktopDomain; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FTonemapParameters, Tonemap) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { if (!IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::ES3_1)) { return false; } return TonemapperPermutation::ShouldCompileDesktopPermutation(Parameters, FPermutationDomain(Parameters.PermutationId)); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { const int UseVolumeLut = PipelineVolumeTextureLUTSupportGuaranteedAtRuntime(Parameters.Platform) ? 1 : 0; OutEnvironment.SetDefine(TEXT("USE_VOLUME_LUT"), UseVolumeLut); } }; IMPLEMENT_GLOBAL_SHADER(FTonemapPS, "/Engine/Private/PostProcessTonemap.usf", "MainPS", SF_Pixel); class FTonemapCS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FTonemapCS); SHADER_USE_PARAMETER_STRUCT(FTonemapCS, FGlobalShader); using FPermutationDomain = TShaderPermutationDomain< TonemapperPermutation::FDesktopDomain, TonemapperPermutation::FTonemapperEyeAdaptationDim>; BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_INCLUDE(FTonemapParameters, Tonemap) SHADER_PARAMETER_RDG_TEXTURE_UAV(RWTexture2D, RWOutputTexture) END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { if (!IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5)) { return false; } FPermutationDomain PermutationVector(Parameters.PermutationId); return TonemapperPermutation::ShouldCompileDesktopPermutation(Parameters, PermutationVector.Get()); } static void ModifyCompilationEnvironment(const FGlobalShaderPermutationParameters& Parameters, FShaderCompilerEnvironment& OutEnvironment) { OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEX"), GTonemapComputeTileSizeX); OutEnvironment.SetDefine(TEXT("THREADGROUP_SIZEY"), GTonemapComputeTileSizeY); const int UseVolumeLut = PipelineVolumeTextureLUTSupportGuaranteedAtRuntime(Parameters.Platform) ? 1 : 0; OutEnvironment.SetDefine(TEXT("USE_VOLUME_LUT"), UseVolumeLut); } }; IMPLEMENT_GLOBAL_SHADER(FTonemapCS, "/Engine/Private/PostProcessTonemap.usf", "MainCS", SF_Compute); FScreenPassTexture AddTonemapPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FTonemapInputs& Inputs) { if (!Inputs.bGammaOnly) { check(Inputs.ColorGradingTexture); } check(Inputs.SceneColor.IsValid()); const FSceneViewFamily& ViewFamily = *(View.Family); const FPostProcessSettings& PostProcessSettings = View.FinalPostProcessSettings; const bool bIsEyeAdaptationResource = (View.GetFeatureLevel() >= ERHIFeatureLevel::SM5) ? Inputs.EyeAdaptationTexture != nullptr : Inputs.EyeAdaptationBuffer != nullptr; const bool bEyeAdaptation = ViewFamily.EngineShowFlags.EyeAdaptation && bIsEyeAdaptationResource; const FScreenPassTextureViewport SceneColorViewport(Inputs.SceneColor); FScreenPassRenderTarget Output = Inputs.OverrideOutput; if (!Output.IsValid()) { FRDGTextureDesc OutputDesc = Inputs.SceneColor.Texture->Desc; OutputDesc.Reset(); OutputDesc.Flags |= View.bUseComputePasses ? TexCreate_UAV : TexCreate_RenderTargetable; OutputDesc.Flags |= GFastVRamConfig.Tonemap; // RGB is the color in LDR, A is the luminance for PostprocessAA OutputDesc.Format = Inputs.bOutputInHDR ? GRHIHDRDisplayOutputFormat : PF_B8G8R8A8; OutputDesc.ClearValue = FClearValueBinding(FLinearColor(0, 0, 0, 0)); const FTonemapperOutputDeviceParameters OutputDeviceParameters = GetTonemapperOutputDeviceParameters(*View.Family); const ETonemapperOutputDevice OutputDevice = static_cast(OutputDeviceParameters.OutputDevice); if (OutputDevice == ETonemapperOutputDevice::LinearEXR) { OutputDesc.Format = PF_A32B32G32R32F; } if (OutputDevice == ETonemapperOutputDevice::LinearNoToneCurve || OutputDevice == ETonemapperOutputDevice::LinearWithToneCurve) { OutputDesc.Format = PF_FloatRGBA; } Output = FScreenPassRenderTarget( GraphBuilder.CreateTexture(OutputDesc, TEXT("Tonemap")), Inputs.SceneColor.ViewRect, ERenderTargetLoadAction::EClear); } const FScreenPassTextureViewport OutputViewport(Output); FRHITexture* BloomDirtMaskTexture = GBlackTexture->TextureRHI; if (PostProcessSettings.BloomDirtMask && PostProcessSettings.BloomDirtMask->GetResource()) { BloomDirtMaskTexture = PostProcessSettings.BloomDirtMask->GetResource()->TextureRHI; } FRHISamplerState* BilinearClampSampler = TStaticSamplerState::GetRHI(); FRHISamplerState* PointClampSampler = TStaticSamplerState::GetRHI(); const float DefaultEyeExposure = bEyeAdaptation ? 0.0f : GetEyeAdaptationFixedExposure(View); const float SharpenDiv6 = FMath::Clamp(CVarTonemapperSharpen.GetValueOnRenderThread(), 0.0f, 10.0f) / 6.0f; FVector4 ChromaticAberrationParams; { // for scene color fringe // from percent to fraction float Offset = 0.0f; float StartOffset = 0.0f; float Multiplier = 1.0f; if (PostProcessSettings.ChromaticAberrationStartOffset < 1.0f - KINDA_SMALL_NUMBER) { Offset = PostProcessSettings.SceneFringeIntensity * 0.01f; StartOffset = PostProcessSettings.ChromaticAberrationStartOffset; Multiplier = 1.0f / (1.0f - StartOffset); } // Wavelength of primaries in nm const float PrimaryR = 611.3f; const float PrimaryG = 549.1f; const float PrimaryB = 464.3f; // Simple lens chromatic aberration is roughly linear in wavelength float ScaleR = 0.007f * (PrimaryR - PrimaryB); float ScaleG = 0.007f * (PrimaryG - PrimaryB); ChromaticAberrationParams = FVector4(Offset * ScaleR * Multiplier, Offset * ScaleG * Multiplier, StartOffset, 0.f); } float EditorNITLevel = 160.0f; #if WITH_EDITOR { static auto CVarHDRNITLevel = IConsoleManager::Get().FindConsoleVariable(TEXT("Editor.HDRNITLevel")); if (CVarHDRNITLevel) { EditorNITLevel = CVarHDRNITLevel->GetFloat(); } } #endif FTonemapParameters CommonParameters; CommonParameters.View = View.ViewUniformBuffer; CommonParameters.FilmGrain = GetFilmGrainParameters(View); CommonParameters.OutputDevice = GetTonemapperOutputDeviceParameters(ViewFamily); CommonParameters.Color = GetScreenPassTextureViewportParameters(SceneColorViewport); if (Inputs.Bloom.Texture) { const FScreenPassTextureViewport BloomViewport(Inputs.Bloom); CommonParameters.Bloom = GetScreenPassTextureViewportParameters(BloomViewport); CommonParameters.ColorToBloom = FScreenTransform::ChangeTextureUVCoordinateFromTo(SceneColorViewport, BloomViewport); } CommonParameters.Output = GetScreenPassTextureViewportParameters(OutputViewport); CommonParameters.ColorTexture = Inputs.SceneColor.Texture; CommonParameters.BloomTexture = Inputs.Bloom.Texture; CommonParameters.EyeAdaptationTexture = Inputs.EyeAdaptationTexture; CommonParameters.ColorGradingLUT = Inputs.ColorGradingTexture; CommonParameters.BloomDirtMaskTexture = BloomDirtMaskTexture; CommonParameters.ColorSampler = BilinearClampSampler; CommonParameters.BloomSampler = BilinearClampSampler; CommonParameters.ColorGradingLUTSampler = BilinearClampSampler; CommonParameters.BloomDirtMaskSampler = BilinearClampSampler; CommonParameters.ColorScale0 = PostProcessSettings.SceneColorTint; CommonParameters.ColorScale1 = FLinearColor::White * PostProcessSettings.BloomIntensity; CommonParameters.BloomDirtMaskTint = PostProcessSettings.BloomDirtMaskTint * PostProcessSettings.BloomDirtMaskIntensity; CommonParameters.ChromaticAberrationParams = ChromaticAberrationParams; CommonParameters.TonemapperParams = FVector4(PostProcessSettings.VignetteIntensity, SharpenDiv6, 0.0f, 0.0f); CommonParameters.SwitchVerticalAxis = Inputs.bFlipYAxis; CommonParameters.DefaultEyeExposure = DefaultEyeExposure; CommonParameters.EditorNITLevel = EditorNITLevel; CommonParameters.bOutputInHDR = ViewFamily.bIsHDR; CommonParameters.LensPrincipalPointOffsetScale = View.LensPrincipalPointOffsetScale; // forward transformation from shader: //return LensPrincipalPointOffsetScale.xy + UV * LensPrincipalPointOffsetScale.zw; // reverse transformation from shader: //return UV*(1.0f/LensPrincipalPointOffsetScale.zw) - LensPrincipalPointOffsetScale.xy/LensPrincipalPointOffsetScale.zw; CommonParameters.LensPrincipalPointOffsetScaleInverse.X = -View.LensPrincipalPointOffsetScale.X / View.LensPrincipalPointOffsetScale.Z; CommonParameters.LensPrincipalPointOffsetScaleInverse.Y = -View.LensPrincipalPointOffsetScale.Y / View.LensPrincipalPointOffsetScale.W; CommonParameters.LensPrincipalPointOffsetScaleInverse.Z = 1.0f / View.LensPrincipalPointOffsetScale.Z; CommonParameters.LensPrincipalPointOffsetScaleInverse.W = 1.0f / View.LensPrincipalPointOffsetScale.W; FRDGBufferSRVRef EyeAdaptationBufferSRV = Inputs.EyeAdaptationBuffer != nullptr ? GraphBuilder.CreateSRV(Inputs.EyeAdaptationBuffer, PF_A32B32G32R32F) : nullptr; CommonParameters.EyeAdaptationBuffer = EyeAdaptationBufferSRV; // Generate permutation vector for the desktop tonemapper. TonemapperPermutation::FDesktopDomain DesktopPermutationVector; { TonemapperPermutation::FCommonDomain CommonDomain = TonemapperPermutation::BuildCommonPermutationDomain(View, Inputs.bGammaOnly, Inputs.bFlipYAxis, Inputs.bMetalMSAAHDRDecode); DesktopPermutationVector.Set(CommonDomain); if (!CommonDomain.Get()) { // Grain Quantization { static TConsoleVariableData* CVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Tonemapper.GrainQuantization")); const int32 Value = CVar->GetValueOnRenderThread(); DesktopPermutationVector.Set(Value > 0); } DesktopPermutationVector.Set(PostProcessSettings.SceneFringeIntensity > 0.01f); } DesktopPermutationVector.Set(ETonemapperOutputDevice(CommonParameters.OutputDevice.OutputDevice)); DesktopPermutationVector = TonemapperPermutation::RemapPermutation(DesktopPermutationVector, View.GetFeatureLevel()); } // Override output might not support UAVs. const bool bComputePass = (Output.Texture->Desc.Flags & TexCreate_UAV) == TexCreate_UAV ? View.bUseComputePasses : false; if (bComputePass) { FTonemapCS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Tonemap = CommonParameters; PassParameters->RWOutputTexture = GraphBuilder.CreateUAV(Output.Texture); FTonemapCS::FPermutationDomain PermutationVector; PermutationVector.Set(DesktopPermutationVector); PermutationVector.Set(bEyeAdaptation); TShaderMapRef ComputeShader(View.ShaderMap, PermutationVector); FComputeShaderUtils::AddPass( GraphBuilder, RDG_EVENT_NAME("Tonemap %dx%d (CS GammaOnly=%d)", OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), Inputs.bGammaOnly), ComputeShader, PassParameters, FComputeShaderUtils::GetGroupCount(OutputViewport.Rect.Size(), FIntPoint(GTonemapComputeTileSizeX, GTonemapComputeTileSizeY))); } else { FTonemapPS::FParameters* PassParameters = GraphBuilder.AllocParameters(); PassParameters->Tonemap = CommonParameters; PassParameters->RenderTargets[0] = Output.GetRenderTargetBinding(); FTonemapVS::FPermutationDomain VertexPermutationVector; VertexPermutationVector.Set(Inputs.bFlipYAxis); VertexPermutationVector.Set(bEyeAdaptation); TShaderMapRef VertexShader(View.ShaderMap, VertexPermutationVector); TShaderMapRef PixelShader(View.ShaderMap, DesktopPermutationVector); // If this is a stereo view, there's a good chance we need alpha out of the tonemapper // @todo: Remove this once Oculus fix the bug in their runtime that requires alpha here. const bool bIsStereo = IStereoRendering::IsStereoEyeView(View); FRHIBlendState* BlendState = Inputs.bWriteAlphaChannel || bIsStereo ? FScreenPassPipelineState::FDefaultBlendState::GetRHI() : TStaticBlendStateWriteMask::GetRHI(); FRHIDepthStencilState* DepthStencilState = FScreenPassPipelineState::FDefaultDepthStencilState::GetRHI(); EScreenPassDrawFlags DrawFlags = EScreenPassDrawFlags::AllowHMDHiddenAreaMask; AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("Tonemap %dx%d (PS GammaOnly=%d)", OutputViewport.Rect.Width(), OutputViewport.Rect.Height(), Inputs.bGammaOnly), View, OutputViewport, SceneColorViewport, FScreenPassPipelineState(VertexShader, PixelShader, BlendState, DepthStencilState), PassParameters, DrawFlags, [VertexShader, PixelShader, PassParameters](FRHICommandList& RHICmdList) { SetShaderParameters(RHICmdList, VertexShader, VertexShader.GetVertexShader(), PassParameters->Tonemap); SetShaderParameters(RHICmdList, PixelShader, PixelShader.GetPixelShader(), *PassParameters); }); } return MoveTemp(Output); }