Files
UnrealEngineUWP/Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessCombineLUTs.cpp
Gil Gribb 07eea7c4b8 Copying //UE4/Dev-Rendering to //UE4/Dev-Main (Source: //UE4/Dev-Rendering @ 2967470)
#lockdown nick.penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2943963 on 2016/04/14 by Daniel.Wright

	Shader compile errors are unsuppressed

Change 2943978 on 2016/04/14 by Gil.Gribb

	UE4 - First pass at async loading improvements....mostly disabled.

Change 2944021 on 2016/04/14 by Martin.Mittring

	fixed HLSL compiler warning

Change 2944031 on 2016/04/14 by Martin.Mittring

	fixed ensures, wrapped some members behind get accessor functions

Change 2944086 on 2016/04/14 by Martin.Mittring

	cleanup: removed not needed code

Change 2944177 on 2016/04/14 by Daniel.Wright

	Clamp on FarShadowCascadeCount, prevents crashing from huge values

Change 2944182 on 2016/04/14 by Martin.Mittring

	removed not needed code

Change 2944250 on 2016/04/14 by Rolando.Caloca

	DR - vk - Minor fixes

Change 2944286 on 2016/04/14 by Daniel.Wright

	Added bRenderSceneTwoSided to planar reflections, which can be useful to limit leaking
	Added ShowOnlyActors and HiddenActors to SceneCaptureComponent for easy use without having to call BP functions
	Added bShowPreviewPlane to planar reflection actors
	The view state is recreated on planar reflection edit, which resets the Temporal AA history, allowing instant previewing of changes

Change 2944288 on 2016/04/14 by Daniel.Wright

	Fixed refraction with a world space normal

Change 2944291 on 2016/04/14 by Daniel.Wright

	Panner nodes have an optional speed input

Change 2944346 on 2016/04/14 by Rolando.Caloca

	DR - Fix Vulkan shader platform on Android
	- Added more info on checks()

Change 2945007 on 2016/04/15 by Gil.Gribb

	Merging //UE4/Dev-Main@2944911 to Dev-Rendering (//UE4/Dev-Rendering)

Change 2945348 on 2016/04/15 by Daniel.Wright

	Fixed compile error

Change 2945358 on 2016/04/15 by Olaf.Piesche

	#jira UE-29241

	Sequential particle selection code was all sorts of weird. Rewrote and simplified.

Change 2945941 on 2016/04/15 by Martin.Mittring

	added r.DisplayInternals to debug determinism for screen shot comparison

Change 2945999 on 2016/04/15 by Martin.Mittring

	improved r.DisplayInternal output

Change 2946023 on 2016/04/15 by Olaf.Piesche

	Adding missing call to Super::PostEditChangeProperty; UDN 286717

Change 2947155 on 2016/04/18 by Martin.Mittring

	started minor cleanup of transluceny rendering, use Sort key to support SeparateTransluceny, not fully hooked up
	#test:PC

Change 2947207 on 2016/04/18 by Martin.Mittring

	fixed engine compiling in shipping/test
	#code_review:Uriel.Doyan

Change 2947212 on 2016/04/18 by Uriel.Doyon

	Lightmap density viewmode now shows the wanted resolution when the lighting isn't build.
	#jira UE-29317

Change 2947374 on 2016/04/18 by Uriel.Doyon

	Fixed support for resolution scale for the PostProcessVisualizeComplexity
	#jira UE-29473

Change 2947903 on 2016/04/19 by Gil.Gribb

	Merging //UE4/Dev-Main@2947728 to Dev-Rendering (//UE4/Dev-Rendering)

Change 2948019 on 2016/04/19 by Rolando.Caloca

	DR - Allow vk format as a target format for win

Change 2948162 on 2016/04/19 by Simon.Tovey

	Fix for crash with Collision visualization.

Change 2948419 on 2016/04/19 by Martin.Mittring

	fixed sort priority of translucent rendering (caused by recent checkin)

Change 2948433 on 2016/04/19 by Martin.Mittring

	fixed memory handling of FRendererViewExtension

Change 2948631 on 2016/04/19 by Martin.Mittring

	fixed compile error on Mac

Change 2948832 on 2016/04/19 by Martin.Mittring

	fixed UE-29572 (should result in less CPU cost and it might even fix some rendeirng issues)

Change 2949013 on 2016/04/19 by Martin.Mittring

	refactored Transluceny rendering, SepTrans and non SepTrans is now in the same container, sorted by that critera first and rendered with ranges. This makes it easier to extend it to more transluceny types e.g. after TemporalAA, after Tonemapping
	this is useful for MeshDecals
	#test:PC, parallel on and off

Change 2949620 on 2016/04/20 by Martin.Mittring

	fixed compiler warning

Change 2949639 on 2016/04/20 by Uriel.Doyon

	Fixed Material TexCoord Analysis not compiling when sampling textures for shader frequency other than PixelShader

Change 2949721 on 2016/04/20 by Chris.Bunner

	Avoid creating additional inline code fragment casting matching uniform types.
	#jira UE-29089

Change 2949722 on 2016/04/20 by Chris.Bunner

	Prevent nullptr crash and added additional logging.
	#jira UE-28387

Change 2949913 on 2016/04/20 by Martin.Mittring

	marked ccommand as cheat

Change 2950064 on 2016/04/20 by Martin.Mittring

	added MatineeTime to r.DisplayInternals to track down rendering determinsim issues, added dark background

Change 2950065 on 2016/04/20 by Martin.Mittring

	nicer debug printout

Change 2950201 on 2016/04/20 by Martin.Mittring

	fixed UE-29752 Console commands input with " = " should display an error message

Change 2950531 on 2016/04/20 by Martin.Mittring

	fixed comment

