Top > ComputerGraphics > HLSL > Common > GetPixelCoordinateInHLSL
Last-modified: Mon, 09 Jul 2012 08:59:01 JST
Counter:10678 Today:2 Yesterday:0 Online:6
このエントリーをはてなブックマークに追加

HLSLでテクスチャのピクセル座標を取得する

About

 HLSL(シェーダ)ではテクスチャの座標は、X軸Y軸共に0~1で表される。しかしながら例えばフィルタリング処理などでは、与えたテクスチャのピクセルレベルでの座標が必要になる。そこでHLSL内であってもテクスチャの座標をピクセル単位で取得できるようにする。

SpriteにPixelShaderを適用する

 Spriteで描画するオブジェクト(スプライト)、例えばテクスチャなどにPixelShaderを適用する場合は次の様にする。

        Effect effect;
…
            spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, effect);

            //エフェクトを利用して描画
            spriteBatch.Draw(tex2d_Lena, Vector2.Zero, Color.White);

            spriteBatch.End();

 「SpriteBatch.Beginメソッド」によってSpriteの描画を開始する際に、利用するエフェクトを指定する。その他のオプションに関しては、基本的には「null」を指定しておけば、デフォルト値が設定されるようになっている。特に必要がない限りはnullを指定するのが良い。第一引数である「SpriteSortMode」のデフォルト値は「SpriteSortMode.Deferred」である。

特に確認していないが、必要がない限り、指定するEffect(シェーダ)は、PixelShaderのみを持つようにした方が良い。VertexShaderがあっても動作するが、Spriteに適用する分には、基本的にはVertexShader無しでEffectファイルを利用・適用することができる。

Sample

1.png

 ここではレナの画像を対象に、ピクセル座標が偶数列・偶数行の時に、そのピクセルに黒を描画する様なプログラムを実装した。SpriteBatchを利用して描画するのでPixelShaderしか用意しない。左に用意した結果画像は縮小表示のため、ブラウザ上では効果が確認し難いので注意されたい。

How to

0.png

 どうということはなくて、実際のサイズにスケールを合わせれば良い。実際のテクスチャ幅が100pxだったとして、例えば現在のHLSLテクスチャ空間内のX座標が0.2であるなら「100 x 0.2 = 20」で「X=20px」といった具合にピクセルでの座標を取得することができる。

 シェーダ内ではテクスチャのピクセルレベルでの大きさを取得することができないので、CPU側からあらかじめパラメータとして与える必要があるので注意する。

CPU

 まずはCPU側から、ピクセル座標を取得したいテクスチャのサイズをシェーダに引き渡す。特にそれ以上の処理をする必要は無い。

            effect.Parameters["TextureSize"].SetValue(new Vector2(tex2d_Lena.Width, tex2d_Lena.Height));

GPU

 与えられたテクスチャサイズから先の説明の通り乗算によってピクセル座標を求める。最後にround関数によって整数に丸める。round関数は切り捨て切り上げを考慮する様なので(MSDN参照:「trunc (DirectX HLSL)」)、実は+0.5の処理は不要。※何かしらの原因で+0.5が必要であった気がするが失念したので同現象が発生次第に追記する。

//テクスチャのサイズ
float2 TextureSize;

//SpriteBatchで送られるTexture
texture ScreenTexture;
sampler ScreenTexSampler:register(s0) = sampler_state
{
    Texture = <ScreenTexture>;
};

float4 PixelShaderFunction(float2 texCoord:TEXCOORD) : COLOR0
{

    //列
    float column = round((texCoord.x * TextureSize.x) + 0.5);
    //行
    float row = round((texCoord.y * TextureSize.y) + 0.5);

References