所以我在 XNA(3.1。是的,我知道它已经非常过时了。是的,我有理由使用它)和 HLSL 中构建了一个下采样算法。本质上,它的工作原理是对原始纹理应用高斯模糊,然后使用内置于 XNA 中的默认最近邻重新缩放来调整其大小。我的想法是,高斯模糊会给出一个颜色区域平均值的近似值,因此它本质上是一种减少混叠的廉价方法。它工作得很好,而且速度很快,但会产生一些有趣的伪影——它似乎略微拉伸了图像。这通常不明显,但我下采样的一些东西是精灵表,当动画时,很明显精灵没有放在正确的位置。我' 我想知道一个不同的重采样器(也内置在 HLSL 中以提高 GPU 的速度)可能是一个更好的选择,或者我可以修复这个错误。我会把我的代码贴在这里,看看有没有人可以启发我。
首先是我的 HLSL 高斯效果文件:
#define RADIUS 7
#define KERNEL_SIZE (RADIUS * 2 + 1)
float weightX[KERNEL_SIZE];
float weightY[KERNEL_SIZE];
float2 offsetH[KERNEL_SIZE];
float2 offsetV[KERNEL_SIZE];
texture colorMapTexture;
sampler TextureSampler : register(s0);
void BlurH(inout float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
float4 c = float4(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < KERNEL_SIZE; ++i)
c += tex2D(TextureSampler, texCoord + offsetH[i]) * weightX[i];
color = c;
}
void BlurV(inout float4 color : COLOR0, float2 texCoord : TEXCOORD0)
{
float4 c = float4(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < KERNEL_SIZE; ++i)
c += tex2D(TextureSampler, texCoord + offsetV[i]) * weightY[i];
color = c;
}
technique GaussianBlur
{
pass
{
PixelShader = compile ps_2_0 BlurH();
}
pass
{
PixelShader = compile ps_2_0 BlurV();
}
}
还有我初始化高斯效应的代码(注意 gaussianBound 设置为 8,即 1+ HLSL 文件中的半径):
public static Effect GaussianBlur(float amount, float radx, float rady, Point scale)
{
Effect rtrn = gaussianblur.Clone(MainGame.graphicsManager.GraphicsDevice);
if (radx >= gaussianBound)
{
radx = gaussianBound - 0.000001F;
}
if (rady >= gaussianBound)
{
rady = gaussianBound - 0.000001F;
}
//If blur is too great, image becomes transparent,
//so cap how much blur can be used.
//Reduces quality of very small images.
Vector2[] offsetsHoriz, offsetsVert;
float[] kernelx = new float[(int)(radx * 2 + 1)];
float sigmax = radx / amount;
float[] kernely = new float[(int)(rady * 2 + 1)];
float sigmay = rady / amount;
//Initialise kernels and sigmas (separately to allow for different scale factors in x and y)
float twoSigmaSquarex = 2.0f * sigmax * sigmax;
float sigmaRootx = (float)Math.Sqrt(twoSigmaSquarex * Math.PI);
float twoSigmaSquarey = 2.0f * sigmay * sigmay;
float sigmaRooty = (float)Math.Sqrt(twoSigmaSquarey * Math.PI);
float totalx = 0.0f;
float totaly = 0.0f;
float distance = 0.0f;
int index = 0;
//Initialise gaussian constants, as well as totals for normalisation.
offsetsHoriz = new Vector2[kernelx.Length];
offsetsVert = new Vector2[kernely.Length];
float xOffset = 1.0f / scale.X;
float yOffset = 1.0f / scale.Y;
//Set offsets for use in the HLSL shader.
for (int i = -(int)radx; i <= radx; ++i)
{
distance = i * i;
index = i + (int)radx;
kernelx[index] = (float)Math.Exp(-distance / twoSigmaSquarex) / sigmaRootx;
//Set x kernel values with gaussian function.
totalx += kernelx[index];
offsetsHoriz[index] = new Vector2(i * xOffset, 0.0f);
//Set x offsets.
}
for (int i = -(int)rady; i <= rady; ++i)
{
distance = i * i;
index = i + (int)rady;
kernely[index] = (float)Math.Exp(-distance / twoSigmaSquarey) / sigmaRooty;
//Set y kernel values with gaussian function.
totaly += kernely[index];
offsetsVert[index] = new Vector2(0.0f, i * yOffset);
//Set y offsets.
}
for (int i = 0; i < kernelx.Length; ++i)
kernelx[i] /= totalx;
for (int i = 0; i < kernely.Length; ++i)
kernely[i] /= totaly;
//Normalise kernel values.
rtrn.Parameters["weightX"].SetValue(kernelx);
rtrn.Parameters["weightY"].SetValue(kernely);
rtrn.Parameters["offsetH"].SetValue(offsetsHoriz);
rtrn.Parameters["offsetV"].SetValue(offsetsVert);
//Set HLSL values.
return rtrn;
}
除此之外,我的函数只是简单地在每次通过效果时绘制到纹理,然后将结果绘制到不同比例的新纹理。这看起来真的很好,但正如我所说,会产生这些不正确的东西的人工制品。一些帮助在这里将不胜感激。