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