Microsoft Visual C++ 2010 Express
 Microsoft DirectX SDK (February 2010)
 Direct3D 11.0

■Direct3D 11.0 デバック用フォント描画 Prev Top Next
関連ページ:Direct3D 11.0 初期化

無料レンタルサーバー
2010/07/08:
  1. ファイルの共有化のためにインクルードパスを変更
  2. 一部の環境でウィンドウモードに変更したとき何も表示されなくなる現象への対応
  3. コンパイル済みシェーダーのヘッダファイルを変名
2010/08/07:
  1. ポリゴンの表面の回転向きを反時計回りに変更( FBX SDK に対応するため )
2011/05/10:
  1. 入力レイアウトの作成処理を D3D11USER クラス内で行うように修正

今回はフォント描画です。タイトルにも書いてますが、デバック用に使用するためのフォント描画です。はっきりいってデバック用にしか使用できません。 そこを理解したうえで必要だと思う人だけ(いないと思うけど)このページを参照してください。

さてフォント描画する手順ですが、いくつか方法があります。
その方法のひとつにお手軽に実装できるものとして ID3DXFont インターフェースがあります。まあ処理が重いことでも有名ですが、 デバック用に使用するのであればこれで十分です。しかしなんと Direct3D 11 に存在しません。 Microsoft の思惑は知りませんが、ないのでは仕方がないので別の方法を探すことにします。

でいろいろ調査したり試した結果、最終的に今回の方法になりました。
簡単に説明すると、あらかじめ用意したフォント画像データを使用してフォントを描画するという方法です。 事前に用意したフォントをテクスチャーとして使用するため、用意したフォントしか使用できない問題があります。 英語圏の場合これで問題ないですが、日本語のような膨大な量のフォントを扱う場合、事前にすべてのパターンのフォントを用意するのは不可能です。 ですが今回は、デバック用に使用するフォント描画を実装するのが目的なのでこの方法で問題なしとします。
サポートするフォントは半角英数字と半角の記号、つまり Shift-JIS 文字コードの0x20(半角スペース)〜0x7e(~)までとします。 この方法はDXUTで用意されていますが、自分で実装しました。

さてこの中途半端な方法を採用した理由はというと、コメントは控えさせていただきます。いや調査段階で他のサイト様の方法を参照したりしたんですが、 やんごとなき事情により保留としました。これについてコメント書くと誤解を生む危険性があるので書きませんが、けっして参考にしたサイト様の記事が悪いのではないです。 悪いのはむしろ Microsoft だあ。

ではソース。今回は DX11User.h を修正してます。まずはDirect3D 11.0 初期化を参照してください。

---DebugFontUser.h---


#ifndef DEBUGFONTUSER_H
#define DEBUGFONTUSER_H

class CDebugFont
{
private:
   // 頂点定義
   typedef struct _FONT_VERTEX
   {
      D3DXVECTOR3 pos;     // 頂点の座標
      D3DXCOLOR   color;   // 頂点カラー( 白固定 )
      D3DXVECTOR2 texel;   // テクセル座標
   }FONT_VERTEX;

   // 定数バッファの定義
   typedef struct _CBUFFER
   {
      D3DXMATRIX  matWVP;  // フォントの表示位置、サイズを指定するための行列
      D3DXVECTOR4 Offset;  // テクスチャー上のテクセル位置を指定するオフセット値
   }CBUFFER;

   ID3D11Buffer*             m_pVertexBuffer;
   ID3D11VertexShader*       m_pVertexShader;
   ID3D11InputLayout*        m_pLayout;
   ID3D11PixelShader*        m_pPixelShader;
   ID3D11ShaderResourceView* m_pResourceView;
   ID3D11Buffer*             m_pConstantBuffers;
   ID3D11SamplerState*       m_pSamplerState;
   ID3D11BlendState*         m_pBlendState;
   ID3D11DepthStencilState*  m_pDepthStencilState;

   float m_FontWidth;   // ポリゴンの横幅( テクスチャーのサイズから取得する )
   float m_FontHeight;  // ポリゴンの縦幅( テクスチャーのサイズから取得する )
   float m_FontCnt;     // フォント数

   // FPS計算用変数
   DWORD m_Timer;         // 前回時間
   DWORD m_Frame;         // FPS計測値
   DWORD m_FrameDisplay;  // 表示用のFPS計測値

public:
   CDebugFont();
   virtual ~CDebugFont();
   void Invalidate();

   // 作成処理
   // フォントサイズ( 実際はポリゴンサイズ )の単位は 1 を設定したときスクリーンいっぱいに描画される感じ.
   // フォントサイズは作成時に設定した値を後で変更できない.
   HRESULT Create( ID3D11Device* pD3DDevice, float FontWidth, float FontHeight );
   // デバック用のテキストを描画する
   // 文字列の最後は必ず \n を指定すること.
   // 文字は半角英数字と半角記号のみサポートする.それ以外の文字をパラメータに渡した場合の動作は保証しない.
   // フォントの基準位置はスクリーン上の左上隅.( 2, 2 )で右下隅という微妙な仕様.
   HRESULT RenderDebugText( ID3D11DeviceContext* pD3DDeviceContext, char* pStr, float X, float Y );
   // FPS 出力
   HRESULT RenderFPS(  ID3D11DeviceContext* pD3DDeviceContext, float X, float Y );
};

