トーラスに色を付けて Shader で描画する

トーラスの VertexBuffer を取得して、光源を設定して色を付けて Shader で描画するサンプルプログラムです。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの概要

  1. ビューと透視変換マトリックスとトーラスの色を設定する頂点シェーダを用意します。
  2. トーラスを生成して VertexBuffer と IndexBuffer を取得します。
    トーラスは D3DFVF_XYZ | D3DFVF_NORMAL の Index 形式で作成されています。
  3. 頂点シェーダのソースコード(nmap.vsh)を入力してシェーダを作成します。
  4. カスタムシェーダにトーラスのディフューズ色を渡します。
  5. Vertex Buffer から頂点シェーダを使ってトーラスを描画します。
  6. このプログラムは、ハードウエアがシェーダの機能をサポートしていなければ動きません。
    またシェーダ機能をサポートしているデバイスを選択することも重要です。
  7. プログラムの作成手順としては、汎用のシェーダーで動くことを確認してから専用のシェーダーを使って下さい。

プログラムの説明

  1. #include と #define と DirectX オブジェクトの定義です。
    D3DXCreateTorus() で生成したトーラスの頂点座標の形式は D3DVERTEX のようになっています。
    D3DVERTEX は MyDx3d.h の中で定義されています。
    g_pVB がトーラスの Vertex Buffer へのポインタで g_pMIdx が IndexBuffer へのポインタです。
    g_NumVtx には Vertex の数が、g_NumFaces には Faces の数が格納されます。
    g_Shader には nmap.vsh から生成したシェーダが格納されます。
    g_cor[3] がトーラスのディフューズ色です。
        /*****************************************************/
        /*  Vertex Shader を使って Torus を描画     前田 稔  */
        /*****************************************************/
        #define     NAME      "VertexTorus"
        #include    "MyD3D.h"
        #define D3DFVF_VERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)
    
        // Global variables
        MyDx3d                  *mydx3d  = NULL;        // MyDx3d Object Class
        HWND                    g_hWnd;
        LPDIRECT3DDEVICE9       g_pDEV   = NULL;
        LPD3DXMESH              g_pMesh  = NULL;
        LPDIRECT3DVERTEXSHADER9 g_Shader = NULL;
        LPDIRECT3DINDEXBUFFER9  g_pMIdx  = NULL;
        LPDIRECT3DVERTEXBUFFER9 g_pVB    = NULL;
        D3DXMATRIX              g_View;
        D3DXMATRIX              g_Proj;
        D3DXMATRIX              g_World;
        D3DLIGHT9               g_light;
        D3DXVECTOR3             ViewForm(0.0f,0.0f,-5.0f);
        DWORD                   g_NumVtx;
        DWORD                   g_NumFaces;
        float                   g_cor[3]= { 1.0f, 0.8f, 0.0f };
        
  2. 頂点シェーダの作成です。
    D3DXAssembleShaderFromFile() で nmap.vsh をアセンブルします。
    CreateVertexShader() で頂点シェーダを作成します。
        HRESULT InitVertexShader()
        {   LPD3DXBUFFER pVS;
    
            if (FAILED(D3DXAssembleShaderFromFile("nmap.vsh",NULL,NULL,0,&pVS,NULL)))   return E_FAIL;
            if (FAILED(g_pDEV->CreateVertexShader((DWORD*)pVS->GetBufferPointer(),&g_Shader)))
            {   pVS->Release();  return E_FAIL;  }
            pVS->Release();
            return S_OK;
        }
        
  3. デバイスの初期化です。
    MyDx3d Object をインスタンス化して、InitD3D() で3Dデバイスを初期化します。
    D3DXCreateTorus() でトーラスを生成します。
    GetVtxBuff() で VertexBuffer のポインタを取得します。
    InitVertexShader() で頂点シェーダのソースコードを入力してシェーダを作成します。
        HRESULT InitDevices(HWND hWnd)
        {
            mydx3d= new MyDx3d(hWnd);
            mydx3d->InitD3D(&g_pDEV,true);
            //トーラスメッシュを生成して VertexBuffer を取得する
            D3DXCreateTorus(g_pDEV,0.32f,0.8f,64,64,&g_pMesh,NULL);
            mydx3d->GetVtxBuff(g_pMesh,&g_NumVtx,&g_NumFaces,&g_pVB,&g_pMIdx);
            //頂点シェーダの作成
            if (FAILED(InitVertexShader()))
            {   ERMSG("Init Vertex Error");  return  E_FAIL;  }
            return S_OK;
        }
        
  4. 取得したオブジェクトを開放します。
        VOID Cleanup()
        {
            SAFE_RELEASE(g_Shader);
            SAFE_RELEASE(g_pVB);
            SAFE_RELEASE(g_pMIdx);
            SAFE_RELEASE(g_pMesh);
            SAFE_DELETE(mydx3d);
        }
        
  5. 時刻を取得してY軸を基点にして World 座標を回転します。
    SetMaterial() でマテリアルを設定します。
    SetLight() で光源を設定します。
    SetMaterial(), SetLight() は私有ライブラリに登録されている関数です。
        VOID SetupMatrices()
        {   RECT            rect;
    
            GetClientRect(g_hWnd,&rect);
            //World 座標をY軸を中心に自動回転
            D3DXMatrixRotationY(&g_World,timeGetTime()/1000.0f);
            g_pDEV->SetTransform(D3DTS_WORLD,&g_World);
            //View 座標の設定
            D3DXMatrixLookAtLH(&g_View,&ViewForm,
                               &D3DXVECTOR3(0.0f,0.0f,0.0f),&D3DXVECTOR3(0.0f,1.0f,0.0f));
            //透視変換の設定
            D3DXMatrixPerspectiveFovLH(&g_Proj,D3DXToRadian(45.0f),(float)rect.right/(float)rect.bottom,1,50);
            //光源を g_light に格納する
            mydx3d->SetLight();
            g_light= mydx3d->Light;
        }
        
  6. トーラスを描画する関数です。
    SetupMatrices() でマテリアルを設定します。
    SetVertexShader() でカスタムシェーダを使用することを指示します。
    SetVertexShaderConstantF(0,(float*)&m,4) で透視変換マトリックスを0番~3番のレジスタに設定します。
    SetVertexShaderConstantF(4,(float*)&g_light,1) で light のベクタを4番のレジスタに設定します。
    SetVertexShaderConstantF(5,g_cor,1) でトーラスの色を5番のレジスタに設定します。
    SetStreamSource() でトーラスのデータを Shader に渡します。
    SetIndices() でトーラスの Index を Shader に渡します。
    DrawIndexedPrimitive() で頂点シェーダを使ってトーラスを描画します。
        VOID Render()
        {
            if (!mydx3d)    return;
            g_pDEV->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(0,0,255),1.0f,0);
            //描画の開始
            if (SUCCEEDED(g_pDEV->BeginScene()))
            {   SetupMatrices();
                //☆専用の Shader で描画
                g_pDEV->SetVertexShader(g_Shader);
                D3DXMATRIX  m;
                m= g_World * g_View * g_Proj;                 //回転・視点・透視
                D3DXMatrixTranspose(&m,&m);
                g_pDEV->SetVertexShaderConstantF(0,(float*)&m,4);       //全体の変換行列
                g_pDEV->SetVertexShaderConstantF(4,(float*)&g_light,1); //light
                g_pDEV->SetVertexShaderConstantF(5,g_cor,1);            //Color
                g_pDEV->SetStreamSource(0,g_pVB,0,sizeof(D3DVERTEX));
                g_pDEV->SetFVF(D3DFVF_VERTEX);
                g_pDEV->SetIndices(g_pMIdx);
                g_pDEV->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0,0,g_NumVtx,0,g_NumFaces);
                g_pDEV->EndScene();
            }
            g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  7. MsgProc(), WinMain() はいつもと同じです。
    InitDevices() でデバイスの設定と初期化を行います。
    Render() でレンダリングを行います。
    Cleanup() で全てのオブジェクトとリソースを開放します。

頂点シェーダ(color.vsh) のソースコードです。

m4x4 oPos,v0,c0 でビューと透視変換マトリックスを設定します。
dp3 r0,v1,c4 でライトのベクトルと法線ベクトルの内積を求めて色情報を掛け合わせます。
mul oD0, r0, c5 でディフューズ色を掛けて oD0 に設定します。
vs_2_0                  // バージョン2.0の頂点シェーダを使う

dcl_position0 v0        // 頂点の位置0をv0レジスタにマッピングする
dcl_normal0   v1        // 頂点の法線0をv1レジスタにマッピングする

m4x4 oPos, v0, c0       // v0とc0~c3行列を掛け合わせるて、oPosに出力する
dp3 r0, v1, c4          // 法線ベクトルv1と光源ベクトルc4の内積を計算
mul oD0, r0, c5         // 光の強さが求まったのでディフューズ色c5に掛ける

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)