ホーム < ゲームつくろー! < プログラマブルシェーダ編

その11 極薄情報のID3DXFragmentLinkerを紐解け!:サンプルプログラム

 DirectXプログラマブルシェーダ編その11「極薄情報のID3DXFragmentLinkerを紐解け!」で説明した内容を踏まえたサンプルプログラムです。実行すると緑色の立方体が画面の真ん中でくるくると回ります。

スクリーンショット


 見た目には固定機能パイプラインの描画と変わりませんが、この描画はフラグメントを組み合わせた頂点シェーダを通しています。フラグメントは、

・ ワールド変換フラグメント
・ ビュー変換フラグメント
・ 射影変換フラグメント
・ ディフューズライトフラグメント

の4つを使っています。


 以下のプログラムは、Cube2.xというXファイルとフラグメントファイルであるSimple.fxをパスの通ったフォルダに置き、空のプロジェクトにコピペすることで完全に動きます必要なファイルはこちらからダウンロードできます(DXPSSmp_No11.lzh)。ダウンロードしたファイルのプロジェクトファイルを実行してもコンパイルできます。

 上からデバッグしていくと、何をしているか良くわかるように1つのソースで完結させています。今回のポイントになりそうなところは太文字で示されています。
(もしうまく動かないようでしたら掲示板にてご連絡下さい)

○ メイン関数

main.cpp
// ○×謹製ID3DXFragmentLinkerサンプルプログラム