#endif

自作クラスを用意しました。自作クラスの D3D11USER とは依存しない設計にしました。 仕様、注意点はコメントに記述してます。参照してください。

---DebugFontUser.cpp---


#include "DX11User.h"
#include "D3D11User.h"
#include "DebugFontUser.h"
#include "DebugFontUser_vs.h"
#include "DebugFontUser_ps.h"

CDebugFont::CDebugFont()
{
   m_pVertexBuffer = NULL;
   m_pVertexShader = NULL;
   m_pLayout = NULL;
   m_pPixelShader = NULL;
   m_pResourceView = NULL;
   m_pConstantBuffers = NULL;
   m_pSamplerState = NULL;
   m_pBlendState = NULL;
   m_pDepthStencilState = NULL;

   Invalidate();
}

void CDebugFont::Invalidate()
{
   SAFE_RELEASE( m_pDepthStencilState );
   SAFE_RELEASE( m_pBlendState );
   SAFE_RELEASE( m_pSamplerState );
   SAFE_RELEASE( m_pConstantBuffers );
   SAFE_RELEASE( m_pResourceView );
   SAFE_RELEASE( m_pPixelShader );
   SAFE_RELEASE( m_pLayout );
   SAFE_RELEASE( m_pVertexShader );
   SAFE_RELEASE( m_pVertexBuffer );

   m_FontWidth = 0;
   m_FontHeight = 0;
   m_FontCnt = 95.0f;

   m_Frame = 0;
   m_FrameDisplay = 0;
}

CDebugFont::~CDebugFont()
{
   Invalidate();
}

