ゲームプログラミング技術集 
ひっと

HLSL アプリケーション側の実装方法

HLSLを使ったシェーダプログラミングを紹介しています。サンプルコードあります。

HLSL エフェクトファイルをアプリケーションで使用する

HLSLで作成したエフェクトファイルをアプリケーションに読み込んでレンダリングする方法を紹介していきます。

アプリケーション側で行うHLSLの処理

・Direct3Dがシェーダ機能を使えるかチェックする
・頂点シェーダで処理する頂点データを定義
・エフェクトオブジェクトの作成( HLSLのエフェクトファイルを読み込む )
・テクニックの選択
・描画
  ・エフェクト開始宣言
  ・HLSLのグローバル変数をセット
  ・パスの開始宣言
  ・(ポリゴンを描画)
  ・パスの終了宣言
  ・エフェクト終了宣言

Direct3Dがシェーダ機能を使えるかチェックする

ピクセルシェーダと頂点シェーダそれぞれの利用可能なバージョンをチェックします。


    LPDIRECT3DDEVICE9 device;

    D3DCAPS9 caps;
    device->GetDeviceCaps(&caps);

    if ( ( caps.VertexShaderVersion < D3DVS_VERSION(2,0) ) ||
         ( caps.PixelShaderVersion < D3DPS_VERSION(2,0)  ) ) {
        ::MessageBox(NULL,_T("プログラマブルシェーダの機能に対応していません。"),_T(""),MB_OK);
    }

頂点シェーダが処理する頂点データを定義

※「頂点シェーダ実装例」で説明した内容と同じなので、知っている方は読み飛ばして大丈夫です。

Direct3Dで使う頂点はプログラマーがある程度自由に定義できます。そのため定義した頂点の内容は頂点シェーダにも知らせなければいけません。

Direct3Dで使う頂点データの例

    struct CUSTOMVERTEX
    {
        D3DVECTOR   pos;   //頂点の位置
        DWORD       color; //頂点の色
        float       tu;    //テクスチャ座標
        float       tv;
    };

CUSTOMVERTEXで定義した頂点の内容に従って頂点シェーダにその構造を知らせます。

頂点シェーダが処理する頂点データの構造を定義

    LPDIRECT3DDEVICE9 device;
    HRESULT hr;

    LPDIRECT3DVERTEXDECLARATION9 pVertexDeclaration; //頂点シェーダの頂点定義

    //パイプラインに渡す頂点データの構造を定義
    D3DVERTEXELEMENT9 decl[] = {
        { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, //位置
        { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},  //色
        { 0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, //テクスチャ座標
        D3DDECL_END() //最後に必ずD3DDECL_END()をつける
    };

    //頂点の定義オブジェクトを作成する
    hr = device->CreateVertexDeclaration( decl, &pVertexDeclaration );
    if ( FAILED(hr) ) {
        throw _T("error");
    }

    //頂点定義をセット
    device->SetVertexDeclaration( pVertexDeclaration );

特にD3DVERTEXELEMENT9の二番目の値に注意して下さい。 0,12,16と入っているこの値は、構造体(CUSTOMVERTEX)の先頭からのオフセット(先頭から何バイトずれているか)を差しています。

エフェクトオブジェクトの作成(HLSLのファイルを読み込み)

HLSLで記述したエフェクトファイルを読み込んでDirect3Dのエフェクトオブジェクトを作成します。


    LPCWSTR path = _T("エフェクトファイル");
    LPDIRECT3DDEVICE9 device;
    LPD3DXEFFECT pEffect;

    HRESULT hr;
    //ファイルの読み込み
    LPD3DXBUFFER pErrMessage = NULL;
    hr = D3DXCreateEffectFromFile( GetDevice(), path, NULL, NULL, 0, NULL, &pEffect, &pErrMessage );
    if ( FAILED(hr) ) {
        //エラーメッセージ表示(文字コードがANSI)
        ::MessageBoxA(NULL,(LPCSTR)(pErrMessage->GetBufferPointer()),"",MB_OK);
        pErrMessage->Release();
        throw 0;
    }

HLSLのエフェクトオブジェクトを使って描画する

テクニックの選択

HLSLに定義しておいたテクニック(シェーダプログラムの組み合わせ)を選択してレンダリング方法を決めます。

    LPD3DXEFFECT pEffect;

    pEffect->SetTechnique("RenderScene");	//引数は任意のテクニックの名称

HLSLのグローバル変数へデータを渡す

エフェクトファイルに定義しておいたグローバル変数へデータを渡します。
この例では座標変換行列とテクスチャを渡しています。


    LPD3DXEFFECT pEffect;
    D3DXMATRIX mat;
    LPDIRECT3DTEXTURE9 texture;

    //第一引数がグローバル変数名 二つ目がデータ。
    pEffect->SetMatrix("g_mWorldViewProjection", mat );
    pEffect->SetTexture( "g_Texture", texture );

アセンブルで頂点シェーダを実装した時は座標変換行列を転置行列にしていましたが、HLSLでは不要になりました。

描画

エフェクトを有効にして描画するにはちょっと手間がかかります。
レンダリング処理をBeginとEndで挟まなければいけません。


    LPD3DXEFFECT pEffect;

    //エフェクト開始
    pEffect->Begin( NULL, 0 );	//第一引数ではパスの数を取得できる

    //使用するパスを選択
    pEffect->BeginPass( 0 );

    //ここで描画処理をする(DrawPrimitiveなど)

    pEffect->EndPass();//パス終了
    pEffect->End();//エフェクト終了

戻る    

Direct3Dグラフィック - シェーダプログラミング