float gaussianKernel(float x, float standardDeviation)
{
return exp(-(x * x) / (2 * standardDeviation * standardDeviation)) / (sqrt(2 * 3.14159265) * standardDeviation);
}
float4 PS(VSO input) : SV_TARGET0
{
const int numSamples = 3;
const float standardDeviation = numSamples / 3.0;
const float offset[numSamples] = { 0.0, 1.3846153846, 3.2307692308 };
const float weight[numSamples] = { 0.40261952, 0.2442015368, 0.0544886997 }; //Either use these or the gaussianKernel function
float3 texColor = TargetTexture.Sample(TargetTextureSampler, input.UV).xyz * gaussianKernel(0, standardDeviation); //You forgot about this weight here
for (int i = 1; i < numSamples; i++)
{
float weight = gaussianKernel(i, standardDeviation);
texColor += TargetTexture.Sample(TargetTextureSampler, input.UV + float2(offset[i], 0.0f) / _ScreenSize.x).rgb * weight;
texColor += TargetTexture.Sample(TargetTextureSampler, input.UV - float2(offset[i], 0.0f) / _ScreenSize.x).rgb * weight;
}
return float4(texColor.rgb, 1.0f);
}
You might also want to check out the implementation I'm currently using in my engine (even though I'm currently switching to a more optimized compute shader implemention with a runtime of O(log n) per pixel):
#ifndef MIN_WEIGHT
#define MIN_WEIGHT 0.0001f
#endif
#ifndef FILTER
#error You have to define the filter. (FILTER = (GAUSSIAN|EXPONENTIAL))
#endif
#define GAUSSIAN 0
#define EXPONENTIAL 1
#if FILTER == GAUSSIAN
#ifndef STANDARD_DEVIATION
#error You have to define the standard deviation when using a gaussian kernel. (STANDARD_DEVIATION = float)
#endif
#elif FILTER == EXPONENTIAL
#ifndef MEAN_VALUE
#error You have to define the mean value when using an exponential kernel. (MEAN_VALUE = float)
#endif
#endif
#ifndef DIRECTION
#error You have to define the direction. (DIRECTION = (HORIZONTAL|VERTICAL|int2(x,y)))
#endif
#ifndef MIP
#define MIP 0
#endif
#define HORIZONTAL int2(1, 0)
#define VERTICAL int2(0, 1)
Texture2D SourceTexture : register(t0);
cbuffer InfoBuffer : register(b0)
{
float Width;
float Height;
};
struct PSIn
{
float4 Position : SV_POSITION;
float2 TexCoord : TEXCOORD0;
float2 ScreenPos : SCREEN_POSITION;
};
float gaussianKernel(int x, float standardDeviation)
{
return exp(-(x * x) / (2 * standardDeviation * standardDeviation)) / (sqrt(2 * 3.14159265) * standardDeviation);
}
float integratedExponentialKernel(float x, float m)
{
return 0.5 * (1 - exp(-x / m) / 2) * (sign(x) + 1) - 0.25 * exp(x / m) * (sign(x) - 1);
}
float exponentialKernel(int x, float m)
{
return integratedExponentialKernel(x + 0.5, m) - integratedExponentialKernel(x - 0.5, m);
}
float filter(int x)
{
#if FILTER == GAUSSIAN
return gaussianKernel(x, STANDARD_DEVIATION);
#elif FILTER == EXPONENTIAL
return exponentialKernel(x, MEAN_VALUE);
#endif
}
float3 sample(int2 position, int offset)
{
float3 textureColor = 0.0f;
float2 newOffset = offset * DIRECTION;
if (newOffset.x >= -8 && newOffset.x <= 7 && newOffset.y >= -8 && newOffset.y <= 7)
textureColor = SourceTexture.Load(
int3(position, MIP),
newOffset);
else
textureColor = SourceTexture.Load(int3(position + newOffset, MIP));
return textureColor;
}
float4 PSMain(PSIn Input) : SV_Target
{
float3 accumulatedColor = 0.0f;
float accumulatedWeight = 0, weight = 0;
[unroll]
for (int x = 0; (weight = filter(x)) > MIN_WEIGHT; ++x)
{
accumulatedWeight += (x != 0) ? (2 * weight) : weight;
}
[unroll]
for (int x = 0; (weight = filter(x)) > MIN_WEIGHT; ++x)
{
accumulatedColor += weight / accumulatedWeight * sample((int2)Input.ScreenPos, x);
if (x != 0)
accumulatedColor += weight / accumulatedWeight * sample((int2)Input.ScreenPos, -x);
}
return float4(accumulatedColor, 1);
}
Hi,
I have few questions about your approach:
1) why you use the custom semantic SCREEN_POSITION and not the SV_Position which is actually the same once you get it in the pixel shader as input.
2) which kind of kernel is the exponential one with respect to the standard gaussian one and which visual results it gives and what is best for?
3) Also, do you support a poisson sampling kernel as well ? If yes what's the best way to express it and what is good for (I mean in what situation)
4) do you apply you filter on downsampled version of your buffer? If yes how much and how that will impact the correctness of the end result ? I guess no, because I see you don't sample at the pixels edges ... (that should help biliniear sampling when you stretch at fullscreen, right?)
5) Last

, do you know any good resource on the web where I can find a list of filters along with their use case situation (I mean something like when it's good to use that filter or that other one and in which case and how it looks like etc.)
Thanks in advance for any reply