HRESULT CDebugFont::Create( ID3D11Device* pD3DDevice, float FontWidth, float FontHeight )
{
   HRESULT hr = E_FAIL;

   Invalidate();

   // ファイルからシェーダーリソースビューを作成する
   D3DX11_IMAGE_LOAD_INFO info;
   ::ZeroMemory( &info, sizeof( D3DX11_IMAGE_LOAD_INFO ) );
   info.Width = D3DX11_DEFAULT; 
   info.Height = D3DX11_DEFAULT; 
   info.Depth = D3DX11_DEFAULT; 
   info.FirstMipLevel = D3DX11_DEFAULT;          // テクスチャーの最高解像度のミップマップ レベル。
   info.MipLevels = 1;                           // ミップマップ数を1個のみ。
   info.Usage = D3D11_USAGE_DEFAULT; 
   info.BindFlags = D3D11_BIND_SHADER_RESOURCE;
   info.CpuAccessFlags = 0;
   info.MiscFlags = 0;
   info.Format = DXGI_FORMAT_FROM_FILE;
   info.Filter = D3DX11_FILTER_POINT;            // テクスチャー読み込み時に使用するフィルター
   info.MipFilter = D3DX11_FILTER_POINT;         // ミップマップ作成時に使用するフィルター
   info.pSrcInfo = NULL;
   hr = D3DX11CreateShaderResourceViewFromFile( pD3DDevice, _T("res\\Font.dds"), &info, NULL, &m_pResourceView, NULL );
   if( FAILED( hr ) ) goto EXIT;

   // 頂点バッファ作成
   m_FontWidth  = FontWidth;      // ポリゴンの横幅のスケーリング値
   m_FontHeight = FontHeight;     // ポリゴンの縦幅のスケーリング値
   float TU = 1.0f / m_FontCnt;   // テクスチャー上での1フォントのサイズ
   // 頂点のデータ
   FONT_VERTEX v[] = {
      //D3DXVECTOR3( -m_FontWidth,  m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( 0,  0 ),
      //D3DXVECTOR3(  m_FontWidth,  m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( TU, 0 ),
      //D3DXVECTOR3( -m_FontWidth, -m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( 0,  1 ),
      //D3DXVECTOR3(  m_FontWidth, -m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( TU, 1 )
      D3DXVECTOR3(  m_FontWidth,  m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( TU, 0 ),
      D3DXVECTOR3( -m_FontWidth,  m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2(  0, 0 ),
      D3DXVECTOR3(  m_FontWidth, -m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2( TU, 1 ),
      D3DXVECTOR3( -m_FontWidth, -m_FontHeight, 0 ), 0xFFFFFFFF, D3DXVECTOR2(  0, 1 )
   };
   // バッファー リソース
   D3D11_BUFFER_DESC BufferDesc;
   ::ZeroMemory( &BufferDesc, sizeof( BufferDesc ) );
   BufferDesc.ByteWidth             = sizeof( v );               // バッファサイズ
   BufferDesc.Usage                 = D3D11_USAGE_DEFAULT;       // リソース使用法を特定する
   BufferDesc.BindFlags             = D3D11_BIND_VERTEX_BUFFER;  // バッファの種類
   BufferDesc.CPUAccessFlags        = 0;                         // CPU アクセス
   BufferDesc.MiscFlags             = 0;                         // その他のフラグも設定しない
   // サブリソース( 初期値 )
   D3D11_SUBRESOURCE_DATA resource;
   resource.pSysMem = (void*)v;
   resource.SysMemPitch = 0;
   resource.SysMemSlicePitch = 0;
   // バッファを作成する
   hr = pD3DDevice->CreateBuffer( &BufferDesc, &resource, &m_pVertexBuffer );
   if( FAILED( hr ) ) goto EXIT;

   // 定数バッファを作成
   ::ZeroMemory( &BufferDesc, sizeof( BufferDesc ) );
   BufferDesc.ByteWidth             = sizeof( CBUFFER );         // バッファサイズ
   BufferDesc.Usage                 = D3D11_USAGE_DYNAMIC;       // リソース使用法を特定する
   BufferDesc.BindFlags             = D3D11_BIND_CONSTANT_BUFFER;// バッファの種類
   BufferDesc.CPUAccessFlags        = D3D11_CPU_ACCESS_WRITE;    // CPU アクセス
   BufferDesc.MiscFlags             = 0;                         // その他のフラグも設定しない
   // バッファを作成する
   hr = pD3DDevice->CreateBuffer( &BufferDesc, NULL, &m_pConstantBuffers );
   if( FAILED( hr ) ) goto EXIT;

   // 頂点シェーダー作成
   hr = pD3DDevice->CreateVertexShader( g_Font_VS_Main, sizeof( g_Font_VS_Main ), NULL, &m_pVertexShader );
   if( FAILED( hr ) ) goto EXIT;

   // 入力レイアウトを作成する。
   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT,    0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "COLOR"   , 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,       0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };
   hr = pD3DDevice->CreateInputLayout( layout, _countof( layout ), g_Font_VS_Main, sizeof( g_Font_VS_Main ), &m_pLayout );
   if( FAILED( hr ) ) goto EXIT;

   // ピクセルシェーダー作成
   hr = pD3DDevice->CreatePixelShader( g_Font_PS_Main, sizeof( g_Font_PS_Main ), NULL, &m_pPixelShader );
   if( FAILED( hr ) ) goto EXIT;

   // サンプラーステートを作成する
   D3D11_SAMPLER_DESC samplerDesc;
   ::ZeroMemory( &samplerDesc, sizeof( samplerDesc ) );
   samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;  // サンプリング時に使用するフィルタ。ここでは異方性フィルターを使用する。
   samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある u テクスチャー座標の描画方法
   samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある v テクスチャー座標の描画方法
   samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある w テクスチャー座標の描画方法
   samplerDesc.MipLODBias = 0;                            // 計算されたミップマップ レベルからのバイアス
   samplerDesc.MaxAnisotropy = 0;                         // サンプリングに異方性補間を使用している場合の限界値。有効な値は 1 〜 16 。
   samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;  // 比較オプション。
   ::CopyMemory( samplerDesc.BorderColor, &D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 0.0f ), sizeof( D3DXVECTOR4 ) ); // 境界色
   samplerDesc.MinLOD = 0;                                // アクセス可能なミップマップの下限値
   samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;                // アクセス可能なミップマップの上限値
   hr = pD3DDevice->CreateSamplerState( &samplerDesc, &m_pSamplerState );
   if( FAILED( hr ) ) goto EXIT;

   // ブレンドステートを作成する( ddsファイルを使用するため線形合成 )
   // D3D11_BLEND_DESC
   D3D11_BLEND_DESC BlendDesc;
   ::ZeroMemory( &BlendDesc, sizeof( BlendDesc ) );
   BlendDesc.AlphaToCoverageEnable = FALSE;
   BlendDesc.IndependentBlendEnable = FALSE;
   BlendDesc.RenderTarget[0].BlendEnable = TRUE;
   BlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
   BlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
   BlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
   BlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
   BlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
   BlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
   BlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
   // ID3D11Device::CreateBlendState
   hr = pD3DDevice->CreateBlendState( &BlendDesc, &m_pBlendState );
   if( FAILED( hr ) ) goto EXIT;

   // 深度ステンシルステートを作成する
   // D3D11_DEPTH_STENCIL_DESC
   D3D11_DEPTH_STENCIL_DESC ddsDesc;
   ::ZeroMemory( &ddsDesc, sizeof( ddsDesc ) );
   ddsDesc.DepthEnable = FALSE;                                     // 深度テストを使用しない
   ddsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
   ddsDesc.DepthFunc = D3D11_COMPARISON_LESS;
   ddsDesc.StencilEnable = FALSE;
   // ID3D11Device::CreateDepthStencilState
   hr = pD3DDevice->CreateDepthStencilState( &ddsDesc, &m_pDepthStencilState );
   if( FAILED( hr ) ) goto EXIT;

   hr = S_OK;
EXIT:

   return hr;
}

HRESULT CDebugFont::RenderDebugText( ID3D11DeviceContext* pD3DDeviceContext, char* pStr, float X, float Y )
{
   HRESULT hr = E_FAIL;

   // 頂点シェーダーをデバイスに設定する。
   pD3DDeviceContext->VSSetShader( m_pVertexShader, NULL, 0 );

   // ハルシェーダーを無効にする。
   pD3DDeviceContext->HSSetShader( NULL, NULL, 0 );

   // ドメインシェーダーを無効にする。
   pD3DDeviceContext->DSSetShader( NULL, NULL, 0 );

   // ジオメトリシェーダーを無効にする。
   pD3DDeviceContext->GSSetShader( NULL, NULL, 0 );

   // ピクセルシェーダーをデバイスに設定する。
   pD3DDeviceContext->PSSetShader( m_pPixelShader, NULL, 0 );
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );
   pD3DDeviceContext->PSSetShaderResources( 0, 1, &m_pResourceView );

   // コンピュートシェーダーを無効にする。
   pD3DDeviceContext->CSSetShader( NULL, NULL, 0 );

   // ブレンドステート( ddsファイルを使用するため線形合成 )
   // ID3D11DeviceContext::OMSetBlendState
   pD3DDeviceContext->OMSetBlendState( m_pBlendState, 0, 0xffffffff );

   // 深度ステンシルステートをセット.最前面に描画するため深度テスト無効
   // ID3D11DeviceContext::OMSetDepthStencilState
   pD3DDeviceContext->OMSetDepthStencilState( m_pDepthStencilState, 0 );

   // 頂点バッファ設定
   UINT stride = sizeof( FONT_VERTEX );
   UINT offset = 0;
   pD3DDeviceContext->IASetVertexBuffers( 0, 1, &m_pVertexBuffer, &stride, &offset );

   // レイアウト設定
   pD3DDeviceContext->IASetInputLayout( m_pLayout );

   // プリミティブタイプを設定
   pD3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // 描画
   D3D11_MAPPED_SUBRESOURCE mappedResource;
   CBUFFER* cBuffer;
   D3DXMATRIX matTranslation;
   // 文字数分ループ
   for( int i=0; i<(int)strlen( pStr ); i++ )
   {
      hr = pD3DDeviceContext->Map( m_pConstantBuffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
      if( FAILED( hr ) ) goto EXIT;
      cBuffer = (CBUFFER*)(mappedResource.pData);
      // 行列
      D3DXMatrixTranslation(
         &matTranslation,
         -1.0f + m_FontWidth * ( 1.0f + 2.0f * (float)i ) + X,
         1.0f - m_FontHeight - Y,
         0.0f
         );
      D3DXMatrixTranspose( &(cBuffer->matWVP), &matTranslation );
      // テクセル
      cBuffer->Offset.x = (float)( pStr[i] - 32 ) / m_FontCnt;// TU
      cBuffer->Offset.y = 0.0f;                               // TV
      cBuffer->Offset.z = 0.0f;                               // 未使用
      cBuffer->Offset.w = 0.0f;                               // 未使用
      pD3DDeviceContext->Unmap( m_pConstantBuffers, 0 );

      // 頂点シェーダーに定数バッファを設定する。
      pD3DDeviceContext->VSSetConstantBuffers( 0, 1, &m_pConstantBuffers );

      // ピクセルシェーダーに定数バッファを設定する。
      pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers );

      // 描画
      pD3DDeviceContext->Draw( 4, 0 );
   }
 
   hr = S_OK;
EXIT:
   return hr;
}