#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("○×謹製ID3DXFragmentLinkerサンプルプログラム");

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam ){
   if( mes == WM_DESTROY || mes == WM_CLOSE ) { 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, (_TCHAR*)gName, NULL };
   if( !RegisterClassEx( &wcex ) )
      return 0;

   DWORD WndStyle = WS_OVERLAPPEDWINDOW & ~( WS_MAXIMIZEBOX | WS_SIZEBOX );
   RECT WndRect = { 0, 0, 640, 480 };
   AdjustWindowRect( &WndRect, WndStyle, false );

   if( !( hWnd = CreateWindow(
           gName, gName, WndStyle,
           CW_USEDEFAULT, 0,
           WndRect.right-WndRect.left, WndRect.bottom-WndRect.top,
       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, TRUE, D3DFMT_D24S8,
                                                                   0, D3DPRESENT_RATE_DEFAULT, D3DPRESENT_INTERVAL_DEFAULT }; 

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

   // フラグメントコンパイル
   ID3DXBuffer* g_pCompiledFragments;
   ID3DXBuffer* g_pError;
   D3DXGatherFragmentsFromFile( _T("Simple.fx"), NULL, NULL, 0, &g_pCompiledFragments, &g_pError );     // フラグメントコンパイル
   if( g_pError ) {
           char* c = (char*)g_pError->GetBufferPointer();
           MessageBoxA( NULL, c, 0, 0 );
           return 0;
   }

   // リンカにフラグメントを登録
   ID3DXFragmentLinker* g_pFragmentLinker = 0;
   D3DXCreateFragmentLinker( g_pD3DDev, 0, &g_pFragmentLinker );
   g_pFragmentLinker->AddFragments( (DWORD*)g_pCompiledFragments->GetBufferPointer() );

   // フラグメントハンドル取得
   D3DXHANDLE fragmentHandle[4];
   fragmentHandle[0] = (D3DXHANDLE)g_pFragmentLinker->GetFragmentHandleByName("WorldTransformFragment");
   fragmentHandle[1] = (D3DXHANDLE)g_pFragmentLinker->GetFragmentHandleByName("ViewTransformFragment");
   fragmentHandle[2] = (D3DXHANDLE)g_pFragmentLinker->GetFragmentHandleByName("ProjTransformFragment");
   fragmentHandle[3] = (D3DXHANDLE)g_pFragmentLinker->GetFragmentHandleByName("DiffuseLightingFragment");

   // 頂点シェーダ作成
   ID3DXBuffer* g_pVertexShaderBuffer = 0;
   IDirect3DVertexShader9* g_pVertexShader = 0;
   g_pFragmentLinker->LinkShader( "vs_2_0", 0, fragmentHandle, 4, &g_pVertexShaderBuffer, 0 );
   g_pD3DDev->CreateVertexShader( (DWORD*)g_pVertexShaderBuffer->GetBufferPointer(), &g_pVertexShader );

   // グローバル変数テーブル取得
   LPD3DXCONSTANTTABLE g_pVertexConstTable;
   D3DXGetShaderConstantTable( (DWORD*)g_pVertexShaderBuffer->GetBufferPointer(), &g_pVertexConstTable );

   // 立方体オブジェクト生成
   ID3DXMesh* pCubeMesh;
   ID3DXBuffer* pCubeMaterials;
   DWORD CubeMaterialNum;
   if( FAILED( D3DXLoadMeshFromX( _T("Cube2.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pCubeMaterials, NULL, &CubeMaterialNum, &pCubeMesh) ) ){
      g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }
   D3DXComputeNormals( pCubeMesh, 0 );
   D3DXVECTOR4 DiffuseColor( 0.3f, 0.9f, 0.1f, 1.0f );  // 立方体の色

   // 立方体頂点変換行列初期化
   D3DXMATRIX World, View, Proj;                                // ワールド、ビュー、射影変換行列
   D3DXMATRIX CubeRot_X, CubeRot_Y;                             // 立方体回転行列

   // カメラ初期化
   D3DXVECTOR3 CameraPos( 0, 0, -50.0f );               // カメラ位置
   D3DXVECTOR3 TargetPos( 0, 0, 0 );                    // 被写体位置
   D3DXVECTOR3 CameraDirectional( TargetPos - CameraPos );      // カメラ向き
   D3DXMatrixLookAtLH( &View, &CameraPos, &TargetPos, &D3DXVECTOR3( 0, 1, 0 ) );
   D3DXMatrixPerspectiveFovLH( &Proj, D3DXToRadian( 45 ), 640.0f/480.0f, 1.0f, 10000.0f );

   // ライト初期化
   D3DXVECTOR3  LightPos( 0.0f, 0.0f, -1.0f );                          // ライト位置
   D3DXVECTOR3  LightTarget( 0.0f, 0.0f, 0.0f );                        // ライト被写体位置
   D3DXVECTOR3  LightDirection( LightTarget - LightPos );       // ライト向き


   ////////////////////////////
   // メッセージループ
   /////
   // ウィンドウ表示
   ShowWindow( hWnd, SW_SHOW );

   float angle = 0;
   do{
         if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ){ DispatchMessage( &msg ); }

         g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 40, 40, 80 ), 1.0f, 0 );
         g_pD3DDev->BeginScene();

         // 変数を更新
         D3DXMatrixIdentity( &World );
         D3DXMatrixScaling( &World, 2.0f, 2.0f, 2.0f );
         D3DXMatrixRotationX( &CubeRot_X, angle += 0.003f );
         D3DXMatrixRotationY( &CubeRot_Y, angle );
         World *= CubeRot_X * CubeRot_Y;
         g_pVertexConstTable->SetMatrix( g_pD3DDev, "g_World", &World );
         g_pVertexConstTable->SetMatrix( g_pD3DDev, "g_View", &View );
         g_pVertexConstTable->SetMatrix( g_pD3DDev, "g_Proj", &Proj );
         g_pVertexConstTable->SetFloatArray( g_pD3DDev, "g_Light", &LightDirection.x, 3 );
         g_pVertexConstTable->SetVector( g_pD3DDev, "g_DiffuseColor", &DiffuseColor );
         g_pVertexConstTable->SetFloatArray( g_pD3DDev, "g_CameraDirectional", &CameraDirectional.x, 3 );

         // 立方体描画
         g_pD3DDev->SetVertexShader( g_pVertexShader );        // フラグメントによる頂点シェーダ設定
         for( unsigned int i = 0; i < CubeMaterialNum; i++ ){
            pCubeMesh->DrawSubset( i ); 
         };

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

   // 終了処理
   g_pVertexConstTable->Release();
   g_pVertexShader->Release();
   g_pCompiledFragments->Release();
   g_pFragmentLinker->Release();
   pCubeMesh->Release();
   pCubeMaterials->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}


○ フラグメントシェーダ

Simple.fx
// 頂点シェーダフラグメント

// 製作者 : IKD
// HP     : ○×つくろ〜ドットこむ
//          (http://marupeke296.com)


float4x4        g_World;                                // ワールド行列
float4x4        g_View;                                 // ビュー行列
float4x4        g_Proj;                                 // 射影変換行列
float3          g_Light = float3( 1.0f, 1.0f, 1.0f );                   // ディレクショナルライトのワールド空間での方向
float3          g_CameraDirectional = float3( 0.2f, 0.3f, 0.4f );       // カメラの向き
float4          g_DiffuseColor = float4( 1.0f, 1.0f, 1.0f, 1.0f );      // 面の色


//----------------------------------
// 頂点変換フラグメント
//----------------------------------
void WorldTransform(
        in float4 LocalPos : POSITION,          // ローカル座標(入力値)
        out float4 vOutPos: r_POSITION,         // シェーダ共通頂点位置
        out float4 WorldPos : POSITION)         // ワールド変換座標(出力値)
{
        WorldPos = mul( LocalPos, g_World );
        vOutPos = WorldPos;
}
vertexfragment WorldTransformFragment = compile_fragment vs_2_0 WorldTransform();


//----------------------------------
// ビュー変換フラグメント
//----------------------------------
void ViewTransform(
        in float4 vInPos: r_POSITION,
        out float4 vOutPos: r_POSITION,
        out float4 ViewPos : POSITION)
{
        ViewPos = mul( vInPos, g_View );
        vOutPos = ViewPos;
}
vertexfragment ViewTransformFragment = compile_fragment vs_2_0 ViewTransform();


//----------------------------------
// 射影変換フラグメント
//----------------------------------
void ProjTransform(
        in float4 vInPos: r_POSITION,
        out float4 vOutPos: r_POSITION,
        out float4 ProjPos : POSITION)
{
        ProjPos = mul( vInPos, g_Proj );
        vOutPos = ProjPos;
}
vertexfragment ProjTransformFragment = compile_fragment vs_2_0 ProjTransform();


//---------------------------------------------
// ディフューズライティングフラグメント
//---------------------------------------------
void DiffuseLighting(
        in float3 LocalNormalVec : NORMAL,
        out float4 LightedColor : COLOR0)
{
        // ワールド空間にある法線とライトの方向からディフューズカラーを決定
        float3 Normal_World = normalize( mul( LocalNormalVec, g_World ) );
        float4 Diffuse_World = dot( Normal_World, -normalize( g_Light ) ) * g_DiffuseColor;
        Diffuse_World = clamp( Diffuse_World, 0.0f, 1.0f );
        
        // カメラから見たディフューズカラーに変換
        LightedColor = dot( Normal_World, -normalize( g_CameraDirectional ) ) * Diffuse_World;
}
vertexfragment DiffuseLightingFragment = compile_fragment vs_2_0 DiffuseLighting();