ピクセルシェーダを使ってトーラスを描画する

頂点シェーダとピクセルシェーダを使って、トーラスにディフューズ色を設定して描画します。

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

プログラムの概要

  1. 頂点シェーダ(Nmap.vsh)とピクセルシェーダ(PShader.ps)を用意します。
  2. トーラスを生成して VertexBuffer と IndexBuffer を取得します。
    トーラスは D3DFVF_XYZ | D3DFVF_NORMAL の Index 形式で作成されています。
  3. 頂点シェーダのソースコードをアセンブルして頂点シェーダを作成します。
  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_PShader には、ピクセルシェーダ(PShader.ps) が格納されます。
    g_cor[3] がトーラスのディフューズ色です。
        /************************************************************/
        /*  Vertex,Pixcel Shader を使ってトーラスを描画    前田 稔  */
        /************************************************************/
        #define     NAME      "Shader Torus"
        #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;
        LPDIRECT3DVERTEXBUFFER9 g_pVB    = NULL;
        LPDIRECT3DVERTEXSHADER9 g_Shader = NULL;
        LPDIRECT3DPIXELSHADER9  g_PShader= NULL;
        LPDIRECT3DINDEXBUFFER9  g_pMIdx  = NULL;
        D3DXMATRIX              g_View;
        D3DXMATRIX              g_Proj;
        D3DXMATRIX              g_World;
        D3DLIGHT9               g_light;
        DWORD                   g_NumVtx;
        DWORD                   g_NumFaces;
    
        // この値でトーラスの色が決まる
        float                   g_cor[3]= { 1.0f, 0.2f, 0.0f };
        D3DXVECTOR3             ViewForm(0.0f, 0.0f, -5.0f);
        
  2. シェーダの作成です。
    D3DXAssembleShaderFromFile() でシェーダをアセンブルします。
    CreateVertexShader() でシェーダを作成します。
        HRESULT InitShader()
        {   LPD3DXBUFFER pCode;
            HRESULT      hr;
    
            // 頂点シェーダのアセンブル
            hr= D3DXAssembleShaderFromFile("nmap.vsh",NULL,NULL,0,&pCode,NULL);
            if (FAILED(hr))     return E_FAIL;
            hr= g_pDEV->CreateVertexShader((DWORD*)pCode->GetBufferPointer(),&g_Shader);
            SAFE_RELEASE(pCode);
            if (FAILED(hr))     return E_FAIL;
    
            // ピクセル・シェーダのアセンブル
            hr = D3DXAssembleShaderFromFile("PShader.ps",NULL,NULL,0,&pCode,NULL);
            if (FAILED(hr))     return E_FAIL;
            hr= g_pDEV->CreatePixelShader((DWORD*)pCode->GetBufferPointer(),&g_PShader);
            SAFE_RELEASE(pCode);
            if (FAILED(hr))     return E_FAIL;
            return S_OK;
        }
        
  3. デバイスの初期化です。
    MyDx3d Object をインスタンス化して、InitD3D() で3Dデバイスを初期化します。
    D3DXCreateTorus() でトーラスを生成します。
    GetVtxBuff() で VertexBuffer のポインタを取得します。
    InitShader() で頂点シェーダのソースコードを入力してシェーダを作成します。
        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(InitShader()))
            {   ERMSG("Init Vertex Error");  return  E_FAIL;  }
            return S_OK;
        }
        
  4. 取得したオブジェクトを開放します。
        VOID Cleanup()
        {
            SAFE_RELEASE(g_Shader);
            SAFE_RELEASE(g_PShader);
            SAFE_RELEASE(g_pVB);
            SAFE_RELEASE(g_pMIdx);
            SAFE_RELEASE(g_pMesh);
            SAFE_DELETE(mydx3d);
        }
        
  5. 時刻を取得してY軸を基点にして World 座標を回転します。
    SetLight() で g_light に光源の情報を設定します。
    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);
            //光源の設定
            mydx3d->SetLight();
            g_light= mydx3d->Light;
        }
        
  6. トーラスを描画する関数です。
    SetupMatrices() でマテリアルを設定します。
    SetVertexShader() で頂点シェーダを使用することを指示します。
    SetPixelShader() でピクセルシェーダを使用することを指示します。
    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);
                g_pDEV->SetPixelShader(g_PShader);
                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,(float*)&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に掛ける

ピクセルシェーダ(PShader.ps) のソースコードです。

mov oC0, v0 でディフューズ色を oC0 に設定します。
ps_2_0                  // バージョン2.0のピクセル・シェーダを使う

dcl v0                  // ディフューズ色「v0」を使う

mov oC0, v0             // 出力する

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

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

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