You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
708 lines
26 KiB
C++
708 lines
26 KiB
C++
// Copyright 1998-2015 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
|
|
static 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::SM4); }
|
|
|
|
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") );
|
|
|
|
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)
|
|
{
|
|
SetTextureParameter(RHICmdList, ShaderRHI, TextureParameter[i], TextureParameterSampler[i], Texture[i]);
|
|
}
|
|
|
|
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 );
|
|
|
|
{
|
|
FVector InvDisplayGammaValue;
|
|
InvDisplayGammaValue.X = 1.0f / ViewFamily.RenderTarget->GetDisplayGamma();
|
|
InvDisplayGammaValue.Y = 2.2f / ViewFamily.RenderTarget->GetDisplayGamma();
|
|
{
|
|
static TConsoleVariableData<float>* CVar = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.TonemapperGamma"));
|
|
float Value = CVar->GetValueOnRenderThread();
|
|
if(Value < 1.0f)
|
|
{
|
|
Value = 1.0f;
|
|
}
|
|
InvDisplayGammaValue.Z = 1.0f / Value;
|
|
}
|
|
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 FilmToeS = FilmLoX;
|
|
float FilmHiG = (-FilmHiYS + (FilmSlopeS*FilmHeal)) / (FilmSlopeS*FilmHeal);
|
|
float FilmLoG = (-FilmLoYS + (FilmSlopeS*FilmToeS)) / (FilmSlopeS*FilmToeS);
|
|
|
|
// 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));
|
|
//OutEnvironment.SetDefine(TEXT("USE_GAMMA"), OutputDevice == 1);
|
|
//OutEnvironment.SetDefine(TEXT("USE_709"), OutputDevice == 2);
|
|
}
|
|
|
|
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 << 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;
|
|
|
|
// 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_EVENT(Context.RHICmdList, PostProcessCombineLUTs);
|
|
|
|
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());
|
|
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
|
|
{
|
|
FPooledRenderTargetDesc Ret = FPooledRenderTargetDesc::Create2DDesc(FIntPoint(GLUTSize * GLUTSize, GLUTSize), PF_A2B10G10R10, TexCreate_None, TexCreate_RenderTargetable | TexCreate_ShaderResource, false);
|
|
|
|
if(UseVolumeTextureLUT(ShaderPlatform))
|
|
{
|
|
Ret.Extent = FIntPoint(GLUTSize, GLUTSize);
|
|
Ret.Depth = GLUTSize;
|
|
}
|
|
|
|
Ret.DebugName = TEXT("CombineLUTs");
|
|
|
|
return Ret;
|
|
} |