読者です 読者をやめる 読者になる 読者になる

Dynamic Compilation - WPF Shader Effects

Shader Effects BuildTask and Project Templates

.NET Framework 3.5 SP1 では PixelShader 2.0 を用いてポストエフェクトがかけられるようになりましたが,ユーザ定義エフェクトの作成は少しややこしい準備が必要です.この作業を簡略化するカスタムビルドタスクとプロジェクトテンプレートが,.NET Framework 3.5 SP1 のリリースに合わせて CodePlex にて公開されました.作者は Microsoft WPF team の Greg Schechter & Gerhard Schneider コンビです.

このカスタムビルドタスクを用いると,プロジェクトをビルド時にエフェクトファイルのコンパイルも一緒に行われます.XNA Game Studio の Content Pipeline の簡易版とも言えます.
このコンパイル部分ですが,C++/CLI で実装されています.実は出所の怪しい D3DX の静的リンクライブラリが同梱されていて*1,d3dx9_xx.dll が見つからない場合でも単独動作可能という特徴があるようです.

ID3DXEffectCompiler

さて,d3dx9_xx.dll の存在を前提にできるなら*2,アンマネージコードとの相互運用を C# だけで完結させることが可能です.
d3dx9_xx.dll がエクスポートする D3DXCreateEffectCompiler に注目します.このエクスポート関数が返す ID3DXEffectCompiler インターフェイスは,動作に IDirect3DDevice9 を必要とせず,バックグラウンドでエフェクトファイルを取り扱うのに便利です.
ID3DXEffectCompiler を基点として,エフェクトファイルに含まれるシェーダコードをコンパイルしたり,エフェクトファイルに含まれる要素に対するリフレクションを行うことができます.

エフェクトファイルの実行時コンパイルサンプル

ちょうど手元に書きかけの ID3DXEffectCompiler ラッパーライブラリがあったので,少し書き足してそこそこ使える形にまとめてみました.それを使って出来たのが以下のサンプルです.このサンプルは,実行時にエフェクトファイルのコンパイルを行い,動的に WPF のシェーダエフェクトを生成します.

  • Binary and source code
  • 実行方法
    • .NET Framework 3.5 SP1 以降が必要です.
    • 起動したら,"Open Movie" ボタンを押して適当に動画ファイルを選択して下さい.
    • 左の Compile ボタンを押すと,表示されている HLSL をコンパイル・実行します.
    • 適用される PixelShader の関数名は main にハードコードしてあります.
    • WPF の仕様で,PixelShader 2.0 に固定されています.
    • elapsedSeconds 変数には,動画ファイル先頭からの経過時間がバインドされます.

ID3DXEffectCompiler ラッパーライブラリの使い方

上記サンプルコードに含まれる ID3DXEffectCompiler ラッパーライブラリの使い方をごく簡単に紹介しておきます.
文字列型のエフェクトファイルソースコードから,シェーダのバイトコードを得るには次のようにします.

//D3DX Effect Format
const string SourceCode = 
@"sampler2D implicitInput : register(s0);
float elapsedSeconds : register(c0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float phase = 10.0f*(uv.y + elapsedSeconds);
    float2 offset = 0.1f * float2(cos(phase), 0.0f);
    return tex2D(implicitInput, uv + offset);
}
";

var effect = CompiledEffect.FromString(SourceCode, CompilerOptions.None);
if(effect.CompileSucceeded)
{
    var shader = effect.CompileShader("main", "ps_2_0", CompilerOptions.None);
    if(shader.CompileSucceeded)
    {
        var shader = new PixelShader();
        shader.SetStreamSource(new MemoryStream(shader.Data));

        .......

ラッパーの本体は EffectCompiler.cs にまとめてあります.コピー・改変等ご自由にお使い下さい.

更新履歴

  • 2008年8月15日
    • EffectCompiler.cs: PreserveSig 属性が設定されていなかった COM 呼び出し全てに PreserveSig 属性を付けた.
  • 2010年10月2日

*1:WPFWindows Vista 用のカスタムビルドの可能性があります

*2:一般的には [http://www.microsoft.com/downloads/details.aspx?displaylang=ja&FamilyID=2da43d38-db71-4c1b-bc6a-9b6652cd92a3:title=DirectX の Web Installer] を使ってもらうことになるでしょう