Change 2951737 on 2016/04/21 by HaarmPieter.Duiker

	Adds support forHDR displays using Dolby PQ output

Change 2951869 on 2016/04/21 by Martin.Mittring

	polish r.DisplayInternal

Change 2951950 on 2016/04/21 by HaarmPieter.Duiker

	Reordered variable definition to address build warning

Change 2951996 on 2016/04/21 by Martin.Mittring

	fixed PerformanceCapture code, added AutomationTest "Rendering.RenderOutputValidation",
	changed directory order
	to run locally it currently requires "r.ScreenshotDelegate=0"
	#code_review:Ben.Salem, Michael.Noland

Change 2952146 on 2016/04/21 by Olaf.Piesche

	make sure that ST PDI primitives render through regular translucency if ST is disabled; fixes light shapes in scene/reflection captures

Change 2952230 on 2016/04/21 by Martin.Mittring

	* Fixed automated ScreenshotVerify difference because of not streamed in texture, wait for up to 5sec .
	* changed some GFrameNumberRenderThread usage to ViewFamily.FrameNumber
	#code_review:Daniel.Wright

Change 2953173 on 2016/04/22 by Olaf.Piesche

	Adding UI for easilly browsing and switching in a folder full of stats dumps

Change 2953213 on 2016/04/22 by Olaf.Piesche

	Renaming a stat to be more descriptive

Change 2953393 on 2016/04/22 by Zabir.Hoque

	Get DX12 running again:
	  - Port Shader Resource Table change
	  - Line up VS outputs and ps inputs
	  - Fix incorrectly defining a static global in a .h

Change 2953453 on 2016/04/22 by Martin.Mittring

	polished r.DisplayInternal

Change 2954618 on 2016/04/25 by Zabir.Hoque

	2 Fixes:
	  - GLSL does not understand "unsigned int", converted to "uint"
	  - Refactored problematic prev buffer allocation code to be more inline with proper level of abstraction.

Change 2955369 on 2016/04/25 by Rolando.Caloca

	DR - hlslcc - Fix some memory leaks in the frontend

Change 2955403 on 2016/04/25 by Uriel.Doyon

	Fixed texture streaming build on OpenGL. Probably more likely to work on other platforms like Mac and Linux.
	Enabled debug view shaders on PCD3D_SM4 and OPENGL_SM4
	#jira UE-28840

Change 2955419 on 2016/04/25 by Rolando.Caloca

	DR - hlslcc - Reenabled support for static global variables being not const

Change 2955432 on 2016/04/25 by Zabir.Hoque

	Fix build break from not undef'ing LOCTEXT_NAMESPACE

Change 2955459 on 2016/04/25 by Zabir.Hoque

	TEMP Fix: On server enqued render thread work is dropped. So on server release Reflection capture resouce immediately instead of trying to defer enque.

Change 2956292 on 2016/04/26 by Zabir.Hoque

	Fix OpenGL shader compile break from CL: 2951737 (Adds support forHDR displays using Dolby PQ output).

	#CodeReview: Jack.Porter, Allan.Bentham

Change 2956662 on 2016/04/26 by Chris.Bunner

	Temporary fix for new Tonemapper issues.
	#jira UE-29935

Change 2957614 on 2016/04/27 by Marcus.Wassmer

	Fix PS4 shader compiler errors.

Change 2958468 on 2016/04/27 by Rolando.Caloca

	DR - Fix hlslcc validation issue
	- Show error on SCW if shader format not found when running with -directcompile
	#jira UE-29982

Change 2959105 on 2016/04/28 by Rolando.Caloca

	DR - Rebuilt hlslcc for Mac

Change 2959891 on 2016/04/28 by Daniel.Wright

	Shader compiler does a recreate render state even during blocking compile - fixes saving a material giving different behavior from applying changes with global distance fields

Change 2959895 on 2016/04/28 by Daniel.Wright

	Work around build machine string matching heuristics that will cause a cook to fail

Change 2959902 on 2016/04/28 by Daniel.Wright

	Added LowerHemisphereSolidColor to sky lights

Change 2959930 on 2016/04/28 by Daniel.Wright

	Added OpacitySourceMode to SubUVAnimation, which is useful with textures created for additive particles

Change 2959933 on 2016/04/28 by Daniel.Wright

	Substring matching for console command suggestions
	* Only implemented in the editor, game uses UConsole which needs an entirely different implementation
	* Not sorting starting matches first, although that is desired

Change 2959942 on 2016/04/28 by Daniel.Wright

	Gracefully handle when input string doesn't match search results

Change 2960743 on 2016/04/29 by Gil.Gribb

	UE4 - UAT - Add map name to editortest command line.

Change 2960940 on 2016/04/29 by Chris.Bunner

	Allow custom material nodes to be used with tessellation outputs.
	#jira UE-29586

Change 2960955 on 2016/04/29 by Gil.Gribb

	UE4 - Improved the CPU burden of loading in several places. Made substantial progress on the complete loading revamp (currently disabled).

Change 2960961 on 2016/04/29 by Chris.Bunner

	Potential material translator Lerp node pre-computations/optimizations.
	#jira OR-20138

Change 2961087 on 2016/04/29 by Gil.Gribb

	Fixed compile error in preflight relating to load time test rig

Change 2962565 on 2016/05/02 by Gil.Gribb

	Merging //UE4/Dev-Main@2962478 to Dev-Rendering (//UE4/Dev-Rendering)

Change 2965058 on 2016/05/03 by Chris.Bunner

	Shader version bump.
	#lockdown Gil.Gribb
	#jira UE-30206

Change 2966554 on 2016/05/04 by Chris.Bunner

	Bumping shader version again, unintentionally polluted DDC previously.
	#lockdown Gil.Gribb
	#jira UE-30329

