ホーム < ゲームつくろー! < プログラマブルシェーダ編 < ピクセルシェーダプログラムの基礎:サンプルプログラム


その5 ピクセルシェーダプログラムの基礎:サンプルプログラム

 DirectXプログラマブルシェーダ編「その4 ピクセルシェーダの流れを知ろう」及び「その5 ピクセルシェーダプログラムの基礎」で説明した内容を踏まえたサンプルプログラムです。実行すると長方形のポリゴンにテクスチャが張られ、そのテクスチャの彩度が変化し、モノクロ画像と原画像を行ったりきたりします。

スクリーンショット。彩度が変化しているのがわかる。ちなみに、この写真は旭山動物園のシロクマ。水に飛び込んだ瞬間をガラス越しに撮影。


 シンプルなプログラムですが、頂点シェーダの流れと組み方がしっかり収まっています。同様の手順を踏めば、オリジナルな頂点シェーダを実装することが可能です。


 以下のプログラムはTest.jpgという画像ファイルを用意して、空のプロジェクトにコピペすることで完全に動きます。上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。
(もしうまく動かないようでしたら掲示板にてご連絡下さい)

 Test.jpgの大きさは任意です。色合いが派手な画だと変化を感じやすいと思います。
 ピクセルシェーダのバージョンチェックを省いていますが、今時1.1をサポートしていないビデオカードは無いと思われますので、うまく動くと思います。何らかの理由で画面が出ずに終了してしまう場合は、どこかの生成がうまく行っていないはずなので、デバッグでソースを追ってみてください。

// ピクセルシェーダプログラムの基礎テストプログラム
// このプログラムに関する詳しい情報は
// ○×つくろ〜どっとコム「ゲームつくろ〜プログラマブルシェーダ編その4、その5」
// をご覧下さい。

#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>

TCHAR gName[100] = _T("ピクセルシェーダプログラムの基礎テストプログラム");


/// 頂点定義 ///
struct CUSTOMVTX
{
   float x, y, z;    // 頂点位置
   DWORD color;      // 頂点カラー
   float u, v;     // テクスチャ座標
};

#define CUSTOMFVF  D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1


/// ウィンドウプロシージャ ///
LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
   if(mes == WM_DESTROY) {PostQuitMessage(0); return 0;}
   return DefWindowProc(hWnd, mes, wParam, lParam);
}


