因此,我在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,即HLSL文件中的半径为1+):

 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;
        }


除此之外,我的函数每次通过效果时都会简单地绘制一个纹理,然后以不同的比例将结果绘制到一个新的纹理上。这看起来确实不错,但是正如我所说的,这些伪造的东西并不是在正确的地方。这里的一些帮助将不胜感激。

最佳答案

好吧,我发现了一些东西:它与高斯模糊无关。问题是我正在缩小“最近邻居”的规模,由于数据丢失,这些伪像会产生这些伪像(例如,当某物本质上需要位于像素5.5时,它只是将其放置在像素5中,从而产生位置误差) 。感谢所有尝试提供帮助的人,但看来我将不得不稍微重新考虑我的算法。

我通过添加一个额外的约束(即重新采样仅适用于整数重新采样)来解决此问题。其他任何东西都将重新采样到最接近的可用整数样本,然后使用NN缩放其余部分。这几乎与我以前的工作完全相同,但是现在由于有了HLSL而使工作变得更快了。我希望获得一个任意的缩放算法,但是它可以很好地满足我的需求。这不是完美的,因为我仍然遇到偏移误差(由于数据丢失,在进行下采样时几乎不可能完全避免偏移误差),但是现在它们明显小于一个像素,因此除非您正在寻找,否则就不明显了。他们。

关于c# - XNA + HLSL高斯模糊产生偏移的伪像,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13690448/

10-13 04:09