C#でツールを作る その15 -HLSLを使ってみる-

今ではシェーダも一般的になっているようなので、私もちょっと遊んでみようと思います。

とりあえず、最低限必要なものを。

Effectオブジェクトの作成と破棄

private Effect effect = null;
String err;     //コンパイルエラー時の情報を取得
effect = Effect.FromFile(
    device,
    エフェクトファイル名,
    null,
    ShaderFlags.None,
    null,
    out err
    );
if (effect != null)
{
    effect.Dispose();
    effect = null;
}

Techniqueの取得と設定

private EffectHandle tech = null;
tech = effect.GetTechnique( テクニック名 );
effect.Technique = tech;

Effect変数の取得と設定

private EffectHandle handle = null;
handle = effect.GetParameter( null, "WorldViewProjection" );   //WorldViewProjectionはエフェクトファイル内の変数
Matrix WorldViewProjection = ...;
effect.SetValue( handle, WorldViewProjection );
//effect.SetValue( "WorldViewProjection", WorldViewProjection );
  • EffectHandleは文字列を渡すことも可能

描画

  • エフェクトを開始するとパス数が返ってくるので、その回数分ループで描画
int n = effect.Begin( FX.None );
for( int pass = 0; pass < n; pass ++ )
{
    effect.BeginPass( pass );

    描画;
    
    effect.EndPass();
}
effect.End();

fxファイル

//-----------------------------------------------------------
//global
//-----------------------------------------------------------
float4x4 WorldViewProjection;  //ワールド*ビュー*プロジェクション行列


//-----------------------------------------------------------
//struct
//-----------------------------------------------------------
struct VertexInput
{
    float4 pos : POSITION;
};

struct VertexOutput
{
    float4 pos : POSITION;
    float4 color : COLOR0;
};

struct PixelOutput
{
    float4 color : COLOR0;
};


//-----------------------------------------------------------
//vertex shader
//-----------------------------------------------------------

VertexOutput TestVertexShader( VertexInput input )
{
    VertexOutput output;
	
    //座標変換
    output.pos = mul( input.pos, WorldViewProjection );
	
    //ライティング
    float4 diffuse = { 0.0f, 0.0f, 0.0f };
    output.color = diffuse;
	
    return output;
}



//-----------------------------------------------------------
//pixel shader
//-----------------------------------------------------------

PixelOutput TestPixelShader( VertexOutput input )
{
    Pixeloutput output;
    output.color = input.color;
    return output;
}


//-----------------------------------------------------------
//technique
//-----------------------------------------------------------
technique test
{
    pass P0
    {
        VertexShader = compile vs_2_0 TestVertexShader();
        PixelShader = compile ps_2_0 TestPixelShader();
    }
}

C#でツールを作る その15.1 -ランバート反射-

もっとも単純なライティングですね。
輪郭線もシェーダで描画したいので、はちゅねのモデルから面を反転した黒いメッシュを削除しました。

サンプル

  • download
  • VC#2008EE
  • VertexShader2.0
  • PixelShader2.0
  • miku
    • miku01.x
    • miku01.fx
    • miku01.mqo
    • miku.dds
    • miku.jpg
    • miku_alpha.tga

Lambert

  • Ia:ライトのアンビエント
  • Ka:マテリアルのアンビエント
  • Id:ライトのディフューズ
  • Kd:マテリアルのディフューズ
  • N:頂点法線
  • L:ライトベクトル

fxファイル

//-----------------------------------------------------------
//global
//-----------------------------------------------------------
float4x4 Mw;    //ワールド行列
float4x4 Mv;    //ビュー行列
float4x4 Mp;    //プロジェクション行列

float4 mat_diffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
float4 mat_ambient = { 0.0f, 0.0f, 0.0f, 0.0f };

float4 light_dir = { 0.0f, 0.0f, 1.0f, 0.0f };
float4 light_diffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
float4 light_ambient = { 0.2f, 0.2f, 0.2f, 1.0f };

texture tex;
sampler test_sampler = sampler_state
{
    Texture = <tex>;
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = NONE;
    AddressU = Clamp;
    AddressV = Clamp;
};
//-----------------------------------------------------------
//struct
//-----------------------------------------------------------
struct VertexInput
{
    float4 pos : POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOORD0;
};

struct VertexOutput
{
    float4 pos : POSITION;
    float4 color : COLOR0;
    float2 tex : TEXCOORD0;
};

struct PixelOutput
{
    float4 color : COLOR0;
};
//-----------------------------------------------------------
//vertex shader
//-----------------------------------------------------------

VertexOutput TestVertexShader( VertexInput input )
{
    VertexOutput output;
	
    //座標変換
    float4 world_pos = mul( input.pos, Mw );
    float4 view_pos = mul( world_pos, Mv );
    output.pos = mul( view_pos, Mp );
	
    //ライティング(lambert)
    float3 L = -light_dir.xyz;
    output.color.rgb =
        mat_diffuse.rgb * light_diffuse * max( dot( L, input.normal ), 0.0f ) +
        mat_ambient.rgb * light_ambient;
    output.color.a = mat_diffuse.a;
	
    output.tex = input.tex;
    return output;
}
//-----------------------------------------------------------
//pixel shader
//-----------------------------------------------------------

PixelOutput TestPixelShader( VertexOutput input )
{
    PixelOutput output;
    output.color = tex2D( test_sampler, input.tex ) * input.color;
    return output;
}
//-----------------------------------------------------------
//technique
//-----------------------------------------------------------
technique test
{
    pass P0
    {
        VertexShader = compile vs_2_0 TestVertexShader();
        PixelShader = compile ps_2_0 TestPixelShader();
		
        ZEnable = true;
        ZWriteEnable = true;
        Lighting = false;
        AlphaBlendEnable = true;
        CullMode = CCW;
        SrcBlend = SRCALPHA;
        DestBlend = INVSRCALPHA;
    }
}

まとめ

  • pass単位でレンダーステートを設定できるのは便利。
  • 複数のpassはどうやって使い分ければいいのか?
  • pass実行中にSetValue不可。これではまった。。。
  • XNAのEffectクラスが使いやすそう。
  • エフェクトで使用するメッシュやテクスチャの指定はアノテーションを使うべきなのか。