Change 2967183 on 2016/05/05 by Gil.Gribb

	UE4 - Fixed a bad hash on landscape grass components. Simple, safe.
	#lockdown nick.penwarden

[CL 2967480 by Gil Gribb in Main branch]
2016-05-05 12:13:26 -04:00

753 lines
28 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
PostProcessCombineLUTs.cpp: Post processing tone mapping implementation.
=============================================================================*/
#include "RendererPrivate.h"
#include "ScenePrivate.h"
#include "SceneFilterRendering.h"
#include "PostProcessCombineLUTs.h"
#include "PostProcessing.h"
#include "ScreenRendering.h"
#include "SceneUtils.h"
// CVars
static TAutoConsoleVariable<float> CVarColorMin(
TEXT("r.Color.Min"),
0.0f,
TEXT("Allows to define where the value 0 in the color channels is mapped to after color grading.\n")
TEXT("The value should be around 0, positive: a gray scale is added to the darks, negative: more dark values become black, Default: 0"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarColorMid(
TEXT("r.Color.Mid"),
0.5f,
TEXT("Allows to define where the value 0.5 in the color channels is mapped to after color grading (This is similar to a gamma correction).\n")
TEXT("Value should be around 0.5, smaller values darken the mid tones, larger values brighten the mid tones, Default: 0.5"),
ECVF_RenderThreadSafe);
static TAutoConsoleVariable<float> CVarColorMax(
TEXT("r.Color.Max"),
1.0f,
TEXT("Allows to define where the value 1.0 in the color channels is mapped to after color grading.\n")
TEXT("Value should be around 1, smaller values darken the highlights, larger values move more colors towards white, Default: 1"),
ECVF_RenderThreadSafe);
int32 GLUTSize = 32;
static FAutoConsoleVariableRef CVarLUTSize(
TEXT("r.LUT.Size"),
GLUTSize,
TEXT("Size of film LUT"),
ECVF_RenderThreadSafe
);
static TAutoConsoleVariable<int32> CVarTonemapperFilm(
TEXT("r.TonemapperFilm"),
0,
TEXT("Use new film tone mapper"),
ECVF_RenderThreadSafe
);
// false:use 256x16 texture / true:use volume texture (faster, requires geometry shader)
// USE_VOLUME_LUT: needs to be the same for C++ and HLSL
bool UseVolumeTextureLUT(EShaderPlatform Platform)
{
// @todo Mac OS X: in order to share precompiled shaders between GL 3.3 & GL 4.1 devices we mustn't use volume-texture rendering as it isn't universally supported.
return (IsFeatureLevelSupported(Platform,ERHIFeatureLevel::SM4) && GSupportsVolumeTextureRendering && Platform != EShaderPlatform::SP_OPENGL_SM4_MAC && RHISupportsGeometryShaders(Platform));
}
// including the neutral one at index 0
const uint32 GMaxLUTBlendCount = 5;
struct FColorTransform
{
FColorTransform()
{
Reset();
}
float MinValue;
float MidValue;
float MaxValue;
void Reset()
{
MinValue = 0.0f;
MidValue = 0.5f;
MaxValue = 1.0f;
}
};
/*-----------------------------------------------------------------------------
FColorRemapShaderParameters
-----------------------------------------------------------------------------*/
FColorRemapShaderParameters::FColorRemapShaderParameters(const FShaderParameterMap& ParameterMap)
{
MappingPolynomial.Bind(ParameterMap, TEXT("MappingPolynomial"));
}
void FColorRemapShaderParameters::Set(FRHICommandList& RHICmdList, const FPixelShaderRHIParamRef ShaderRHI)
{
FColorTransform ColorTransform;
ColorTransform.MinValue = FMath::Clamp(CVarColorMin.GetValueOnRenderThread(), -10.0f, 10.0f);
ColorTransform.MidValue = FMath::Clamp(CVarColorMid.GetValueOnRenderThread(), -10.0f, 10.0f);
ColorTransform.MaxValue = FMath::Clamp(CVarColorMax.GetValueOnRenderThread(), -10.0f, 10.0f);
{
// x is the input value, y the output value
// RGB = a, b, c where y = a * x*x + b * x + c
float c = ColorTransform.MinValue;
float b = 4 * ColorTransform.MidValue - 3 * ColorTransform.MinValue - ColorTransform.MaxValue;
float a = ColorTransform.MaxValue - ColorTransform.MinValue - b;
SetShaderValue(RHICmdList, ShaderRHI, MappingPolynomial, FVector(a, b, c));
}
}
FArchive& operator<<(FArchive& Ar, FColorRemapShaderParameters& P)
{
Ar << P.MappingPolynomial;
return Ar;
}
/**
* A pixel shader for blending multiple LUT to one
*
* @param BlendCount >0
*/
template<uint32 BlendCount>
class FLUTBlenderPS : public FGlobalShader
{
DECLARE_SHADER_TYPE(FLUTBlenderPS,Global);
public:
static bool ShouldCache(EShaderPlatform Platform) { return IsFeatureLevelSupported(Platform, ERHIFeatureLevel::ES2); }
FLUTBlenderPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
: FGlobalShader(Initializer)
, ColorRemapShaderParameters(Initializer.ParameterMap)
{
// Suppress static code analysis warnings about a potentially ill-defined loop. BlendCount > 0 is valid.
CA_SUPPRESS(6294)
// starts as 1 as 0 is the neutral one
for(uint32 i = 1; i < BlendCount; ++i)
{
FString Name = FString::Printf(TEXT("Texture%d"), i);
TextureParameter[i].Bind(Initializer.ParameterMap, *Name);
TextureParameterSampler[i].Bind(Initializer.ParameterMap, *(Name + TEXT("Sampler")));
}
WeightsParameter.Bind(Initializer.ParameterMap, TEXT("LUTWeights"));
ColorScale.Bind(Initializer.ParameterMap,TEXT("ColorScale"));
OverlayColor.Bind(Initializer.ParameterMap,TEXT("OverlayColor"));
InverseGamma.Bind(Initializer.ParameterMap,TEXT("InverseGamma"));
WhiteTemp.Bind( Initializer.ParameterMap,TEXT("WhiteTemp") );
WhiteTint.Bind( Initializer.ParameterMap,TEXT("WhiteTint") );
ColorSaturation.Bind( Initializer.ParameterMap,TEXT("ColorSaturation") );
ColorContrast.Bind( Initializer.ParameterMap,TEXT("ColorContrast") );
ColorGamma.Bind( Initializer.ParameterMap,TEXT("ColorGamma") );
ColorGain.Bind( Initializer.ParameterMap,TEXT("ColorGain") );
ColorOffset.Bind( Initializer.ParameterMap,TEXT("ColorOffset") );
FilmSlope.Bind( Initializer.ParameterMap,TEXT("FilmSlope") );
FilmToe.Bind( Initializer.ParameterMap,TEXT("FilmToe") );
FilmShoulder.Bind( Initializer.ParameterMap,TEXT("FilmShoulder") );
FilmBlackClip.Bind( Initializer.ParameterMap,TEXT("FilmBlackClip") );
FilmWhiteClip.Bind( Initializer.ParameterMap,TEXT("FilmWhiteClip") );
OutputDevice.Bind(Initializer.ParameterMap, TEXT("OutputDevice"));
OutputGamut.Bind(Initializer.ParameterMap, TEXT("OutputGamut"));
ACESInversion.Bind(Initializer.ParameterMap, TEXT("ACESInversion"));
ColorMatrixR_ColorCurveCd1.Bind(Initializer.ParameterMap, TEXT("ColorMatrixR_ColorCurveCd1"));
ColorMatrixG_ColorCurveCd3Cm3.Bind(Initializer.ParameterMap, TEXT("ColorMatrixG_ColorCurveCd3Cm3"));
ColorMatrixB_ColorCurveCm2.Bind(Initializer.ParameterMap, TEXT("ColorMatrixB_ColorCurveCm2"));
ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3"));
ColorCurve_Ch1_Ch2.Bind(Initializer.ParameterMap, TEXT("ColorCurve_Ch1_Ch2"));
ColorShadow_Luma.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Luma"));
ColorShadow_Tint1.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint1"));
ColorShadow_Tint2.Bind(Initializer.ParameterMap, TEXT("ColorShadow_Tint2"));
}
FLUTBlenderPS() {}
void SetParameters(FRHICommandList& RHICmdList, const FSceneView& View, FTexture* Texture[BlendCount], float Weights[BlendCount])
{
const FPostProcessSettings& Settings = View.FinalPostProcessSettings;
const FSceneViewFamily& ViewFamily = *(View.Family);
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
for(uint32 i = 0; i < BlendCount; ++i)
{
// we don't need to set the neutral one
if(i != 0)
{
// don't use texture asset sampler as it might have anisotropic filtering enabled
FSamplerStateRHIParamRef Sampler = TStaticSamplerState<SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp, 0, 1>::GetRHI();
SetTextureParameter(RHICmdList, ShaderRHI, TextureParameter[i], TextureParameterSampler[i], Sampler, Texture[i]->TextureRHI);
}
SetShaderValue(RHICmdList, ShaderRHI, WeightsParameter, Weights[i], i);
}
SetShaderValue(RHICmdList, ShaderRHI, ColorScale, View.ColorScale);
SetShaderValue(RHICmdList, ShaderRHI, OverlayColor, View.OverlayColor);
ColorRemapShaderParameters.Set(RHICmdList, ShaderRHI);
// White balance
SetShaderValue( RHICmdList, ShaderRHI, WhiteTemp, Settings.WhiteTemp );
SetShaderValue( RHICmdList, ShaderRHI, WhiteTint, Settings.WhiteTint );
// Color grade
SetShaderValue( RHICmdList, ShaderRHI, ColorSaturation, Settings.ColorSaturation );
SetShaderValue( RHICmdList, ShaderRHI, ColorContrast, Settings.ColorContrast );
SetShaderValue( RHICmdList, ShaderRHI, ColorGamma, Settings.ColorGamma );
SetShaderValue( RHICmdList, ShaderRHI, ColorGain, Settings.ColorGain );
SetShaderValue( RHICmdList, ShaderRHI, ColorOffset, Settings.ColorOffset );
// Film
SetShaderValue( RHICmdList, ShaderRHI, FilmSlope, Settings.FilmSlope );
SetShaderValue( RHICmdList, ShaderRHI, FilmToe, Settings.FilmToe );
SetShaderValue( RHICmdList, ShaderRHI, FilmShoulder, Settings.FilmShoulder );
SetShaderValue( RHICmdList, ShaderRHI, FilmBlackClip, Settings.FilmBlackClip );
SetShaderValue( RHICmdList, ShaderRHI, FilmWhiteClip, Settings.FilmWhiteClip );
{
static TConsoleVariableData<int32>* CVar709 = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Tonemapper709"));
static TConsoleVariableData<float>* CVarGamma = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
static TConsoleVariableData<int32>* CVar2084 = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Tonemapper2084"));
int32 Rec709 = CVar709->GetValueOnRenderThread();
int32 ST2084 = CVar2084->GetValueOnRenderThread();
float Gamma = CVarGamma->GetValueOnRenderThread();
if (PLATFORM_APPLE && Gamma == 0.0f)
{
Gamma = 2.2f;
}
int32 Value = 0; // sRGB
Value = Rec709 ? 1 : Value; // Rec709
Value = Gamma != 0.0f ? 2 : Value; // Explicit gamma
// ST-2084 (Dolby PQ) options
// 1 = ACES
// 2 = Vanilla PQ for 200 nit input
// 3 = Unreal FilmToneMap + Inverted ACES + PQ
Value = ST2084 >= 1 ? ST2084 + 2 : Value;
SetShaderValue(RHICmdList, ShaderRHI, OutputDevice, Value);
static TConsoleVariableData<int32>* CVarOutputGamut = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.TonemapperOutputGamut"));
int32 OutputGamutValue = CVarOutputGamut->GetValueOnRenderThread();
SetShaderValue(RHICmdList, ShaderRHI, OutputGamut, OutputGamutValue);
// The approach to use when applying the inverse ACES Output Transform
static TConsoleVariableData<int32>* CVarACESInversion = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.TonemapperACESInversion"));
int32 ACESInversionValue = CVarACESInversion->GetValueOnRenderThread();
SetShaderValue(RHICmdList, ShaderRHI, ACESInversion, ACESInversionValue);
FVector InvDisplayGammaValue;
InvDisplayGammaValue.X = 1.0f / ViewFamily.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Y = 2.2f / ViewFamily.RenderTarget->GetDisplayGamma();
InvDisplayGammaValue.Z = 1.0f / FMath::Max(Gamma, 1.0f);
SetShaderValue(RHICmdList, ShaderRHI, InverseGamma, InvDisplayGammaValue);
}
{
// Legacy tone mapper
// TODO remove
// Must insure inputs are in correct range (else possible generation of NaNs).
float InExposure = 1.0f;
FVector InWhitePoint(Settings.FilmWhitePoint);
float InSaturation = FMath::Clamp(Settings.FilmSaturation, 0.0f, 2.0f);
FVector InLuma = FVector(1.0f/3.0f, 1.0f/3.0f, 1.0f/3.0f);
FVector InMatrixR(Settings.FilmChannelMixerRed);
FVector InMatrixG(Settings.FilmChannelMixerGreen);
FVector InMatrixB(Settings.FilmChannelMixerBlue);
float InContrast = FMath::Clamp(Settings.FilmContrast, 0.0f, 1.0f) + 1.0f;
float InDynamicRange = powf(2.0f, FMath::Clamp(Settings.FilmDynamicRange, 1.0f, 4.0f));
float InToe = (1.0f - FMath::Clamp(Settings.FilmToeAmount, 0.0f, 1.0f)) * 0.18f;
InToe = FMath::Clamp(InToe, 0.18f/8.0f, 0.18f * (15.0f/16.0f));
float InHeal = 1.0f - (FMath::Max(1.0f/32.0f, 1.0f - FMath::Clamp(Settings.FilmHealAmount, 0.0f, 1.0f)) * (1.0f - 0.18f));
FVector InShadowTint(Settings.FilmShadowTint);
float InShadowTintBlend = FMath::Clamp(Settings.FilmShadowTintBlend, 0.0f, 1.0f) * 64.0f;
// Shadow tint amount enables turning off shadow tinting.
float InShadowTintAmount = FMath::Clamp(Settings.FilmShadowTintAmount, 0.0f, 1.0f);
InShadowTint = InWhitePoint + (InShadowTint - InWhitePoint) * InShadowTintAmount;
// Make sure channel mixer inputs sum to 1 (+ smart dealing with all zeros).
InMatrixR.X += 1.0f / (256.0f*256.0f*32.0f);
InMatrixG.Y += 1.0f / (256.0f*256.0f*32.0f);
InMatrixB.Z += 1.0f / (256.0f*256.0f*32.0f);
InMatrixR *= 1.0f / FVector::DotProduct(InMatrixR, FVector(1.0f));
InMatrixG *= 1.0f / FVector::DotProduct(InMatrixG, FVector(1.0f));
InMatrixB *= 1.0f / FVector::DotProduct(InMatrixB, FVector(1.0f));
// Conversion from linear rgb to luma (using HDTV coef).
FVector LumaWeights = FVector(0.2126f, 0.7152f, 0.0722f);
// Make sure white point has 1.0 as luma (so adjusting white point doesn't change exposure).
// Make sure {0.0,0.0,0.0} inputs do something sane (default to white).
InWhitePoint += FVector(1.0f / (256.0f*256.0f*32.0f));
InWhitePoint *= 1.0f / FVector::DotProduct(InWhitePoint, LumaWeights);
InShadowTint += FVector(1.0f / (256.0f*256.0f*32.0f));
InShadowTint *= 1.0f / FVector::DotProduct(InShadowTint, LumaWeights);
// Grey after color matrix is applied.
FVector ColorMatrixLuma = FVector(
FVector::DotProduct(InLuma.X * FVector(InMatrixR.X, InMatrixG.X, InMatrixB.X), FVector(1.0f)),
FVector::DotProduct(InLuma.Y * FVector(InMatrixR.Y, InMatrixG.Y, InMatrixB.Y), FVector(1.0f)),
FVector::DotProduct(InLuma.Z * FVector(InMatrixR.Z, InMatrixG.Z, InMatrixB.Z), FVector(1.0f)));
FVector OutMatrixR = FVector(0.0f);
FVector OutMatrixG = FVector(0.0f);
FVector OutMatrixB = FVector(0.0f);
FVector OutColorShadow_Luma = LumaWeights * InShadowTintBlend;
FVector OutColorShadow_Tint1 = InWhitePoint;
FVector OutColorShadow_Tint2 = InShadowTint - InWhitePoint;
// Final color matrix effected by saturation and exposure.
OutMatrixR = (ColorMatrixLuma + ((InMatrixR - ColorMatrixLuma) * InSaturation)) * InExposure;
OutMatrixG = (ColorMatrixLuma + ((InMatrixG - ColorMatrixLuma) * InSaturation)) * InExposure;
OutMatrixB = (ColorMatrixLuma + ((InMatrixB - ColorMatrixLuma) * InSaturation)) * InExposure;
// Line for linear section.
float FilmLineOffset = 0.18f - 0.18f*InContrast;
float FilmXAtY0 = -FilmLineOffset/InContrast;
float FilmXAtY1 = (1.0f - FilmLineOffset) / InContrast;
float FilmXS = FilmXAtY1 - FilmXAtY0;
// Coordinates of linear section.
float FilmHiX = FilmXAtY0 + InHeal*FilmXS;
float FilmHiY = FilmHiX*InContrast + FilmLineOffset;
float FilmLoX = FilmXAtY0 + InToe*FilmXS;
float FilmLoY = FilmLoX*InContrast + FilmLineOffset;
// Supported exposure range before clipping.
float FilmHeal = InDynamicRange - FilmHiX;
// Intermediates.
float FilmMidXS = FilmHiX - FilmLoX;
float FilmMidYS = FilmHiY - FilmLoY;
float FilmSlopeS = FilmMidYS / (FilmMidXS);
float FilmHiYS = 1.0f - FilmHiY;
float FilmLoYS = FilmLoY;
float FilmToeVal = FilmLoX;
float FilmHiG = (-FilmHiYS + (FilmSlopeS*FilmHeal)) / (FilmSlopeS*FilmHeal);
float FilmLoG = (-FilmLoYS + (FilmSlopeS*FilmToeVal)) / (FilmSlopeS*FilmToeVal);
// Constants.
float OutColorCurveCh1 = FilmHiYS/FilmHiG;
float OutColorCurveCh2 = -FilmHiX*(FilmHiYS/FilmHiG);
float OutColorCurveCh3 = FilmHiYS/(FilmSlopeS*FilmHiG) - FilmHiX;
float OutColorCurveCh0Cm1 = FilmHiX;
float OutColorCurveCm2 = FilmSlopeS;
float OutColorCurveCm0Cd0 = FilmLoX;
float OutColorCurveCd3Cm3 = FilmLoY - FilmLoX*FilmSlopeS;
float OutColorCurveCd1 = 0.0f;
float OutColorCurveCd2 = 1.0f;
// Handle these separate in case of FilmLoG being 0.
if(FilmLoG != 0.0f)
{
OutColorCurveCd1 = -FilmLoYS/FilmLoG;
OutColorCurveCd2 = FilmLoYS/(FilmSlopeS*FilmLoG);
}
else
{
// FilmLoG being zero means dark region is a linear segment (so just continue the middle section).
OutColorCurveCm0Cd0 = 0.0f;
OutColorCurveCd3Cm3 = 0.0f;
}
FVector4 Constants[8];
Constants[0] = FVector4(OutMatrixR, OutColorCurveCd1);
Constants[1] = FVector4(OutMatrixG, OutColorCurveCd3Cm3);
Constants[2] = FVector4(OutMatrixB, OutColorCurveCm2);
Constants[3] = FVector4(OutColorCurveCm0Cd0, OutColorCurveCd2, OutColorCurveCh0Cm1, OutColorCurveCh3);
Constants[4] = FVector4(OutColorCurveCh1, OutColorCurveCh2, 0.0f, 0.0f);
Constants[5] = FVector4(OutColorShadow_Luma, 0.0f);
Constants[6] = FVector4(OutColorShadow_Tint1, 0.0f);
Constants[7] = FVector4(OutColorShadow_Tint2, CVarTonemapperFilm.GetValueOnRenderThread());
SetShaderValue(RHICmdList, ShaderRHI, ColorMatrixR_ColorCurveCd1, Constants[0]);
SetShaderValue(RHICmdList, ShaderRHI, ColorMatrixG_ColorCurveCd3Cm3, Constants[1]);
SetShaderValue(RHICmdList, ShaderRHI, ColorMatrixB_ColorCurveCm2, Constants[2]);
SetShaderValue(RHICmdList, ShaderRHI, ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3, Constants[3]);
SetShaderValue(RHICmdList, ShaderRHI, ColorCurve_Ch1_Ch2, Constants[4]);
SetShaderValue(RHICmdList, ShaderRHI, ColorShadow_Luma, Constants[5]);
SetShaderValue(RHICmdList, ShaderRHI, ColorShadow_Tint1, Constants[6]);
SetShaderValue(RHICmdList, ShaderRHI, ColorShadow_Tint2, Constants[7]);
}
}
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
{
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
OutEnvironment.SetDefine(TEXT("BLENDCOUNT"), BlendCount);
OutEnvironment.SetDefine(TEXT("USE_VOLUME_LUT"), UseVolumeTextureLUT(Platform));
}
virtual bool Serialize(FArchive& Ar) override
{
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
for(uint32 i = 0; i < BlendCount; ++i)
{
Ar << TextureParameter[i];
Ar << TextureParameterSampler[i];
}
Ar << WeightsParameter << ColorScale << OverlayColor;
Ar << ColorRemapShaderParameters;
Ar << InverseGamma;
Ar << WhiteTemp;
Ar << WhiteTint;
Ar << ColorSaturation;
Ar << ColorContrast;
Ar << ColorGamma;
Ar << ColorGain;
Ar << ColorOffset;
Ar << OutputDevice;
Ar << OutputGamut;
Ar << ACESInversion;
Ar << FilmSlope;
Ar << FilmToe;
Ar << FilmShoulder;
Ar << FilmBlackClip;
Ar << FilmWhiteClip;
Ar << ColorMatrixR_ColorCurveCd1 << ColorMatrixG_ColorCurveCd3Cm3 << ColorMatrixB_ColorCurveCm2
<< ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3 << ColorCurve_Ch1_Ch2 << ColorShadow_Luma << ColorShadow_Tint1 << ColorShadow_Tint2;
return bShaderHasOutdatedParameters;
}
private: // ---------------------------------------------------
// [0] is not used as it's the neutral one we do in the shader
FShaderResourceParameter TextureParameter[GMaxLUTBlendCount];
FShaderResourceParameter TextureParameterSampler[GMaxLUTBlendCount];
FShaderParameter WeightsParameter;
FShaderParameter ColorScale;
FShaderParameter OverlayColor;
FShaderParameter InverseGamma;
FColorRemapShaderParameters ColorRemapShaderParameters;
FShaderParameter WhiteTemp;
FShaderParameter WhiteTint;
FShaderParameter ColorSaturation;
FShaderParameter ColorContrast;
FShaderParameter ColorGamma;
FShaderParameter ColorGain;
FShaderParameter ColorOffset;
FShaderParameter FilmSlope;
FShaderParameter FilmToe;
FShaderParameter FilmShoulder;
FShaderParameter FilmBlackClip;
FShaderParameter FilmWhiteClip;
FShaderParameter OutputDevice;
FShaderParameter OutputGamut;
FShaderParameter ACESInversion;
// Legacy
FShaderParameter ColorMatrixR_ColorCurveCd1;
FShaderParameter ColorMatrixG_ColorCurveCd3Cm3;
FShaderParameter ColorMatrixB_ColorCurveCm2;
FShaderParameter ColorCurve_Cm0Cd0_Cd2_Ch0Cm1_Ch3;
FShaderParameter ColorCurve_Ch1_Ch2;
FShaderParameter ColorShadow_Luma;
FShaderParameter ColorShadow_Tint1;
FShaderParameter ColorShadow_Tint2;
};
IMPLEMENT_SHADER_TYPE(template<>,FLUTBlenderPS<1>,TEXT("PostProcessCombineLUTs"),TEXT("MainPS"),SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>,FLUTBlenderPS<2>,TEXT("PostProcessCombineLUTs"),TEXT("MainPS"),SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>,FLUTBlenderPS<3>,TEXT("PostProcessCombineLUTs"),TEXT("MainPS"),SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>,FLUTBlenderPS<4>,TEXT("PostProcessCombineLUTs"),TEXT("MainPS"),SF_Pixel);
IMPLEMENT_SHADER_TYPE(template<>,FLUTBlenderPS<5>,TEXT("PostProcessCombineLUTs"),TEXT("MainPS"),SF_Pixel);
void SetLUTBlenderShader(FRenderingCompositePassContext& Context, uint32 BlendCount, FTexture* Texture[], float Weights[], const FVolumeBounds& VolumeBounds)
{
check(BlendCount > 0);
FShader* LocalPixelShader = 0;
FGlobalBoundShaderState* LocalBoundShaderState = 0;
const FSceneView& View = Context.View;
const auto FeatureLevel = Context.GetFeatureLevel();
auto ShaderMap = Context.GetShaderMap();
// A macro to handle setting the filter shader for a specific number of samples.
#define CASE_COUNT(BlendCount) \
case BlendCount: \
{ \
TShaderMapRef<FLUTBlenderPS<BlendCount> > PixelShader(ShaderMap); \
static FGlobalBoundShaderState BoundShaderState; \
LocalBoundShaderState = &BoundShaderState;\
LocalPixelShader = *PixelShader;\
}; \
break;
switch(BlendCount)
{
// starts at 1 as we always have at least the neutral one
CASE_COUNT(1);
CASE_COUNT(2);
CASE_COUNT(3);
CASE_COUNT(4);
CASE_COUNT(5);
// default:
// UE_LOG(LogRenderer, Fatal,TEXT("Invalid number of samples: %u"),BlendCount);
}
#undef CASE_COUNT
check(LocalBoundShaderState != NULL);
if(UseVolumeTextureLUT(Context.View.GetShaderPlatform()))
{
TShaderMapRef<FWriteToSliceVS> VertexShader(ShaderMap);
TShaderMapRef<FWriteToSliceGS> GeometryShader(ShaderMap);
SetGlobalBoundShaderState(Context.RHICmdList, FeatureLevel, *LocalBoundShaderState, GScreenVertexDeclaration.VertexDeclarationRHI, *VertexShader, LocalPixelShader, *GeometryShader);
VertexShader->SetParameters(Context.RHICmdList, VolumeBounds, VolumeBounds.MaxX - VolumeBounds.MinX);
GeometryShader->SetParameters(Context.RHICmdList, VolumeBounds);
}
else
{
TShaderMapRef<FPostProcessVS> VertexShader(ShaderMap);
SetGlobalBoundShaderState(Context.RHICmdList, FeatureLevel, *LocalBoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, LocalPixelShader);
VertexShader->SetParameters(Context);
}
#define CASE_COUNT(BlendCount) \
case BlendCount: \
{ \
TShaderMapRef<FLUTBlenderPS<BlendCount> > PixelShader(ShaderMap); \
PixelShader->SetParameters(Context.RHICmdList, View, Texture, Weights); \
}; \
break;
switch(BlendCount)
{
// starts at 1 as we always have at least the neutral one
CASE_COUNT(1);
CASE_COUNT(2);
CASE_COUNT(3);
CASE_COUNT(4);
CASE_COUNT(5);
// default:
// UE_LOG(LogRenderer, Fatal,TEXT("Invalid number of samples: %u"),BlendCount);
}
#undef CASE_COUNT
}
uint32 FRCPassPostProcessCombineLUTs::FindIndex(const FFinalPostProcessSettings& Settings, UTexture* Tex) const
{
for(uint32 i = 0; i < (uint32)Settings.ContributingLUTs.Num(); ++i)
{
if(Settings.ContributingLUTs[i].LUTTexture == Tex)
{
return i;
}
}
return 0xffffffff;
}
uint32 FRCPassPostProcessCombineLUTs::GenerateFinalTable(const FFinalPostProcessSettings& Settings, FTexture* OutTextures[], float OutWeights[], uint32 MaxCount) const
{
// find the n strongest contributors, drop small contributors
// (inefficient implementation for many items but count should be small)
uint32 LocalCount = 1;
// add the neutral one (done in the shader) as it should be the first and always there
OutTextures[0] = 0;
{
uint32 NeutralIndex = FindIndex(Settings, 0);
OutWeights[0] = NeutralIndex == 0xffffffff ? 0.0f : Settings.ContributingLUTs[NeutralIndex].Weight;
}
float OutWeightsSum = OutWeights[0];
for(; LocalCount < MaxCount; ++LocalCount)
{
uint32 BestIndex = 0xffffffff;
// find the one with the strongest weight, add until full
for(uint32 i = 0; i < (uint32)Settings.ContributingLUTs.Num(); ++i)
{
bool AlreadyInArray = false;
{
UTexture* LUTTexture = Settings.ContributingLUTs[i].LUTTexture;
FTexture* Internal = LUTTexture ? LUTTexture->Resource : 0;
for(uint32 e = 0; e < LocalCount; ++e)
{
if(Internal == OutTextures[e])
{
AlreadyInArray = true;
break;
}
}
}
if(AlreadyInArray)
{
// we already have this one
continue;
}
if(BestIndex != 0xffffffff
&& Settings.ContributingLUTs[BestIndex].Weight > Settings.ContributingLUTs[i].Weight)
{
// we have a better ones, maybe add next time
continue;
}
BestIndex = i;
}
if(BestIndex == 0xffffffff)
{
// no more elements to process
break;
}
float BestWeight = Settings.ContributingLUTs[BestIndex].Weight;
if(BestWeight < 1.0f / 512.0f)
{
// drop small contributor
break;
}
UTexture* BestLUTTexture = Settings.ContributingLUTs[BestIndex].LUTTexture;
FTexture* BestInternal = BestLUTTexture ? BestLUTTexture->Resource : 0;
OutTextures[LocalCount] = BestInternal;
OutWeights[LocalCount] = BestWeight;
OutWeightsSum += BestWeight;
}
// normalize
if(OutWeightsSum > 0.001f)
{
float InvOutWeightsSum = 1.0f / OutWeightsSum;
for(uint32 i = 0; i < LocalCount; ++i)
{
OutWeights[i] *= InvOutWeightsSum;
}
}
else
{
// neutral only is fully utilized
OutWeights[0] = 1.0f;
LocalCount = 1;
}
return LocalCount;
}
void FRCPassPostProcessCombineLUTs::Process(FRenderingCompositePassContext& Context)
{
SCOPED_DRAW_EVENTF(Context.RHICmdList, PostProcessCombineLUTs, TEXT("PostProcessCombineLUTs %dx%dx%d"), GLUTSize, GLUTSize, GLUTSize);
FTexture* LocalTextures[GMaxLUTBlendCount];
float LocalWeights[GMaxLUTBlendCount];
const FSceneView& View = Context.View;
const FSceneViewFamily& ViewFamily = *(View.Family);
uint32 LocalCount = 1;
// set defaults for no LUT
LocalTextures[0] = 0;
LocalWeights[0] = 1.0f;
if(ViewFamily.EngineShowFlags.ColorGrading)
{
LocalCount = GenerateFinalTable(Context.View.FinalPostProcessSettings, LocalTextures, LocalWeights, GMaxLUTBlendCount);
}
// for a 3D texture, the viewport is 16x16 (per slice), for a 2D texture, it's unwrapped to 256x16
FIntPoint DestSize(UseVolumeTextureLUT(ShaderPlatform) ? GLUTSize : GLUTSize * GLUTSize, GLUTSize);
const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
// Set the view family's render target/viewport.
SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef(), ESimpleRenderTargetMode::EUninitializedColorAndDepth);
Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f );
// set the state
Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
const FVolumeBounds VolumeBounds(GLUTSize);
SetLUTBlenderShader(Context, LocalCount, LocalTextures, LocalWeights, VolumeBounds);
if (UseVolumeTextureLUT(ShaderPlatform))
{
// use volume texture 16x16x16
RasterizeToVolumeTexture(Context.RHICmdList, VolumeBounds);
}
else
{
// use unwrapped 2d texture 256x16
TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap());
DrawRectangle(
Context.RHICmdList,
0, 0, // XY
GLUTSize * GLUTSize, GLUTSize, // SizeXY
0, 0, // UV
GLUTSize * GLUTSize, GLUTSize, // SizeUV
FIntPoint(GLUTSize * GLUTSize, GLUTSize), // TargetSize
FIntPoint(GLUTSize * GLUTSize, GLUTSize), // TextureSize
*VertexShader,
EDRF_UseTriangleOptimization);
}
Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
}
FPooledRenderTargetDesc FRCPassPostProcessCombineLUTs::ComputeOutputDesc(EPassOutputId InPassOutputId) const
{
EPixelFormat LUTPixelFormat = PF_A2B10G10R10;
if (!GPixelFormats[LUTPixelFormat].Supported)
{
LUTPixelFormat = PF_R8G8B8A8;
}
FPooledRenderTargetDesc Ret = FPooledRenderTargetDesc::Create2DDesc(FIntPoint(GLUTSize * GLUTSize, GLUTSize), LUTPixelFormat, FClearValueBinding::None, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource, false);
if(UseVolumeTextureLUT(ShaderPlatform))
{
Ret.Extent = FIntPoint(GLUTSize, GLUTSize);
Ret.Depth = GLUTSize;
}
Ret.DebugName = TEXT("CombineLUTs");
return Ret;
}