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

シェーダ編
その2 スクリーン座標にあるオブジェクトをマウスで指す方法:サンプルプログラム

 DirectXプログラマブルシェーダ編ピクセルシェーダ編「その2 スクリーン座標にあるオブジェクトをマウスで指す方法」で説明した内容を踏まえたサンプルプログラムです。実行すると3つの立方体が出現します。立方体の上にカーソルを置くと、その立方体の色のフォントに切り替わります。


サンプルスクリーンショット。画面には見えていませんが(543, 270)にカーソルがあります。

プログラム内にはオブジェクトの前後関係を判定してオブジェクトを識別するサーフェイスが1枚作成されています。このサーフェイスに描画されている色を出力するには、プログラム後半部分に定義してあるPlaneDrawFlagフラグをtrueにしてください。


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

/// オブジェクト前後判定サンプルプログラム

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

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

_TCHAR gName[100] = _T("オブジェクト前後判定サンプルプログラム");


// ウィンドウプロシージャ
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, (_TCHAR*)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,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 *pMaterials;
   DWORD NumMaterials;
   ID3DXMesh *pMesh;
   if(FAILED(D3DXLoadMeshFromX( _T("Cube2.x"), D3DXMESH_MANAGED, g_pD3DDev, NULL, &pMaterials, NULL, &NumMaterials, &pMesh))){
      g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   // ライト
   g_pD3DDev->LightEnable(0, true);

   ////////////////////////////////////////////////
   // サーフェイスの作成
   //   pSurface[0] : デバイスのバックバッファ
   //   pSurface[1] : オブジェクト判定バッファ
   IDirect3DSurface9 *pSurface[2];

   // デバイスのバックバッファを取得
   if(FAILED(g_pD3DDev->GetRenderTarget(0, &pSurface[0]))){
      pMaterials->Release(); pMesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   // バックバッファサーフェイスの属性を取得
   D3DSURFACE_DESC DevSufDesc;
   pSurface[0]->GetDesc( &DevSufDesc );

   // オブジェクト判定サーフェイスの新規作成
   if(FAILED(g_pD3DDev->CreateRenderTarget( DevSufDesc.Width, DevSufDesc.Height, DevSufDesc.Format, DevSufDesc.MultiSampleType, 0, true, &pSurface[1], NULL))){
      pMaterials->Release(); pSurface[0]->Release(); pMesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }

   /////////////////////////////////////////
   // ピクセルシェーダ設定
   //

   // ピクセルシェーダプログラム
   // レジスタc0の色でオブジェクトを塗りつぶします
   const char PxShader[] =
      "ps_1_1 \n"
      "mov      r0,   c0";    // 色をc0に決定

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

   // シェーダハンドラ
   IDirect3DPixelShader9 *pShaderHandler;
   if(FAILED(g_pD3DDev->CreatePixelShader( (DWORD*)pShader->GetBufferPointer(), &pShaderHandler ))){
      pShader->Release(); pMaterials->Release(); pSurface[0]->Release(); pSurface[1]->Release(); pMesh->Release(); g_pD3DDev->Release(); g_pD3D->Release(); return 0;
   }


   ////////////////////
   // DirectXフォント作成
   HFONT hFont;
   LOGFONT LF;
   ID3DXFont *pDXFont;
   hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
   GetObject( hFont, sizeof(LOGFONT), &LF);
   D3DXCreateFont(g_pD3DDev, 50, 0, 500, 0, 0,   LF.lfCharSet, LF.lfOutPrecision, LF.lfQuality, LF.lfPitchAndFamily, LF.lfFaceName, &pDXFont);

   // ウィンドウ表示
   ShowWindow( hWnd, SW_SHOW );



   ////////////////////////////
   // メッセージループ
   /////

   // 射影変換
   D3DXMATRIX Persp;
   D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(45), 640.0f/480.0f, 1.0f, 10000.0f);

   // ビュー変換行列
   D3DXMATRIX View;
   D3DXMatrixLookAtLH(   &View, &D3DXVECTOR3(0, 0, -2), &D3DXVECTOR3(0, 0, 0), &D3DXVECTOR3(0, 1, 0));

   D3DCOLORVALUE Diffuse;    // オブジェクトの色
   FLOAT ObjectColor[3][4] = {            // ピクセルシェーダに渡すオブジェクト番号
      {1.0f, 0.0f, 0.0f, 1.0f},
      {0.0f, 1.0f, 0.0f, 1.0f},
      {0.0f, 0.0f, 1.0f, 1.0f}
   };

   // 描画切り替えフラグ
   // このフラグをtrueにすると最前面オブジェクトの
   // 色情報が描画されます
   bool PlaneDrawFlag = false;

   FLOAT cnt = 0.0f;
   size_t i, s;
   do{
      Sleep(1);
      if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ){ DispatchMessage(&msg);}

      cnt+=1.0f;

      // サーフェースバッファのクリア
      g_pD3DDev->SetRenderTarget( 0, pSurface[1] );   // 前後判定用
      g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
      g_pD3DDev->SetRenderTarget( 0, pSurface[0] );   // 描画バックバッファ
      g_pD3DDev->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,60), 1.0f, 0 );
      g_pD3DDev->BeginScene();

      // オブジェクトを3つ描画
      for(s=0; s<3; s++)
      {
         float rad = D3DXToRadian( s*50 );  // 適当な角度

         // オブジェクトの色を設定
         memcpy( &Diffuse, ObjectColor[s], sizeof(FLOAT)*4 );

         // 行列登録
         D3DXMATRIX World, RotX, RotY, Ofs;
         D3DXMatrixIdentity( &World );
         D3DXMatrixRotationX( &RotX, D3DXToRadian( cnt*(s+1)/3 ) );
         D3DXMatrixRotationY( &RotY, D3DXToRadian( cnt*(s+1)/2 ) );
         D3DXMatrixTranslation( &Ofs, 8*sin(rad+cnt/100/(s+1)), 12*cos(rad+cnt/100/(s+1)), 50*(1+sin(rad)));
         D3DXMatrixMultiply( &World, &World, &RotX );
         D3DXMatrixMultiply( &World, &World, &RotY );
         D3DXMatrixMultiply( &World, &World, &Ofs );
         g_pD3DDev->SetTransform(D3DTS_WORLD, &World);
         g_pD3DDev->SetTransform(D3DTS_VIEW, &View);
         g_pD3DDev->SetTransform(D3DTS_PROJECTION, &Persp);

         /////////////////////////////////////////////////
         // デバイスの描画バッファにオブジェクト描画
         if( PlaneDrawFlag == true ){
            // ベタ塗り描画する
            g_pD3DDev->SetPixelShader( pShaderHandler );
            g_pD3DDev->SetPixelShaderConstantF(0, ObjectColor[s], 1);
         }
         else
            // 通常描画
            g_pD3DDev->SetPixelShader( NULL );

         g_pD3DDev->SetRenderTarget( 0, pSurface[0] );   // ターゲットを変更
         for(i=0; i<NumMaterials; i++){
            D3DXMATERIAL *mtrl = (D3DXMATERIAL*)(pMaterials->GetBufferPointer());
            mtrl->MatD3D.Diffuse = Diffuse;
            g_pD3DDev->SetMaterial( &mtrl->MatD3D );
            pMesh->DrawSubset(i); 
         }

         /////////////////////////////////////////////////////////////
         // オブジェクトカラーでオブジェクト判定サーフェイスに描画
         g_pD3DDev->SetRenderTarget( 0, pSurface[1] );    // ターゲットを変更
         g_pD3DDev->SetPixelShaderConstantF(0, ObjectColor[s], 1);   // ベタ塗りの色をピクセルシェーダに通達
         g_pD3DDev->SetPixelShader( pShaderHandler );     // ピクセルシェーダを登録
         for(i=0; i<NumMaterials; i++){
            D3DXMATERIAL *mtrl = (D3DXMATERIAL*)(pMaterials->GetBufferPointer());
            g_pD3DDev->SetMaterial( &mtrl->MatD3D );
            pMesh->DrawSubset(i); 
         }
      }

      ////////////////////////////////////
      // カーソル位置のオブジェクト判定
      //

      // 現在のカーソルの位置を取得
      D3DLOCKED_RECT D3DLockRect;
      POINT point;
      GetCursorPos( &point );
      ScreenToClient( hWnd, &point);
      RECT LockR = {point.x, point.y, point.x+1, point.y+1};  // カーソル位置のみをロック
      
      // 前後判定用サブサーフェイスをロック
      if(SUCCEEDED( pSurface[1]->LockRect( &D3DLockRect, &LockR, D3DLOCK_READONLY)))
      {
         DWORD *tmp = (DWORD*)D3DLockRect.pBits;
         DWORD color = *tmp;   // 色情報取得
         _TCHAR str[128];      // 文字出力用配列
         _stprintf_s( str, 128,_T("%d, %d, A=%d, R=%d, G=%d, B=%d"),
            point.x, point.y, (color&0xff000000)>>24, (color&0x00ff0000)>>16, (color&0x0000ff00)>>8, (color&0x000000ff) );

         // 文字出力
         g_pD3DDev->SetRenderTarget( 0, pSurface[0] );   // デバイスのバックバッファに切り替え
         RECT R;
         SetRect( &R, 0,0,0,0 );
         pDXFont->DrawText(NULL, str, -1, &R, DT_LEFT | DT_CALCRECT, color);
         pDXFont->DrawText(NULL, str, -1, &R, DT_LEFT, color | 0xff000000);

         // サーフェイスアンロック
         pSurface[1]->UnlockRect();
      }

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

   pShaderHandler->Release();
   pShader->Release();
   pSurface[0]->Release();
   pSurface[1]->Release();
   pMesh->Release();
   pMaterials->Release();
   g_pD3DDev->Release();
   g_pD3D->Release();

   return 0;
}