/// エントリ ///
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
   // アプリケーションの初期化(ウィンドウ作成)
   MSG msg; HWND hWnd;
   WNDCLASSEX wcex ={sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL,
                                    (HBRUSH)(COLOR_WINDOW+1), NULL, (LPCWSTR)gName, NULL};
   if(!RegisterClassEx(&wcex)) return 0;
   if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
                                    NULL, NULL, hInstance, NULL)))
      return 0;

   // Direct3Dの初期化
   LPDIRECT3D9 g_pD3D;   LPDIRECT3DDEVICE9 g_pD3DDev;
   if( !(g_pD3D = Direct3DCreate9( D3D_SDK_VERSION )) ) return 0;
   D3DPRESENT_PARAMETERS d3dpp = {0,0,D3DFMT_UNKNOWN,0,D3DMULTISAMPLE_NONE,0,
                                                      D3DSWAPEFFECT_DISCARD,NULL,TRUE,0,D3DFMT_UNKNOWN,0,0}; 

   if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pD3DDev ) ) )
   {g_pD3D->Release(); return 0;}

   // 頂点の設定(小さな長方形)
   CUSTOMVTX v[]=
   {
      { -1.0f, +1.5f, 0.0f, 0xffffffff, 1.0f, 0.0f},
      { -1.0f, -1.5f, 0.0f, 0xffffffff, 1.0f, 1.0f}, 
      { +1.0f, +1.5f, 0.0f, 0xffffffff, 0.0f, 0.0f}, 
      { +1.0f, -1.5f, 0.0f, 0xffffffff, 0.0f, 1.0f}
   };

   // 頂点バッファ作成と頂点情報の書き込み
   IDirect3DVertexBuffer9* pVertex;
   void *pData;
   if(FAILED(g_pD3DDev->CreateVertexBuffer(sizeof(CUSTOMVTX)*4, D3DUSAGE_WRITEONLY, CUSTOMFVF,
                                                             D3DPOOL_MANAGED, &pVertex, NULL))){
                g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }
   if(FAILED(pVertex->Lock(0, sizeof(CUSTOMVTX)*4, (void**)&pData, 0))){
      g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }
   memcpy(pData, v, sizeof(CUSTOMVTX)*4);
   pVertex->Unlock();


   // ピクセルシェーダ命令配列定義
   // 彩度調節シェーダ
   // c1 : 彩度調節係数 (c, c, c, 0) (グレー)0.0f < c < 1.0f(原色)
   const char PxShader[] =
     "ps_1_1                                        \n"
     "def   c0, 0.2989f, 0.5866f, 0.1145f, 0.0f     \n"     // 彩度算出係数
     "tex   t0                                      \n"     // テクスチャ0番使用
     "dp3   r0, t0, c0                              \n"     // 彩度Y算出(r0.aに格納される)
     "mov   r1, r0.a                                \n"     // r1の各成分をYで埋める
     "lrp   r0, c1, t0, r1                          \n";    // 線形補間( t0 + c1*(Y-t0) )

   // シェーダ命令のコンパイル
   ID3DXBuffer *pShader;   // シェーダ命令格納バッファ
   ID3DXBuffer *pError;     // コンパイルエラー情報格納バッファ
   if( FAILED( D3DXAssembleShader(PxShader, sizeof(PxShader)-1, 0, NULL, 0, &pShader, &pError ))){
           pVertex->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   // シェーダハンドラの生成
   IDirect3DPixelShader9 *pShaderHandler;
   if( FAILED( g_pD3DDev->CreatePixelShader( (DWORD*)pShader->GetBufferPointer(), &pShaderHandler ))){
           pShader->Release(); pVertex->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   pShader->Release();             // シェーダ命令はもういらない
   if(pError) pError->Release();   // エラー情報はもういらない(NULLの時があるので判定が必要)

   // 変換行列の設定
   D3DXMATRIX mat, matView, matProj;
   D3DXMatrixIdentity( &mat );
   D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3(0,0,5), &D3DXVECTOR3(0,0,0), &D3DXVECTOR3(0,1,0) );
   D3DXMatrixPerspectiveFovLH( &matProj, 0.785398163f, 480.0f/640.0f, 0.1f, 10000.0f );

   // ピクセルシェーダに切り替え
   g_pD3DDev->SetPixelShader( pShaderHandler );

   // テクスチャの作成
   IDirect3DTexture9 *pTex;
   if( FAILED(D3DXCreateTextureFromFile( g_pD3DDev, _T("Test.JPG"), &pTex )) ){
        pShaderHandler->Release(); pVertex->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   // テクスチャステージ0番にテクスチャを設定
   g_pD3DDev->SetTexture( 0, pTex ) ;


   ShowWindow( hWnd, SW_SHOW );

   // メッセージ ループ
   float sc = 1.0f;        // 彩度調節係数
   float sgn = +1.0f;      // 切り替え符号
   float scale[4] = {1.0f, 1.0f, 1.0f, 0.0f};      // 彩度
   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}
      else{
          // Direct3Dの処理
          g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
          g_pD3DDev->BeginScene();
                                
             // 彩度計算(0.0f-1.0fの間を繰り返させているだけです)
             sc += 0.0025*sgn;
             if     (sc<=0){ sc=0.0f;  sgn=+1.0f;}
             else if(sc>=1){ sc=1.0f;  sgn=-1.0f;}
             else          {sc+=0.0025f*sgn;}
             scale[0] = scale[1] = scale[2] = sc;

             // 描画
             g_pD3DDev->SetPixelShaderConstantF(1, scale, 1);        // 彩度調節係数をGPUの定数レジスタc1に登録
             g_pD3DDev->SetTransform(D3DTS_WORLD, &mat);
             g_pD3DDev->SetTransform(D3DTS_VIEW, &matView);
             g_pD3DDev->SetTransform(D3DTS_PROJECTION, &matProj);
             g_pD3DDev->SetStreamSource(0, pVertex, 0, sizeof(CUSTOMVTX));
             g_pD3DDev->SetFVF( CUSTOMFVF );
             g_pD3DDev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);      // 描画ストリームスタート

         g_pD3DDev->EndScene();
         g_pD3DDev->Present( NULL, NULL, NULL, NULL );
        }
    }while(msg.message != WM_QUIT);

   pShaderHandler->Release();
   pVertex->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}