// FPS 出力
HRESULT CDebugFont::RenderFPS( ID3D11DeviceContext* pD3DDeviceContext, float X, float Y )
{
   HRESULT hr = E_FAIL;

   DWORD now = ::timeGetTime();
   char s[20];

   if( now - m_Timer > 1000 )
   {
      m_Timer = now;
      m_FrameDisplay = m_Frame;
      m_Frame = 0;
   }
   else
      m_Frame++;
   sprintf_s( s, "FPS : %d", m_FrameDisplay );

   // フォント描画
   hr = RenderDebugText( pD3DDeviceContext, s, X, Y );

   hr = S_OK;
//EXIT:
   return hr;
}

フォント描画で使用するシェーダーはあらかじめコンパイルします。 コンパイルして作成されたヘッダファイルをインクルードして使用します。 コンパイル時のバージョンは 頂点シェーダーに vs_4_0_level_9_1 、ピクセルシェーダーに ps_4_0_level_9_1 を指定するとよいでしょう。 これだと Direct3D 9 モードでも実行できるようになります。

あと1点注意。描画関数を実行するとシェーダーやステートを変更しますが、戻すための処理をいれていません。 したがって、CDebugFont クラスをサンプルのまま使用する場合は、描画関数を使用した後の状態を意識する必要があります。

---DebugFontUser.hlsl---


cbuffer cbMatrixWVP : register( b0 )
{
   // 列優先行列
   column_major  float4x4 g_matWVP : packoffset( c0 );
   // テクセルのオフセット値
   float4 g_Offset : packoffset( c4 );
};

// テクスチャー
Texture2D g_Tex : register( t0 );

// サンプラーステート
SamplerState  g_Sampler : register( s0 );

// 頂点シェーダーの入力パラメータ
struct VS_IN
{
   float3 pos   : POSITION;   // 頂点座標
   float4 color : COLOR;      // 頂点カラー
   float2 texel : TEXCOORD;   // テクセル
};

// 頂点シェーダーの出力パラメータ
struct VS_OUT
{
   float4 pos   : SV_POSITION;
   float4 color : COLOR0;
   float2 texel : TEXCOORD0;
};

// 頂点シェーダー
VS_OUT Font_VS_Main( VS_IN In )
{
   VS_OUT Out;

   Out.pos   = mul( float4( In.pos, 1.0f ), g_matWVP );
   Out.color = In.color;
   Out.texel = In.texel;

   return Out;
}

// ピクセルシェーダ
float4 Font_PS_Main( VS_OUT In ) : SV_TARGET
{
   return g_Tex.Sample( g_Sampler, In.texel + g_Offset.xy ) * In.color;
}

解説はいらんだろ。

---main.cpp---


