2014-12-07 19:09:38 -05:00
|
|
|
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/*=============================================================================
|
|
|
|
|
PostProcessWeightedSampleSum.cpp: Post processing weight sample sum implementation.
|
|
|
|
|
=============================================================================*/
|
|
|
|
|
|
|
|
|
|
#include "RendererPrivate.h"
|
|
|
|
|
#include "ScenePrivate.h"
|
|
|
|
|
#include "SceneFilterRendering.h"
|
|
|
|
|
#include "PostProcessWeightedSampleSum.h"
|
2014-08-21 06:03:00 -04:00
|
|
|
#include "SceneUtils.h"
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A pixel shader which filters a texture.
|
|
|
|
|
* @param CombineMethod 0:weighted filtering, 1: weighted filtering + additional texture, 2: max magnitude
|
|
|
|
|
*/
|
|
|
|
|
template<uint32 NumSamples, uint32 CombineMethodInt>
|
|
|
|
|
class TFilterPS : public FGlobalShader
|
|
|
|
|
{
|
|
|
|
|
DECLARE_SHADER_TYPE(TFilterPS,Global);
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
|
|
static bool ShouldCache(EShaderPlatform Platform)
|
|
|
|
|
{
|
|
|
|
|
if( IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM5) )
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-08-25 14:41:54 -04:00
|
|
|
else if( IsFeatureLevelSupported(Platform, ERHIFeatureLevel::SM4) )
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
return NumSamples <= 16;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return NumSamples <= 7;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
|
|
|
|
|
{
|
|
|
|
|
FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
|
|
|
|
|
OutEnvironment.SetDefine(TEXT("NUM_SAMPLES"), NumSamples);
|
|
|
|
|
OutEnvironment.SetDefine(TEXT("COMBINE_METHOD"), CombineMethodInt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Default constructor. */
|
|
|
|
|
TFilterPS() {}
|
|
|
|
|
|
|
|
|
|
/** Initialization constructor. */
|
|
|
|
|
TFilterPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
|
|
|
|
|
: FGlobalShader(Initializer)
|
|
|
|
|
{
|
|
|
|
|
FilterTexture.Bind(Initializer.ParameterMap,TEXT("FilterTexture"));
|
|
|
|
|
FilterTextureSampler.Bind(Initializer.ParameterMap,TEXT("FilterTextureSampler"));
|
|
|
|
|
AdditiveTexture.Bind(Initializer.ParameterMap,TEXT("AdditiveTexture"));
|
|
|
|
|
AdditiveTextureSampler.Bind(Initializer.ParameterMap,TEXT("AdditiveTextureSampler"));
|
|
|
|
|
SampleWeights.Bind(Initializer.ParameterMap,TEXT("SampleWeights"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Serializer */
|
2015-04-01 07:20:55 -04:00
|
|
|
virtual bool Serialize(FArchive& Ar) override
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
|
|
|
|
|
Ar << FilterTexture << FilterTextureSampler << AdditiveTexture << AdditiveTextureSampler << SampleWeights;
|
|
|
|
|
return bShaderHasOutdatedParameters;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Sets shader parameter values */
|
2014-06-10 07:29:49 -04:00
|
|
|
void SetParameters(FRHICommandList& RHICmdList, FSamplerStateRHIParamRef SamplerStateRHI, FTextureRHIParamRef FilterTextureRHI, FTextureRHIParamRef AdditiveTextureRHI, const FLinearColor* SampleWeightValues)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
const FPixelShaderRHIParamRef ShaderRHI = GetPixelShader();
|
|
|
|
|
|
2014-06-05 16:38:54 -04:00
|
|
|
SetTextureParameter(RHICmdList, ShaderRHI, FilterTexture, FilterTextureSampler, SamplerStateRHI, FilterTextureRHI);
|
|
|
|
|
SetTextureParameter(RHICmdList, ShaderRHI, AdditiveTexture, AdditiveTextureSampler, SamplerStateRHI, AdditiveTextureRHI);
|
|
|
|
|
SetShaderValueArray(RHICmdList, ShaderRHI, SampleWeights, SampleWeightValues, NumSamples);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const TCHAR* GetSourceFilename()
|
|
|
|
|
{
|
|
|
|
|
return TEXT("FilterPixelShader");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const TCHAR* GetFunctionName()
|
|
|
|
|
{
|
|
|
|
|
return TEXT("Main");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
FShaderResourceParameter FilterTexture;
|
|
|
|
|
FShaderResourceParameter FilterTextureSampler;
|
|
|
|
|
FShaderResourceParameter AdditiveTexture;
|
|
|
|
|
FShaderResourceParameter AdditiveTextureSampler;
|
|
|
|
|
FShaderParameter SampleWeights;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// #define avoids a lot of code duplication
|
|
|
|
|
#define VARIATION1(A) VARIATION2(A,0) VARIATION2(A,1) VARIATION2(A,2)
|
|
|
|
|
#define VARIATION2(A, B) typedef TFilterPS<A, B> TFilterPS##A##B; \
|
|
|
|
|
IMPLEMENT_SHADER_TYPE2(TFilterPS##A##B, SF_Pixel);
|
|
|
|
|
VARIATION1(1) VARIATION1(2) VARIATION1(3) VARIATION1(4) VARIATION1(5) VARIATION1(6) VARIATION1(7) VARIATION1(8)
|
|
|
|
|
VARIATION1(9) VARIATION1(10) VARIATION1(11) VARIATION1(12) VARIATION1(13) VARIATION1(14) VARIATION1(15) VARIATION1(16)
|
|
|
|
|
VARIATION1(17) VARIATION1(18) VARIATION1(19) VARIATION1(20) VARIATION1(21) VARIATION1(22) VARIATION1(23) VARIATION1(24)
|
|
|
|
|
VARIATION1(25) VARIATION1(26) VARIATION1(27) VARIATION1(28) VARIATION1(29) VARIATION1(30) VARIATION1(31) VARIATION1(32)
|
|
|
|
|
#undef VARIATION1
|
|
|
|
|
#undef VARIATION2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** A macro to declaring a filter shader type for a specific number of samples. */
|
|
|
|
|
#define IMPLEMENT_FILTER_SHADER_TYPE(NumSamples) \
|
|
|
|
|
IMPLEMENT_SHADER_TYPE(template<>,TFilterVS<NumSamples>,TEXT("FilterVertexShader"),TEXT("Main"),SF_Vertex);
|
|
|
|
|
/** The filter shader types for 1-MAX_FILTER_SAMPLES samples. */
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(1);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(2);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(3);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(4);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(5);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(6);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(7);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(8);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(9);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(10);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(11);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(12);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(13);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(14);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(15);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(16);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(17);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(18);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(19);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(20);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(21);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(22);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(23);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(24);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(25);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(26);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(27);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(28);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(29);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(30);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(31);
|
|
|
|
|
IMPLEMENT_FILTER_SHADER_TYPE(32);
|
|
|
|
|
#undef IMPLEMENT_FILTER_SHADER_TYPE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Sets the filter shaders with the provided filter samples.
|
|
|
|
|
* @param SamplerStateRHI - The sampler state to use for the source texture.
|
|
|
|
|
* @param FilterTextureRHI - The source texture.
|
|
|
|
|
* @param AdditiveTextureRHI - The additional source texture, used in CombineMethod=1
|
|
|
|
|
* @param CombineMethodInt 0:weighted filtering, 1: weighted filtering + additional texture, 2: max magnitude
|
|
|
|
|
* @param SampleOffsets - A pointer to an array of NumSamples UV offsets
|
|
|
|
|
* @param SampleWeights - A pointer to an array of NumSamples 4-vector weights
|
|
|
|
|
* @param NumSamples - The number of samples used by the filter.
|
2014-04-23 17:26:59 -04:00
|
|
|
* @param OutVertexShader - The vertex shader used for the filter
|
2014-03-14 14:13:41 -04:00
|
|
|
*/
|
|
|
|
|
void SetFilterShaders(
|
2014-06-27 11:07:13 -04:00
|
|
|
FRHICommandListImmediate& RHICmdList,
|
2014-08-19 10:41:34 -04:00
|
|
|
ERHIFeatureLevel::Type FeatureLevel,
|
2014-03-14 14:13:41 -04:00
|
|
|
FSamplerStateRHIParamRef SamplerStateRHI,
|
|
|
|
|
FTextureRHIParamRef FilterTextureRHI,
|
|
|
|
|
FTextureRHIParamRef AdditiveTextureRHI,
|
|
|
|
|
uint32 CombineMethodInt,
|
|
|
|
|
FVector2D* SampleOffsets,
|
|
|
|
|
FLinearColor* SampleWeights,
|
2014-04-23 17:26:59 -04:00
|
|
|
uint32 NumSamples,
|
|
|
|
|
FShader** OutVertexShader
|
2014-03-14 14:13:41 -04:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
check(CombineMethodInt <= 2);
|
2014-08-28 06:22:54 -04:00
|
|
|
auto ShaderMap = GetGlobalShaderMap(FeatureLevel);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// A macro to handle setting the filter shader for a specific number of samples.
|
|
|
|
|
#define SET_FILTER_SHADER_TYPE(NumSamples) \
|
|
|
|
|
case NumSamples: \
|
|
|
|
|
{ \
|
2014-08-28 06:22:54 -04:00
|
|
|
TShaderMapRef<TFilterVS<NumSamples> > VertexShader(ShaderMap); \
|
2014-04-23 17:26:59 -04:00
|
|
|
*OutVertexShader = *VertexShader; \
|
2014-03-14 14:13:41 -04:00
|
|
|
if(CombineMethodInt == 0) \
|
|
|
|
|
{ \
|
2014-08-28 06:22:54 -04:00
|
|
|
TShaderMapRef<TFilterPS<NumSamples, 0> > PixelShader(ShaderMap); \
|
2014-03-14 14:13:41 -04:00
|
|
|
{ \
|
|
|
|
|
static FGlobalBoundShaderState BoundShaderState; \
|
2014-08-19 10:41:34 -04:00
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
2014-06-05 16:38:54 -04:00
|
|
|
PixelShader->SetParameters(RHICmdList, SamplerStateRHI, FilterTextureRHI, AdditiveTextureRHI, SampleWeights); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
|
|
|
|
else if(CombineMethodInt == 1) \
|
|
|
|
|
{ \
|
2014-08-28 06:22:54 -04:00
|
|
|
TShaderMapRef<TFilterPS<NumSamples, 1> > PixelShader(ShaderMap); \
|
2014-03-14 14:13:41 -04:00
|
|
|
{ \
|
|
|
|
|
static FGlobalBoundShaderState BoundShaderState; \
|
2014-08-19 10:41:34 -04:00
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
2014-06-05 16:38:54 -04:00
|
|
|
PixelShader->SetParameters(RHICmdList, SamplerStateRHI, FilterTextureRHI, AdditiveTextureRHI, SampleWeights); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
|
|
|
|
else\
|
|
|
|
|
{ \
|
2014-08-28 06:22:54 -04:00
|
|
|
TShaderMapRef<TFilterPS<NumSamples, 2> > PixelShader(ShaderMap); \
|
2014-03-14 14:13:41 -04:00
|
|
|
{ \
|
|
|
|
|
static FGlobalBoundShaderState BoundShaderState; \
|
2014-08-19 10:41:34 -04:00
|
|
|
SetGlobalBoundShaderState(RHICmdList, FeatureLevel, BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
2014-06-05 16:38:54 -04:00
|
|
|
PixelShader->SetParameters(RHICmdList, SamplerStateRHI, FilterTextureRHI, AdditiveTextureRHI, SampleWeights); \
|
2014-03-14 14:13:41 -04:00
|
|
|
} \
|
2014-06-05 16:38:54 -04:00
|
|
|
VertexShader->SetParameters(RHICmdList, SampleOffsets); \
|
2014-03-14 14:13:41 -04:00
|
|
|
break; \
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Set the appropriate filter shader for the given number of samples.
|
|
|
|
|
switch(NumSamples)
|
|
|
|
|
{
|
|
|
|
|
SET_FILTER_SHADER_TYPE(1);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(2);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(3);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(4);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(5);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(6);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(7);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(8);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(9);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(10);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(11);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(12);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(13);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(14);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(15);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(16);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(17);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(18);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(19);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(20);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(21);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(22);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(23);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(24);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(25);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(26);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(27);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(28);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(29);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(30);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(31);
|
|
|
|
|
SET_FILTER_SHADER_TYPE(32);
|
|
|
|
|
default:
|
|
|
|
|
UE_LOG(LogRenderer, Fatal,TEXT("Invalid number of samples: %u"),NumSamples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef SET_FILTER_SHADER_TYPE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Evaluates a normal distribution PDF at given X.
|
|
|
|
|
* This function misses the math for scaling the result (faster, not needed if the resulting values are renormalized).
|
|
|
|
|
* @param X - The X to evaluate the PDF at.
|
|
|
|
|
* @param Mean - The normal distribution's mean.
|
|
|
|
|
* @param Variance - The normal distribution's variance.
|
|
|
|
|
* @return The value of the normal distribution at X. (unscaled)
|
|
|
|
|
*/
|
2015-01-12 20:35:04 -05:00
|
|
|
static float NormalDistributionUnscaled(float X,float Mean,float Variance, EFilterShape FilterShape, float CrossCenterWeight)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2015-01-26 17:53:20 -05:00
|
|
|
float dx = FMath::Abs(X - Mean);
|
2015-01-12 20:35:04 -05:00
|
|
|
|
2015-01-26 17:53:20 -05:00
|
|
|
float Ret = FMath::Exp(-FMath::Square(dx) / (2.0f * Variance));
|
|
|
|
|
|
|
|
|
|
// tweak the gaussian shape e.g. "r.Bloom.Cross 3.5"
|
|
|
|
|
if(CrossCenterWeight > 1.0f)
|
2015-01-12 20:35:04 -05:00
|
|
|
{
|
2015-01-26 17:53:20 -05:00
|
|
|
Ret = FMath::Max(0.0f, 1.0f - dx / Variance);
|
|
|
|
|
Ret = FMath::Pow(Ret, CrossCenterWeight);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Ret = FMath::Lerp(Ret, FMath::Max(0.0f, 1.0f - dx / Variance), CrossCenterWeight);
|
2015-01-12 20:35:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Ret;
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return NumSamples >0
|
|
|
|
|
*/
|
|
|
|
|
|
2015-01-12 20:35:04 -05:00
|
|
|
static uint32 Compute1DGaussianFilterKernel(ERHIFeatureLevel::Type InFeatureLevel, float KernelRadius, FVector2D OutOffsetAndWeight[MAX_FILTER_SAMPLES], uint32 MaxFilterSamples, EFilterShape FilterShape, float CrossCenterWeight)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-05-08 09:05:50 -04:00
|
|
|
float ClampedKernelRadius = FRCPassPostProcessWeightedSampleSum::GetClampedKernelRadius( InFeatureLevel, KernelRadius );
|
|
|
|
|
int32 IntegerKernelRadius = FRCPassPostProcessWeightedSampleSum::GetIntegerKernelRadius( InFeatureLevel, KernelRadius );
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
// smallest IntegerKernelRadius will be 1
|
|
|
|
|
|
|
|
|
|
uint32 NumSamples = 0;
|
|
|
|
|
float WeightSum = 0.0f;
|
|
|
|
|
for(int32 SampleIndex = -IntegerKernelRadius; SampleIndex <= IntegerKernelRadius; SampleIndex += 2)
|
|
|
|
|
{
|
2015-01-12 20:35:04 -05:00
|
|
|
float Weight0 = NormalDistributionUnscaled(SampleIndex, 0, ClampedKernelRadius, FilterShape, CrossCenterWeight);
|
2014-03-14 14:13:41 -04:00
|
|
|
float Weight1 = 0.0f;
|
|
|
|
|
|
|
|
|
|
// Because we use bilinear filtering we only require half the sample count.
|
|
|
|
|
// But we need to fix the last weight.
|
|
|
|
|
// Example (radius one texel, c is center, a left, b right):
|
|
|
|
|
// a b c (a is left texel, b center and c right) becomes two lookups one with a*.. + b **, the other with
|
|
|
|
|
// c * .. but another texel to the right would accidentially leak into this computation.
|
|
|
|
|
if(SampleIndex != IntegerKernelRadius)
|
|
|
|
|
{
|
2015-01-12 20:35:04 -05:00
|
|
|
Weight1 = NormalDistributionUnscaled(SampleIndex + 1, 0, ClampedKernelRadius, FilterShape, CrossCenterWeight);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float TotalWeight = Weight0 + Weight1;
|
|
|
|
|
OutOffsetAndWeight[NumSamples].X = (SampleIndex + Weight1 / TotalWeight);
|
|
|
|
|
OutOffsetAndWeight[NumSamples].Y = TotalWeight;
|
|
|
|
|
WeightSum += TotalWeight;
|
|
|
|
|
NumSamples++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize blur weights.
|
|
|
|
|
float InvWeightSum = 1.0f / WeightSum;
|
|
|
|
|
for(uint32 SampleIndex = 0;SampleIndex < NumSamples; ++SampleIndex)
|
|
|
|
|
{
|
|
|
|
|
OutOffsetAndWeight[SampleIndex].Y *= InvWeightSum;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NumSamples;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-10 12:31:36 -04:00
|
|
|
FRCPassPostProcessWeightedSampleSum::FRCPassPostProcessWeightedSampleSum(EFilterShape InFilterShape, EFilterCombineMethod InCombineMethod, float InSizeScale, const TCHAR* InDebugName, FLinearColor InTintValue)
|
2014-03-14 14:13:41 -04:00
|
|
|
: FilterShape(InFilterShape)
|
|
|
|
|
, CombineMethod(InCombineMethod)
|
|
|
|
|
, SizeScale(InSizeScale)
|
|
|
|
|
, TintValue(InTintValue)
|
|
|
|
|
, DebugName(InDebugName)
|
2015-01-12 20:35:04 -05:00
|
|
|
, CrossCenterWeight(0.0f)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FRCPassPostProcessWeightedSampleSum::Process(FRenderingCompositePassContext& Context)
|
|
|
|
|
{
|
|
|
|
|
const FSceneView& View = Context.View;
|
|
|
|
|
|
|
|
|
|
FRenderingCompositeOutput *Input = PassInputs[0].GetOutput();
|
|
|
|
|
|
|
|
|
|
// input is not hooked up correctly
|
|
|
|
|
check(Input);
|
|
|
|
|
|
|
|
|
|
const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
|
|
|
|
|
|
|
|
|
|
// input is not hooked up correctly
|
|
|
|
|
check(InputDesc);
|
|
|
|
|
|
|
|
|
|
const FSceneViewFamily& ViewFamily = *(View.Family);
|
|
|
|
|
|
|
|
|
|
FIntPoint SrcSize = InputDesc->Extent;
|
|
|
|
|
FIntPoint DestSize = PassOutputs[0].RenderTargetDesc.Extent;
|
|
|
|
|
|
|
|
|
|
// e.g. 4 means the input texture is 4x smaller than the buffer size
|
|
|
|
|
FIntPoint SrcScaleFactor = GSceneRenderTargets.GetBufferSizeXY() / SrcSize;
|
|
|
|
|
FIntPoint DstScaleFactor = GSceneRenderTargets.GetBufferSizeXY() / DestSize;
|
|
|
|
|
|
|
|
|
|
TRefCountPtr<IPooledRenderTarget> InputPooledElement = Input->RequestInput();
|
|
|
|
|
|
|
|
|
|
check(!InputPooledElement->IsFree());
|
|
|
|
|
|
|
|
|
|
const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context);
|
|
|
|
|
|
|
|
|
|
FVector2D InvSrcSize(1.0f / SrcSize.X, 1.0f / SrcSize.Y);
|
|
|
|
|
// we scale by width because FOV is defined horizontally
|
2015-01-26 17:53:20 -05:00
|
|
|
float SrcSizeForThisAxis = View.ViewRect.Width() / (float)SrcScaleFactor.X;
|
|
|
|
|
|
|
|
|
|
// in texel (input resolution), /2 as we use the diameter, 100 as we use percent
|
|
|
|
|
float EffectiveBlurRadius = SizeScale * SrcSizeForThisAxis / 2 / 100.0f;
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
FVector2D BlurOffsets[MAX_FILTER_SAMPLES];
|
|
|
|
|
FLinearColor BlurWeights[MAX_FILTER_SAMPLES];
|
|
|
|
|
FVector2D OffsetAndWeight[MAX_FILTER_SAMPLES];
|
|
|
|
|
|
2014-05-08 09:05:50 -04:00
|
|
|
const auto FeatureLevel = Context.View.GetFeatureLevel();
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-05-08 09:05:50 -04:00
|
|
|
// compute 1D filtered samples
|
|
|
|
|
uint32 MaxNumSamples = GetMaxNumSamples(FeatureLevel);
|
|
|
|
|
|
2015-01-12 20:35:04 -05:00
|
|
|
uint32 NumSamples = Compute1DGaussianFilterKernel(FeatureLevel, EffectiveBlurRadius, OffsetAndWeight, MaxNumSamples, FilterShape, CrossCenterWeight);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-10-20 10:43:43 -04:00
|
|
|
SCOPED_DRAW_EVENTF(Context.RHICmdList, PostProcessWeightedSampleSum, TEXT("PostProcessWeightedSampleSum#%d"), NumSamples);
|
2014-08-25 14:31:48 -04:00
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
// compute weights as weighted contributions of the TintValue
|
|
|
|
|
for(uint32 i = 0; i < NumSamples; ++i)
|
|
|
|
|
{
|
|
|
|
|
BlurWeights[i] = TintValue * OffsetAndWeight[i].Y;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-12 07:13:34 -04:00
|
|
|
SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef());
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
Context.SetViewportAndCallRHI(0, 0, 0.0f, DestSize.X, DestSize.Y, 1.0f);
|
|
|
|
|
|
|
|
|
|
// set the state
|
2014-06-12 07:13:34 -04:00
|
|
|
Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
|
|
|
|
|
Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI());
|
|
|
|
|
Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI());
|
2014-03-14 14:13:41 -04:00
|
|
|
|
|
|
|
|
const FTextureRHIRef& FilterTexture = InputPooledElement->GetRenderTargetItem().ShaderResourceTexture;
|
|
|
|
|
|
|
|
|
|
FRenderingCompositeOutput *Input1 = PassInputs[1].GetOutput();
|
|
|
|
|
|
|
|
|
|
uint32 CombineMethodInt = 0;
|
|
|
|
|
|
|
|
|
|
if(CombineMethod == EFCM_MaxMagnitude)
|
|
|
|
|
{
|
|
|
|
|
CombineMethodInt = 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// can be optimized
|
|
|
|
|
FTextureRHIRef AdditiveTexture;
|
|
|
|
|
|
|
|
|
|
if(Input1)
|
|
|
|
|
{
|
|
|
|
|
TRefCountPtr<IPooledRenderTarget> InputPooledElement1 = Input1->RequestInput();
|
|
|
|
|
AdditiveTexture = InputPooledElement1->GetRenderTargetItem().ShaderResourceTexture;
|
|
|
|
|
|
|
|
|
|
check(CombineMethod == EFCM_Weighted);
|
|
|
|
|
|
|
|
|
|
CombineMethodInt = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bDoFastBlur = DoFastBlur();
|
|
|
|
|
|
|
|
|
|
if (FilterShape == EFS_Horiz)
|
|
|
|
|
{
|
|
|
|
|
float YOffset = bDoFastBlur ? (InvSrcSize.Y * 0.5f) : 0.0f;
|
|
|
|
|
for (uint32 i = 0; i < NumSamples; ++i)
|
|
|
|
|
{
|
|
|
|
|
BlurOffsets[i] = FVector2D(InvSrcSize.X * OffsetAndWeight[i].X, YOffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
float YOffset = bDoFastBlur ? -(InvSrcSize.Y * 0.5f) : 0.0f;
|
|
|
|
|
for (uint32 i = 0; i < NumSamples; ++i)
|
|
|
|
|
{
|
|
|
|
|
BlurOffsets[i] = FVector2D(0, InvSrcSize.Y * OffsetAndWeight[i].X + YOffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-23 17:26:59 -04:00
|
|
|
FShader* VertexShader = nullptr;
|
2014-03-14 14:13:41 -04:00
|
|
|
SetFilterShaders(
|
2014-06-12 07:13:34 -04:00
|
|
|
Context.RHICmdList,
|
2014-08-19 10:41:34 -04:00
|
|
|
FeatureLevel,
|
2014-03-14 14:13:41 -04:00
|
|
|
TStaticSamplerState<SF_Bilinear,AM_Border,AM_Border,AM_Clamp>::GetRHI(),
|
|
|
|
|
FilterTexture,
|
|
|
|
|
AdditiveTexture,
|
|
|
|
|
CombineMethodInt,
|
|
|
|
|
BlurOffsets,
|
|
|
|
|
BlurWeights,
|
2014-04-23 17:26:59 -04:00
|
|
|
NumSamples,
|
|
|
|
|
&VertexShader
|
2014-03-14 14:13:41 -04:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
bool bRequiresClear = true;
|
|
|
|
|
// check if we have to clear the whole surface.
|
|
|
|
|
// Otherwise perform the clear when the dest rectangle has been computed.
|
2014-10-09 17:50:34 -04:00
|
|
|
if (FeatureLevel == ERHIFeatureLevel::ES2 || FeatureLevel == ERHIFeatureLevel::ES3_1)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-06-12 07:13:34 -04:00
|
|
|
Context.RHICmdList.Clear(true, FLinearColor(0, 0, 0, 0), false, 1.0f, false, 0, FIntRect());
|
2014-03-14 14:13:41 -04:00
|
|
|
bRequiresClear = false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-26 18:55:05 -05:00
|
|
|
FIntRect SrcRect = FIntRect::DivideAndRoundUp(View.ViewRect, SrcScaleFactor);
|
|
|
|
|
FIntRect DestRect = FIntRect::DivideAndRoundUp(View.ViewRect, DstScaleFactor);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-09-10 12:31:36 -04:00
|
|
|
DrawQuad(Context.RHICmdList, bDoFastBlur, SrcRect, DestRect, bRequiresClear, DestSize, SrcSize, VertexShader);
|
2014-03-14 14:13:41 -04:00
|
|
|
|
2014-06-12 07:13:34 -04:00
|
|
|
Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams());
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FPooledRenderTargetDesc FRCPassPostProcessWeightedSampleSum::ComputeOutputDesc(EPassOutputId InPassOutputId) const
|
|
|
|
|
{
|
|
|
|
|
FPooledRenderTargetDesc Ret = PassInputs[0].GetOutput()->RenderTargetDesc;
|
|
|
|
|
|
|
|
|
|
if(DoFastBlur())
|
|
|
|
|
{
|
|
|
|
|
if(FilterShape == EFS_Horiz)
|
|
|
|
|
{
|
|
|
|
|
Ret.Extent.X = FMath::DivideAndRoundUp(Ret.Extent.X, 2);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// not perfect - we might get a RT one texel larger
|
|
|
|
|
Ret.Extent.X *= 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ret.Reset();
|
|
|
|
|
Ret.DebugName = DebugName;
|
|
|
|
|
|
|
|
|
|
return Ret;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-25 05:47:33 -04:00
|
|
|
|
|
|
|
|
static TAutoConsoleVariable<float> CVarFastBlurThreshold(
|
|
|
|
|
TEXT("r.FastBlurThreshold"),
|
|
|
|
|
7.0f,
|
|
|
|
|
TEXT("Defines at what radius the Gaussian blur optimization kicks in (estimated 25% - 40% faster).\n")
|
|
|
|
|
TEXT("The optimzation uses slightly less memory and has a quality loss on smallblur radius.\n")
|
|
|
|
|
TEXT(" 0: use the optimization always (fastest, lowest quality)\n")
|
|
|
|
|
TEXT(" 3: use the optimization starting at a 3 pixel radius (quite fast)\n")
|
|
|
|
|
TEXT(" 7: use the optimization starting at a 7 pixel radius (default)\n")
|
|
|
|
|
TEXT(">15: barely ever use the optimization (high quality)"),
|
|
|
|
|
ECVF_Scalability | ECVF_RenderThreadSafe);
|
|
|
|
|
|
2014-03-14 14:13:41 -04:00
|
|
|
bool FRCPassPostProcessWeightedSampleSum::DoFastBlur() const
|
|
|
|
|
{
|
|
|
|
|
bool bRet = false;
|
|
|
|
|
|
|
|
|
|
// only do the fast blur only with bilinear filtering
|
|
|
|
|
if(CombineMethod == EFCM_Weighted)
|
|
|
|
|
{
|
|
|
|
|
const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0);
|
|
|
|
|
|
|
|
|
|
// input is not hooked up correctly
|
|
|
|
|
check(InputDesc);
|
|
|
|
|
|
|
|
|
|
if(FilterShape == EFS_Horiz)
|
|
|
|
|
{
|
|
|
|
|
FIntPoint SrcSize = InputDesc->Extent;
|
|
|
|
|
|
|
|
|
|
int32 SrcSizeForThisAxis = SrcSize.X;
|
|
|
|
|
|
|
|
|
|
// in texel (input resolution), *2 as we use the diameter
|
|
|
|
|
// we scale by width because FOV is defined horizontally
|
|
|
|
|
float EffectiveBlurRadius = SizeScale * SrcSizeForThisAxis * 2 / 100.0f;
|
|
|
|
|
|
|
|
|
|
#if PLATFORM_HTML5
|
2014-06-25 05:47:33 -04:00
|
|
|
float FastBlurThreshold = CVarFastBlurThreshold.GetValueOnGameThread();
|
2014-03-14 14:13:41 -04:00
|
|
|
#else
|
2014-06-25 05:47:33 -04:00
|
|
|
float FastBlurThreshold = CVarFastBlurThreshold.GetValueOnRenderThread();
|
2014-03-14 14:13:41 -04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// small radius look too different with this optimization so we only to it for larger radius
|
|
|
|
|
bRet = EffectiveBlurRadius >= FastBlurThreshold;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FIntPoint SrcSize = InputDesc->Extent;
|
|
|
|
|
FIntPoint BufferSize = GSceneRenderTargets.GetBufferSizeXY();
|
|
|
|
|
|
|
|
|
|
float InputRatio = SrcSize.X / (float)SrcSize.Y;
|
|
|
|
|
float BufferRatio = BufferSize.X / (float)BufferSize.Y;
|
|
|
|
|
|
|
|
|
|
// Half res input detected
|
|
|
|
|
bRet = InputRatio < BufferRatio * 0.75f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-27 11:07:13 -04:00
|
|
|
void FRCPassPostProcessWeightedSampleSum::DrawQuad(FRHICommandListImmediate& RHICmdList, bool bDoFastBlur, FIntRect SrcRect, FIntRect DestRect, bool bRequiresClear, FIntPoint DestSize, FIntPoint SrcSize, FShader* VertexShader) const
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
if (bDoFastBlur)
|
|
|
|
|
{
|
|
|
|
|
if (FilterShape == EFS_Horiz)
|
|
|
|
|
{
|
|
|
|
|
SrcRect.Min.X = DestRect.Min.X * 2;
|
|
|
|
|
SrcRect.Max.X = DestRect.Max.X * 2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DestRect.Min.X = SrcRect.Min.X * 2;
|
|
|
|
|
DestRect.Max.X = SrcRect.Max.X * 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (bRequiresClear)
|
|
|
|
|
{
|
2014-06-12 07:13:34 -04:00
|
|
|
RHICmdList.Clear(true, FLinearColor(0, 0, 0, 0), false, 1.0f, false, 0, DestRect);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw a quad mapping scene color to the view's render target
|
|
|
|
|
DrawRectangle(
|
2014-06-12 07:13:34 -04:00
|
|
|
RHICmdList,
|
2014-03-14 14:13:41 -04:00
|
|
|
DestRect.Min.X, DestRect.Min.Y,
|
|
|
|
|
DestRect.Width(), DestRect.Height(),
|
|
|
|
|
SrcRect.Min.X, SrcRect.Min.Y,
|
|
|
|
|
SrcRect.Width(), SrcRect.Height(),
|
|
|
|
|
DestSize,
|
|
|
|
|
SrcSize,
|
2014-04-23 17:26:59 -04:00
|
|
|
VertexShader,
|
2014-03-14 14:13:41 -04:00
|
|
|
EDRF_UseTriangleOptimization);
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 09:05:50 -04:00
|
|
|
uint32 FRCPassPostProcessWeightedSampleSum::GetMaxNumSamples(ERHIFeatureLevel::Type InFeatureLevel)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
uint32 MaxNumSamples;
|
2014-05-08 09:05:50 -04:00
|
|
|
if (InFeatureLevel >= ERHIFeatureLevel::SM5)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
MaxNumSamples = MAX_FILTER_SAMPLES;
|
|
|
|
|
}
|
2014-08-25 14:41:54 -04:00
|
|
|
else if (InFeatureLevel >= ERHIFeatureLevel::SM4)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
|
|
|
|
MaxNumSamples = 16;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MaxNumSamples = 7;
|
|
|
|
|
}
|
|
|
|
|
return MaxNumSamples;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-08 09:05:50 -04:00
|
|
|
float FRCPassPostProcessWeightedSampleSum::GetClampedKernelRadius(ERHIFeatureLevel::Type InFeatureLevel, float KernelRadius)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-05-08 09:05:50 -04:00
|
|
|
return FMath::Clamp<float>(KernelRadius, DELTA, GetMaxNumSamples(InFeatureLevel) - 1);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|
|
|
|
|
|
2014-05-08 09:05:50 -04:00
|
|
|
int FRCPassPostProcessWeightedSampleSum::GetIntegerKernelRadius(ERHIFeatureLevel::Type InFeatureLevel, float KernelRadius)
|
2014-03-14 14:13:41 -04:00
|
|
|
{
|
2014-05-08 09:05:50 -04:00
|
|
|
return FMath::Min<int32>(FMath::CeilToInt(GetClampedKernelRadius(InFeatureLevel, KernelRadius)), GetMaxNumSamples(InFeatureLevel) - 1);
|
2014-03-14 14:13:41 -04:00
|
|
|
}
|