Top > ComputerGraphics > HLSL > XNA > UsePixelShaderModel3InSpriteBatch
Last-modified: Wed, 11 Jul 2012 10:14:27 JST
Counter:1917 Today:1 Yesterday:2 Online:8
このエントリーをはてなブックマークに追加

SpriteBatchでPixelShader3を利用する

About

  • &ref(): File not found: "WindowsGame1_UsePixelShaderModel3InSpriteBatch.zip" at page "ComputerGraphics/HLSL/XNA/UsePixelShaderModel3InSpriteBatch";
    • VisualStudio2010
    • XNA4.0

 XNA4.0で新規ファイルとしてEffectファイルを追加すると、デフォルトのシェーダモデルは2_0となっている。2012年7月現在シェーダモデルは5まで進んでいるので、モデル2はいささか古い。とはいえXNA4.0はDirectX9の上に作られたフレームワークなので利用できるシェーダモデルは(たしか)3まで。だがしかしシェーダモデル2と3では雲泥の差がある。シェーダモデル3はモデル2と異なり、利用できる演算数の上限がなくなっている。対してモデル2では演算数の上限が64と定められているため、実装したい内容によってはモデル3が必須となってくる。

//Effectファイルを新規追加した際のデフォルトの状態
technique Technique1
{
    pass Pass1
    {
        // TODO: ここでレンダーステートを設定します。

        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

SpriteBatchによるレンダリングへの適用

 VetexShader、PixelShaderともシェーダモデル3_0を使うのであればどうということはなく、デフォルトで定められているシェーダモデルの記述を「2_0」から「3_0」と書き換えればよい。

technique Technique1
{
    pass Pass1
    {
        // TODO: ここでレンダーステートを設定します。

        VertexShader = compile vs_3_0 VertexShaderFunction();
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }
}

 ところがSpriteBatchでレンダリングする場合は、単純にはいかない。VertexShaderがなくともコンパイルが通るし、実際にプログラマにはPixelShaderしか利用していない様に見える。そこでとりあえず次の様なことを試すことになる。

    pass Pass1
    {
        PixelShader = compile ps_3_0 PixelShaderFunction();
    }

 すると一応コンパイルは通る。しかしいざプログラムを実行しようとするとエラーが起きる。「InvalidOperationException」である。

 シェーダー モデル 3.0 をそれ以前のバージョンのシェーダー モデルと混在させることはできません。頂点シェーダーとピクセル シェーダーのいずれかが 3.0 でコンパイルされている場合、両方とも 3.0 でコンパイルする必要があります。

 先にも述べたように、XNAでは標準的にシェーダモデル2が利用されている。つまりはプログラマ側に開示はされていないが、SpriteBatchを利用して描画する際に、内部的にはシェーダモデル2のVertexShaderが利用されている、ということらしい。

 この問題を解決するには、XNAのSpriteBatchで用いられるVertexShaderと同等の関数を用意し、シェーダモデル3として自前でコンパイルするしかない。

How To

 調べてみると多くの所で同様の問題について言及されているので直に答えに行きつく。要するにVertexShaderでは頂点の投影が行われるわけなので、SpriteBatchによる描画と同等の結果を得るためには、描画域と同サイズの平行投影を行えばよいことになる。特にソースコードについては解説しないが、次の様になっている。サンプルプロジェクトを展開するなりして確認されたい。

  • GPU側(Effect)
    //頂点シェーダ用
    float4x4 MatrixTransform;
    
    …
    
    void DummyVertexShaderFunction(inout float4 color    : COLOR0,
                            inout float2 texCoord : TEXCOORD0,
                            inout float4 position : SV_Position)
    {
        position = mul(position, MatrixTransform);
    }
    
    technique Technique1
    {
        pass Pass1
        {
            VertexShader = compile vs_3_0 DummyVertexShaderFunction();
            PixelShader = compile ps_3_0 PixelShaderFunction();
        }
    }
    
  • CPU側(Gameクラス)
            protected override void LoadContent()
            {
                // 新規の SpriteBatch を作成します。これはテクスチャーの描画に使用できます。
                spriteBatch = new SpriteBatch(GraphicsDevice);
    
                effect = Content.Load<Effect>("Effect1");
                tex2d_Lena = Content.Load<Texture2D>("lena");
    
                //頂点シェーダ用の初期化
                Matrix projection = Matrix.CreateOrthographicOffCenter
                    (0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, 1);
                Matrix halfPixelOffset = Matrix.CreateTranslation(-0.5f, -0.5f, 0);
                effect.Parameters["MatrixTransform"].SetValue(halfPixelOffset * projection);
    
            }
    

Result

0.png

 図の通り実行結果は通常のSpriteBatchによる描画と同等になっている。多少記述量が増えるが一度実装してしまえば以降はPixelShaderのみ気にして実装すればよいので、然程気にならない。とはいえ早いところ標準のシェーダモデルを3にして貰いたいものである。XBOX等のハードウェア側の対応待ちか。いっそ最新のシェーダモデルまで引き上げてくれることを期待する。

References