#include "../../USER/DX11User.h"
#include "../../USER/D3D11User.h"
#include "../../USER/DebugFontUser.h"
// コンパイル済み頂点シェーダー
#include "../../USER/HLSL/SimpleHLSL03_vs.h"
// コンパイル済みピクセルシェーダー
#include "../../USER/HLSL/SimpleHLSL03_ps.h"

// シェーダーオブジェクトを作成するとき、ファイルから読むか、メモリから読むかを切り替える
#if defined(DEBUG) || defined(_DEBUG)
#define UNCOMPILED_SHADER     // ファイルを読み込んでコンパイルする
#endif

// アプリケーション名
TCHAR* AppName = _T("DX11_Tutrial05");

// Direct3D関連の自作クラス
D3D11USER* g_pD3D11User = NULL;

// デバッグ専用のテキスト描画する自作クラス
CDebugFont* g_pDebugFontUser = NULL;

// 頂点バッファ
ID3D11Buffer* g_pVertexBuffer = NULL;
// インデックスバッファ
ID3D11Buffer* g_pIndexBuffer = NULL;
// 頂点シェーダー
ID3D11VertexShader* g_pVertexShader = NULL;
// 入力レイアウト
ID3D11InputLayout* g_pLayout = NULL;
// ピクセルシェーダー
ID3D11PixelShader* g_pPixelShader = NULL;
// シェーダーリソースビュー
ID3D11ShaderResourceView* g_pSRView = NULL;
// サンプラーステート
ID3D11SamplerState* g_pSamplerState = NULL;
// ブレンドステート
ID3D11BlendState* g_pBlendState = NULL;
// 深度ステンシルステート
ID3D11DepthStencilState*  g_pDepthStencilState = NULL;

// 頂点定義
struct VERTEX
{
   // 頂点座標
   D3DXVECTOR3   pos;
   // 頂点カラー
   D3DXCOLOR   color;
   // テクセル
   D3DXVECTOR2 texel;
};

// 節電モードの制御に使用する変数。
bool Activate = true;    // ウィンドウがアクティブか
bool StandBy = false;    // スタンバイ状態か

bool ScreenShot = false; // スクリーンショットを作成するかフラグ

