2

そこで、XNA(3.1。はい、それがひどく時代遅れであることを認識しています。はい、それを使用する理由があります)とHLSLでダウンサンプリングアルゴリズムを構築しました。基本的には、元のテクスチャにガウスぼかしを適用し、XNAに組み込まれているデフォルトの最近傍再スケーリングを使用してサイズを変更することで機能します。私の考えでは、ガウスぼかしは色の領域の平均に近似するので、エイリアシングを減らすための本質的に安価な方法です。それはうまく機能し、非常に迅速ですが、いくつかの興味深いアーティファクトを生成します-それは画像を非常にわずかに引き伸ばすようです。これは通常は目立ちませんが、私がダウンサンプリングしているもののいくつかはスプライトシートであり、アニメーション化すると、スプライトが正しい位置に配置されていないことは非常に明白です。私' 別のリサンプラー(GPUの速度のためにHLSLにも組み込まれている)がより良いオプションであるかどうか、またはこれにエラーがある場合は修正できるかどうか疑問に思っています。私はここに私のコードを投稿し、私を啓発することができる誰かがいるかどうかを確認します。

最初は私の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;
        }

これを超えて、私の関数は、エフェクトのパスごとにテクスチャに描画し、その結果を異なるスケールで新しいテクスチャに描画します。これは本当に見栄えがしますが、私が言ったように、適切な場所に完全に配置されていないもののこれらのアーティファクトを生成します。ここでいくつかの助けをいただければ幸いです。

遺物を表示

4

2 に答える 2

2

さて、私は何かを発見しました:それはガウスぼかしとは何の関係もありません。問題は、データの損失のためにこれらのアーティファクトを生成するNearest Neighbourでスケールダウンしていることです(たとえば、何かが本質的にピクセル5.5にある必要がある場合、ピクセル5に配置するだけで、位置エラーが発生します) 。これを手伝ってくれたみんなに感謝しますが、アルゴリズムを少し考え直さなければならないようです。

リサンプリングは整数のリサンプルに対してのみ機能するという制約を追加することで修正しました。それ以外のものは、利用可能な最も近い整数サンプルにリサンプリングし、残りをNNでスケーリングします。これは私が以前に作業していたものとほぼ同じですが、HLSLのおかげで今でははるかに高速になっています。任意のスケーリングアルゴリズムを取得したいと思っていましたが、それは私のニーズには十分に機能します。まだオフセットエラーが発生しているため(データが失われたためにダウンサンプリング時に完全に回避することはほぼ不可能です)、完全ではありませんが、現在は明らかに1ピクセル未満であるため、探している場合を除いて目立たないようになっています。彼ら。

于 2012-12-04T18:06:45.407 に答える
0

疑問があります。2番目のパスでは、最初のパスの結果を使用する必要があります。それ以外の場合は、BlurHとBlurVを組み合わせるだけで、結果は同じになります。最初のパスの結果を利用したり、最初のパスから2番目のパスに転送したりするコードが見つかりません。

于 2013-02-28T12:15:18.113 に答える