// リソースの初期化
HRESULT Init()
{
   HRESULT hr = E_FAIL;

   // D3DX11_IMAGE_LOAD_INFO
   D3DX11_IMAGE_LOAD_INFO info;
   // 頂点座標と頂点カラーを設定
   VERTEX v[] = {
                  D3DXVECTOR3(  80.0f, 200.0f, 0.0f ), 0xFFFF0000, D3DXVECTOR2( 1.0f, 0.0f ),
                  D3DXVECTOR3( -80.0f, 200.0f, 0.0f ), 0xFF00FF00, D3DXVECTOR2( 0.0f, 0.0f ),
                  D3DXVECTOR3(  80.0f,  10.0f, 0.0f ), 0xFF0000FF, D3DXVECTOR2( 1.0f, 1.0f ),
                  D3DXVECTOR3( -80.0f,  10.0f, 0.0f ), 0xFFFF00FF, D3DXVECTOR2( 0.0f, 1.0f ),
               };

   // 頂点バッファを作成する
   hr = g_pD3D11User->CreateVertexBuffer( &g_pVertexBuffer, v, sizeof( v ), 0 );
   if( FAILED( hr ) ) goto EXIT;

   // インデックスバッファを設定
   UINT Index[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
   // インデックスバッファを作成する。
   hr = g_pD3D11User->CreateIndexBuffer( &g_pIndexBuffer, Index, sizeof( Index ), 0 );
   if( FAILED( hr ) ) goto EXIT;

   // 頂点シェーダーを作成する

   D3D11_INPUT_ELEMENT_DESC layout[] = {
         { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT,    0,  0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "COLOR"   , 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,       0, 28, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };

#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreateVertexShaderFromMemory( &g_pVertexShader, g_VS_Main, sizeof( g_VS_Main ), &g_pLayout, layout, _countof( layout ) );
   if( FAILED( hr ) ) goto EXIT;
#else
   hr = g_pD3D11User->CreateVertexShaderFromFile( &g_pVertexShader,
                                                  _T("../../USER/HLSL/SimpleHLSL03.hlsl"), "VS_Main", "vs_4_0",
                                                  &g_pLayout, layout, _countof( layout ) );
   if( FAILED( hr ) ) goto EXIT;
#endif

   // ピクセルシェーダーを作成する
#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreatePixelShaderFromMemory( &g_pPixelShader, g_PS_Main, sizeof( g_PS_Main ) );
   if( FAILED( hr ) ) goto EXIT;
#else
   hr = g_pD3D11User->CreatePixelShaderFromFile( &g_pPixelShader, _T("../../USER/HLSL/SimpleHLSL03.hlsl"), "PS_Main", "ps_4_0" );
   if( FAILED( hr ) ) goto EXIT;
#endif

   // ファイルからシェーダーリソースビューを作成する
   ::ZeroMemory( &info, sizeof( D3DX11_IMAGE_LOAD_INFO ) );
   info.Width = D3DX11_DEFAULT; 
   info.Height = D3DX11_DEFAULT; 
   info.Depth = D3DX11_DEFAULT; 
   info.FirstMipLevel = D3DX11_DEFAULT;          // テクスチャーの最高解像度のミップマップ レベル。実際の使用方法不明。
   info.MipLevels = 0;                           // ミップマップ数。0 または D3DX11_DEFAULT を使用するとすべてのミップマップ チェーンを作成する。
   info.Usage = D3D11_USAGE_DEFAULT; 
   info.BindFlags = D3D11_BIND_SHADER_RESOURCE;
   info.CpuAccessFlags = 0;
   info.MiscFlags = 0;
   info.Format = DXGI_FORMAT_FROM_FILE;
   info.Filter = D3DX11_FILTER_POINT;            // テクスチャー読み込み時に使用するフィルター
   info.MipFilter = D3DX11_FILTER_POINT;         // ミップマップ作成時に使用するフィルター
   info.pSrcInfo = NULL;
   hr = D3DX11CreateShaderResourceViewFromFile( g_pD3D11User->m_D3DDevice, _T("res\\01.jpg"), &info, NULL, &g_pSRView, NULL );
   if( FAILED( hr ) ) goto EXIT;

   // サンプラーステートの設定
   D3D11_SAMPLER_DESC samplerDesc;
   samplerDesc.Filter = D3D11_FILTER_ANISOTROPIC;         // サンプリング時に使用するフィルタ。ここでは異方性フィルターを使用する。
   samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある u テクスチャー座標の描画方法
   samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある v テクスチャー座標
   samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;     // 0 〜 1 の範囲外にある w テクスチャー座標
   samplerDesc.MipLODBias = 0;                            // 計算されたミップマップ レベルからのバイアス
   samplerDesc.MaxAnisotropy = 16;                        // サンプリングに異方性補間を使用している場合の限界値。有効な値は 1 〜 16 。
   samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;  // 比較オプション。
   ::CopyMemory( samplerDesc.BorderColor, &D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 0.0f ), sizeof( D3DXVECTOR4 ) ); // 境界色
   samplerDesc.MinLOD = 0;                                // アクセス可能なミップマップの下限値
   samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;                // アクセス可能なミップマップの上限値
   hr = g_pD3D11User->m_D3DDevice->CreateSamplerState( &samplerDesc, &g_pSamplerState );
   if( FAILED( hr ) ) goto EXIT;

   // ブレンドステートを作成する( アルファブレンドを使用しない )
   D3D11_BLEND_DESC BlendDesc;
   ::ZeroMemory( &BlendDesc, sizeof( BlendDesc ) );
   BlendDesc.AlphaToCoverageEnable = FALSE;
   BlendDesc.IndependentBlendEnable = FALSE;
   BlendDesc.RenderTarget[0].BlendEnable = FALSE;
   BlendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
   BlendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
   BlendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
   BlendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
   BlendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
   BlendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
   BlendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
   hr = g_pD3D11User->m_D3DDevice->CreateBlendState( &BlendDesc, &g_pBlendState );
   if( FAILED( hr ) ) goto EXIT;

   // 深度ステンシルステートを作成する
   D3D11_DEPTH_STENCIL_DESC ddsDesc;
   ::ZeroMemory( &ddsDesc, sizeof( ddsDesc ) );
   ddsDesc.DepthEnable = TRUE;                                     // 深度テストを使用する
   ddsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
   ddsDesc.DepthFunc = D3D11_COMPARISON_LESS;
   ddsDesc.StencilEnable = FALSE;
   hr = g_pD3D11User->m_D3DDevice->CreateDepthStencilState( &ddsDesc, &g_pDepthStencilState );
   if( FAILED( hr ) ) goto EXIT;

   hr = S_OK;

EXIT:
   return hr;
}

// メモリ開放
void Invalidate()
{
   SAFE_RELEASE( g_pDepthStencilState );
   SAFE_RELEASE( g_pBlendState );
   SAFE_RELEASE( g_pSamplerState );
   SAFE_RELEASE( g_pSRView );
   SAFE_RELEASE( g_pVertexShader );
   SAFE_RELEASE( g_pPixelShader );
   SAFE_RELEASE( g_pLayout );
   SAFE_RELEASE( g_pIndexBuffer );
   SAFE_RELEASE( g_pVertexBuffer );

   SAFE_DELETE( g_pDebugFontUser );
   SAFE_DELETE( g_pD3D11User );
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   D3DXMATRIX matWorld, matView, matProj, matWVP;

   float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };

   // バックバッファをクリア
   g_pD3D11User->m_D3DDeviceContext->ClearRenderTargetView( g_pD3D11User->m_RenderTargetView, ClearColor ); 

   // 深度バッファをクリア
   if( g_pD3D11User->m_DepthStencilView )
      g_pD3D11User->m_D3DDeviceContext->ClearDepthStencilView( g_pD3D11User->m_DepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0 );

   // 頂点バッファ設定
   UINT stride = sizeof( VERTEX );
   UINT offset = 0;
   g_pD3D11User->m_D3DDeviceContext->IASetVertexBuffers( 0, 1, &g_pVertexBuffer, &stride, &offset );

   // インデックスバッファ設定
   g_pD3D11User->m_D3DDeviceContext->IASetIndexBuffer( g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

   // レイアウト設定
   g_pD3D11User->m_D3DDeviceContext->IASetInputLayout( g_pLayout );

   // プリミティブ タイプおよびデータの順序に関する情報を設定
   g_pD3D11User->m_D3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP );

   // 行列設定
   D3DXMatrixPerspectiveFovLH( &matProj, 3.1415926f / 2.0f, 4.0f / 3.0f, 1.0f, 250.0f );
   D3DXMatrixLookAtLH( &matView,
                       &D3DXVECTOR3( 0.0f, 20.0f, -20.0f ),
                       &D3DXVECTOR3( 0.0f, 20.0f, 0.0f ),
                       &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );
   static float x = 90.0f;
   D3DXMatrixRotationX( &matWorld, D3DXToRadian( x ) );

   matWVP = matWorld * matView * matProj;
   // シェーダー内では列優先にしているので転置行列を作成する。
   D3DXMatrixTranspose( &matWVP, &matWVP );

   ID3D11Buffer* Buffers = NULL;
   
   // 定数バッファを作成する。
   hr = g_pD3D11User->CreateConstantBuffer( &Buffers, NULL, sizeof( D3DXMATRIX ), D3D11_CPU_ACCESS_WRITE );
   if( FAILED( hr ) ) goto EXIT;

   D3D11_MAPPED_SUBRESOURCE mappedResource;

   // サブリソースに格納されているデータへのポインターを取得して、そのサブリソースへの GPU のアクセスを拒否。
   hr = g_pD3D11User->m_D3DDeviceContext->Map( Buffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
   if( FAILED( hr ) ) goto EXIT;
   ::CopyMemory( mappedResource.pData, &matWVP, sizeof( D3DXMATRIX ) );
   g_pD3D11User->m_D3DDeviceContext->Unmap( Buffers, 0 );

   // 頂点シェーダーをデバイスに設定する。
   g_pD3D11User->m_D3DDeviceContext->VSSetShader( g_pVertexShader, NULL, 0 );
   // 頂点シェーダーに定数バッファを設定する。
   g_pD3D11User->m_D3DDeviceContext->VSSetConstantBuffers( 0, 1, &Buffers );

   // ハルシェーダーを無効にする。
   g_pD3D11User->m_D3DDeviceContext->HSSetShader( NULL, NULL, 0 );

   // ドメインシェーダーを無効にする。
   g_pD3D11User->m_D3DDeviceContext->DSSetShader( NULL, NULL, 0 );

   // ジオメトリシェーダーを無効にする。
   g_pD3D11User->m_D3DDeviceContext->GSSetShader( NULL, NULL, 0 );

   // ピクセルシェーダーをデバイスに設定する。
   g_pD3D11User->m_D3DDeviceContext->PSSetShader( g_pPixelShader, NULL, 0 );
   g_pD3D11User->m_D3DDeviceContext->PSSetSamplers( 0, 1, &g_pSamplerState );
   g_pD3D11User->m_D3DDeviceContext->PSSetShaderResources( 0, 1, &g_pSRView );

   // コンピュートシェーダーを無効にする。
   g_pD3D11User->m_D3DDeviceContext->CSSetShader( NULL, NULL, 0 );

   // アルファブレンドを無効にする
   g_pD3D11User->m_D3DDeviceContext->OMSetBlendState( g_pBlendState, 0, 0xffffffff );

   // 深度テストを有効にする
   g_pD3D11User->m_D3DDeviceContext->OMSetDepthStencilState( g_pDepthStencilState, 0 );

   // インデックスバッファを使用した描画
   g_pD3D11User->m_D3DDeviceContext->DrawIndexed( 4, 0, 0 );

   // デバッグ専用フォント描画
   if( g_pDebugFontUser )
   {
      g_pDebugFontUser->RenderFPS( g_pD3D11User->m_D3DDeviceContext, 0, 0 );
   }

   // レンダリングされたイメージをユーザーに表示。
   hr = g_pD3D11User->m_SwapChain->Present( 0, 0 );

   if( ScreenShot )
   {
      // スクリーンショット作成
      g_pD3D11User->CreateScreenShot();
      ScreenShot = false;
   }

EXIT:
   SAFE_RELEASE( Buffers );
   return hr;
}

// 節電処理および描画処理
HRESULT PowerSavingAndRender()
{
   HRESULT hr = E_FAIL;

   switch( StandBy )
   {
   // スタンバイモード
   case  true:
      // テストのみ行い、描画処理は行わない。
      hr = g_pD3D11User->m_SwapChain->Present( 0, DXGI_PRESENT_TEST );
      switch( hr )
      {
      // いまだスタンバイ中。。。
      case DXGI_STATUS_OCCLUDED:
         // 電源管理によるスリープ状態の場合ここにくる。
         // フルスクリーンモード時にスクリーンセーバーが起動時した場合は、表示モードが強制的にウィンドウモードに変更されるためここにこない。
         goto EXIT;
         break;
      case S_OK:
         // フルスクリーンモード時にスクリーンセーバーが起動時した場合は表示モードが強制的にウィンドウモードに変更される。
         // ウィンドウモードの場合スタンバイから復帰してしまうため、ウィンドウがアクティブになったときに復帰するようにする。
         if( Activate == true )
         {
            // たまにウィンドウが表示されないときがあるので表示するようにする
            ::ShowWindow( g_pD3D11User->m_hWnd, SW_SHOW );
            StandBy = false;
         }
         break;
      default:
         goto EXIT;
         break;
      }
      break;
   // スタンバイモードでない
   case false:
      // 描画処理
      hr = Render();
      if( FAILED( hr ) ) goto EXIT;

      switch( hr )
      {
      case DXGI_STATUS_OCCLUDED:
         // スタンバイモードへ移行
         // フルスクリーンモード時のスクリーンセーバー起動時、
         // スリープ状態に移行した時に発生する。
         StandBy = true;
         goto EXIT;
         break;
      case S_OK:
         break;
      default:
         goto EXIT;
         break;
      }
      break;
   }

   hr = S_OK;

EXIT:

   return hr;
}

// ウィンドウプロシージャ
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, UINT wParam, LONG lParam )
{
   switch( msg )
   {
   case WM_KEYUP:
      // アプリ終了
      if( wParam == VK_ESCAPE )
         ::DestroyWindow( hWnd );

      // F2キーを押すと、ウィンドウモードを切り替える。
      // 自動的にウィンドウモードを切り替える機能もあるが、ウィンドウスタイルを自由に変更するために自分で実装することにした。
      if( wParam == VK_F2 )
      {
         g_pD3D11User->ChangeWindowMode();
      }
      // スクリーンショットを作成する
      if( wParam == VK_SNAPSHOT )
         ScreenShot = true;
      break;

   case WM_ACTIVATE:
      Activate = true;
      break;

// フルスクリーンからウィンドウモードに切り替えるとき WM_SIZE イベントが発生せず、結果的に IDXGISwapChain::ResizeBuffers がコールされない。
// 環境にもよるようだが、画面上に何も表示されない現象が発生する可能性があるので
// D3D11USER::ChangeWindowModeOptimization() は D3D11USER::ChangeWindowMode() 内でコールするように修正し、ここでの処理は無効にする
//   case WM_SIZE:
//      g_pD3D11User->ChangeWindowModeOptimization();
//      break;

   case WM_DESTROY:
      Invalidate();
      ::PostQuitMessage(0);
      break;

   default:
      return ::DefWindowProc( hWnd, msg, wParam, lParam );
   }

   return 0L;
}

// メイン関数
int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE /*hPrevInstance*/,
                        LPTSTR    /*lpCmpLine*/,
                        INT       /*nCmdShow*/ )
{
   HRESULT hr = E_FAIL;
   MSG msg;
   ::ZeroMemory(&msg, sizeof(MSG));

   // 表示モードを記述するための構造体。
   DXGI_MODE_DESC sd;

   // Direct3D 関連自作クラスのインスタンスを作成
   g_pD3D11User = new D3D11USER();

   // ディスプレイモード一覧を取得する。
   // 取得した値はクラス内部に保持される。
   hr = g_pD3D11User->GetDisplayMode();
   if( FAILED( hr ) )
   {
      ::MessageBox( NULL, _T("ディスプレイモード取得エラー"), _T("初期化エラー"), MB_OK );
      goto EXIT;
   }
   // とりあえず最初に見つかったディスプレイモードを選択する
   CopyMemory( &sd, &g_pD3D11User->m_DisplayModeDesc[0], sizeof( DXGI_MODE_DESC ) );

   // ウィンドウの作成およびDirect3D の初期化
   hr = g_pD3D11User->InitD3D11( AppName, hInstance, WndProc, &sd, TRUE, TRUE, TRUE, TRUE );
   if( FAILED( hr ) )
   {
      ::MessageBox( NULL, _T("Direct3D 11.0 初期化エラー"), _T("初期化エラー"), MB_OK );
      goto EXIT;
   }

   // デバッグ専用フォント出力クラスの作成処理
   // デバックコンパイル時のみ使用する
#if defined(DEBUG) || defined(_DEBUG)
   g_pDebugFontUser = new CDebugFont();
   hr = g_pDebugFontUser->Create( g_pD3D11User->m_D3DDevice, 0.015f, 0.05f );
   if( FAILED( hr ) )
   {
      ::MessageBox( NULL, _T("デバックフォントクラス初期化エラー"), _T("初期化エラー"), MB_OK );
      goto EXIT;
   }
#endif

   // リソースの初期化
   hr = Init();
   if( FAILED( hr ) )
   {
      ::MessageBox( NULL, _T("リソース初期化エラー"), _T("初期化エラー"), MB_OK );
      goto EXIT;
   }
   
   ::ShowWindow(g_pD3D11User->m_hWnd, SW_SHOW);
   ::UpdateWindow(g_pD3D11User->m_hWnd);

   // メッセージループ
   do
   { 
      if( ::PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
      {
         ::TranslateMessage(&msg); 
         ::DispatchMessage(&msg); 
      }
      else
      {
         hr = PowerSavingAndRender();
         if( FAILED( hr ) )
            ::DestroyWindow( g_pD3D11User->m_hWnd );
      }
   }while( msg.message != WM_QUIT );

EXIT:
   if( g_pD3D11User && g_pD3D11User->m_hWnd )
      ::DestroyWindow( g_pD3D11User->m_hWnd );

   ::UnregisterClass( AppName, hInstance );

   return msg.wParam;
}

ソース内で使用している SimpleHLSL03.hlslDirect3D 11.0 テクスチャーを使うで使用したファイルをそのまま使用しています。

最後にフォント画像。

この画像を使用して dds ファイルを作成します。使いたい人は使ってください。 dds ファイルの作成方法はアルファチャンネルつきDDSファイル作成を参照してください。


web拍手 by FC2

Prev Top Next

inserted by FC2 system