Microsoft Visual C++ 2010 Express
 Microsoft DirectX SDK (June 2010)
 Direct3D 11.0
 Shader Model 4.0

■Direct3D 11.0 ストリームアウトプット Prev Top Next
関連ページ:Direct3D 11.0 初期化 Direct3D 11.0 デバック用フォント描画
2011/05/10:
  1. 入力レイアウトの作成処理を D3D11USER クラス内で行うように修正
2012/03/11:
  1. マルチレンダーターゲットに対応するためにブレンドステートを設定する関数を修正


今回は、シェーダー関連のネタとからは少し外れますが、GPUのみでパーティクルを実装することをやってみます。 パーティクル自体はDirectX9でも普通に実装できましたが、座標の更新などはCPUで計算していました。 今回は、この座標の更新もGPUで行うようにするため、すべての処理をGPUで処理できるようになります。 そのため、CPUによる処理が不必要となるので理屈ではアプリケーション全体のパフォーマンスの向上が期待できます。 実際どうなのかはテストしてないので不明ですが。

さて、今回のネタを実装するのに必要となるのはストリームアウトプットとID3D11DeviceContext::DrawAuto()です。 この当たりについては図を見たほうがわかりやすいと思うのでID3D11DeviceContext::DrawAutoを参照してください。 図を参照するとわかると思いますが、ジオメトリシェーダーからストリームアウトプットステージに出力しています。 その後再度、頂点シェーダーに出力するようなことをしています。 つまり、ジオメトリシェーダーを2回通します。なぜこんなことをするのか、いまいちわからないと思うので、今回のサンプルの処理フローで説明します。

1.頂点シェーダーでの処理
パーティクルの位置情報を、そのままジオメトリシェーダーに出力します。

2.パーティクルの位置の更新とパーティクルの生成と削除
ジオメトリシェーダー内で、パーティクルの位置の更新を行います。 またパーティクルの生成と削除も行います。

3.ストリームアウトプットに出力
ストリームアウトプットにデータを出力します。

4.再度頂点シェーダーでの処理
パーティクルの位置情報を、そのままジオメトリシェーダーに出力します。

5.ポイントスプライト( 四角ポリゴン1個 )を生成
パーティクルの位置情報をもとに、ジオメトリシェーダー内でポイントスプライトを生成します。

6.描画
ポイントスプライトを描画します。

こんな感じです。ではソースを見ていきます。


---SimpleHLSL12.hlsl---

// ワールド行列 × ビュー × 射影行列
cbuffer cbMatrixWVP : register( b0 )
{
   // 列優先
   column_major  float4x4 g_matWVP : packoffset( c0 );
   float1 g_Scale     : packoffset( c4.x );   // パーティクルのスケーリング値
   float1 g_LimitTime : packoffset( c4.y );   // タイマーの最大値( この値を超えると初期化する )
   float1 g_Dummy2    : packoffset( c4.z );
   float1 g_Dummy3    : packoffset( c4.w );
};

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

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

// ジオメトリシェーダーの入力パラメータ
struct VS_IN
{
   float3 pos    : POSITION;  // 座標
   float3 v0     : NORMAL;    // 初速度
   float time    : TEXCOORD;  // 時間
};

typedef VS_IN VS_OUT;

// 頂点シェーダーではそのまま出力
VS_OUT VS0_Main( VS_IN In )
{
   return In;
}

typedef VS_OUT GS_IN;
typedef GS_IN GS0_OUT;

[maxvertexcount(6)]   // ジオメトリシェーダーで出力する最大頂点数
// ジオメトリシェーダー
void GS0_Main( point GS_IN In[1],                   // ポイント プリミティブの入力情報
               inout PointStream<GS0_OUT> PStream   // ポイント プリミティブの出力ストリーム
             )
{
   GS0_OUT Out;
   
   // パーティクルの時間が g_LimitTime を超えたので初期化する
   if( In[0].time > g_LimitTime )
   {
      Out.pos = float3( 0, 0, 0 );
      Out.v0 = In[0].v0;
      Out.time = 0;
   }
   else
   {
      // X 位置 = 初速度 × 時間
      Out.pos.x = In[0].v0.x * In[0].time;

      // 自由落下運動の公式により、Y 座標を計算する。
      // 9.80665f は重力加速度。
      Out.pos.y = -0.5f * 9.80665f * In[0].time * In[0].time + In[0].v0.y * In[0].time;

      // Z 位置 = 初速度 × 時間
      Out.pos.z = In[0].v0.z * In[0].time;

      Out.v0 = In[0].v0;

      // 時間を進める
      Out.time = In[0].time + 0.001f;
   }

   PStream.Append( Out );
   PStream.RestartStrip();
}

struct GS1_OUT
{
   float4 pos    : SV_POSITION;  // パーティクルの位置
   float4 color  : COLOR;        // パーティクルの色
   float2 texel  : TEXCOORD0;    // テクセル
};

[maxvertexcount(4)]   // ジオメトリシェーダーで出力する最大頂点数
// ジオメトリシェーダー
void GS1_Main( point GS_IN In[1],                       // ポイント プリミティブの入力情報
               inout TriangleStream<GS1_OUT> TriStream  // トライアングル プリミティブの出力ストリーム
              )
{
   GS1_OUT Out;

   // 時間は 0.0f 〜 g_LimitTime の範囲内で変化する
   float c = 1.0f - In[0].time / g_LimitTime;

   // 頂点カラーは、時間の経過に伴い徐々に黄色から赤っぽくかつ透明になるようにしている。
   float4 color = float4( 1, c * c, 0, c );

   Out.pos =  mul( float4( In[0].pos.x + g_Scale, In[0].pos.y + g_Scale, In[0].pos.z, 1.0f ), g_matWVP );
   Out.color = color;
   Out.texel = float2( 1, 0 );
   TriStream.Append( Out );

   Out.pos = mul( float4( In[0].pos.x - g_Scale, In[0].pos.y + g_Scale, In[0].pos.z, 1.0f ), g_matWVP );
   Out.color = color;
   Out.texel = float2( 0, 0 );
   TriStream.Append( Out );

   Out.pos = mul( float4( In[0].pos.x + g_Scale, In[0].pos.y - g_Scale, In[0].pos.z, 1.0f ), g_matWVP );
   Out.color = color;
   Out.texel = float2( 1, 1 );
   TriStream.Append( Out );

   Out.pos = mul( float4( In[0].pos.x - g_Scale, In[0].pos.y - g_Scale, In[0].pos.z, 1.0f ), g_matWVP );
   Out.color = color;
   Out.texel = float2( 0, 1 );
   TriStream.Append( Out );

   TriStream.RestartStrip();
}

typedef GS1_OUT PS_IN;

// ピクセルシェーダ
float4 PS1_Main( PS_IN In ) : SV_TARGET
{
   return g_Tex.Sample( g_Sampler, In.texel ) * In.color;
}

GS0_Main() はジオメトリシェーダーのメイン関数です。 この関数ではパーティクルの座標を更新して、ストリームアウトプットに出力します。 Y座標は自由落下運動により計算しています。自由落下運動については、ウィキペディアに情報がのってますので参照してみてください。 自由落下
GS1_Main() もジオメトリシェーダーのメイン関数です。 この関数ではストリームアウトプットに出力された情報をもとにポイントスプライトを生成します。

---main.cpp---


#include "../../USER/DX11User.h"
#include "../../USER/D3D11User.h"
#include "../../USER/DebugFontUser.h"
#include "../../USER/HLSL/SimpleHLSL12_VS0_Main.h"
#include "../../USER/HLSL/SimpleHLSL12_GS0_Main.h"
#include "../../USER/HLSL/SimpleHLSL12_GS1_Main.h"
#include "../../USER/HLSL/SimpleHLSL12_PS1_Main.h"

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

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

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

// 頂点定義
typedef struct _VERTEX
{
   D3DXVECTOR3 pos;  // 位置座標
   D3DXVECTOR3 v0;   // 初速度
   float time;       // 時間
}VERTEX;

// 定数定義
typedef struct _CBUFFER
{
   D3DXMATRIX  g_matWVP;    // ワールド × ビュー × 射影 行列
   float       g_Scale;     // パーティクルのスケーリング値
   float       g_LimitTime; // 時間の限界値( この値を超えると初期値に戻す )
   float       g_dummy2;    // 未使用
   float       g_dummy3;    // 未使用
}CBUFFER;

// ターゲット入出力バッファー
ID3D11Buffer* g_pSOBuffer[2] = { NULL, NULL };
// 頂点シェーダー
ID3D11VertexShader*   g_pVertexShader= NULL;
// 入力レイアウト
ID3D11InputLayout*    g_pLayout = NULL;
// 定数バッファ
ID3D11Buffer* g_pConstantBuffers = NULL;
// ジオメトリシェーダー( パーティクルの座標更新用、描画用 )
ID3D11GeometryShader* g_pGeometryShader[2] = { NULL, NULL };
// ピクセルシェーダー
ID3D11PixelShader* g_pPixelShader = NULL;
// サンプラーステート
ID3D11SamplerState* g_pSamplerState = NULL;
// シェーダーリソースビュー
ID3D11ShaderResourceView* g_pSRView = NULL;

// パーティクル数
#define NUM_PARTICLE 100

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

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

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

   // パーティクルの初期値を設定
   VERTEX v[NUM_PARTICLE];
   for( int i=0; i<NUM_PARTICLE; i++ )
   {
      v[i].pos = D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), 
      v[i].v0 = D3DXVECTOR3( (float)(rand()%50-25) * 0.3f, (float)(rand()%50) * 0.5f, (float)(rand()%50-25) * 0.3f );
      v[i].time = (float)(rand()%100) * 0.1f;
   };
   // ターゲット入出力バッファーを作成
   D3D11_BUFFER_DESC BufferDesc;
   ::ZeroMemory( &BufferDesc, sizeof( BufferDesc ) );
   BufferDesc.ByteWidth      = sizeof( v );  // バッファサイズ
   BufferDesc.Usage          = D3D11_USAGE_DEFAULT;
   BufferDesc.BindFlags      = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;
   BufferDesc.CPUAccessFlags = 0;
   BufferDesc.MiscFlags      = 0;
   D3D11_SUBRESOURCE_DATA mappedResource;
   mappedResource.pSysMem          = v;  // バッファ・データの初期値
   mappedResource.SysMemPitch      = 0;
   mappedResource.SysMemSlicePitch = 0;
   for( int i=0; i<_countof( g_pSOBuffer ); i++ )
   {
      hr = g_pD3D11User->m_D3DDevice->CreateBuffer( &BufferDesc, &mappedResource, &g_pSOBuffer[i] );
      if( FAILED( hr ) ) goto EXIT;
   }

   // 頂点シェーダーの作成

   D3D11_INPUT_ELEMENT_DESC layout[] = {
      {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0},  // 位置座標
      {"NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},  // 初速度
      {"TEXCOORD", 0, DXGI_FORMAT_R32_FLOAT,       0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0}   // 時間
   };

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

   // ターゲット入出力バッファーの定義
   // D3D11_SO_DECLARATION_ENTRY
   // SemanticNameに指定可能な値は、"POSITION"、"NORMAL"、または "TEXCOORD0" らしい。
   D3D11_SO_DECLARATION_ENTRY decl[] = { 
      { 0, "POSITION",  0, 0, 3, 0 },
      { 0, "NORMAL",    0, 0, 3, 0 },
      { 0, "TEXCOORD",  0, 0, 1, 0 },
   };

   UINT stride[] = { sizeof( VERTEX ) };

   // ストリームアウトプットとしてジオメトリシェーダーを作成
#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreateGeometryShaderWithStreamOutputFromMemory(
                                          &g_pGeometryShader[0],
                                          g_GS0_Main,
                                          sizeof( g_GS0_Main ),
                                          decl,
                                          _countof( decl ),
                                          stride,
                                          _countof( stride )
                                       );
#else
   hr = g_pD3D11User->CreateGeometryShaderWithStreamOutputFromFile(
                                          &g_pGeometryShader[0],
                                          _T("../../USER/HLSL/SimpleHLSL12.hlsl"),
                                          "GS0_Main",
                                          "gs_4_0",
                                          decl,
                                          _countof( decl ),
                                          stride,
                                          _countof( stride )
                                    );
#endif

   // 描画用のジオメトリシェーダーを作成する
#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreateGeometryShaderFromMemory( &g_pGeometryShader[1], g_GS1_Main, sizeof( g_GS1_Main ) );
   if( FAILED( hr ) ) goto EXIT;
#else
   hr = g_pD3D11User->CreateGeometryShaderFromFile( &g_pGeometryShader[1], _T("../../USER/HLSL/SimpleHLSL12.hlsl"), "GS1_Main", "gs_4_0" );
   if( FAILED( hr ) ) goto EXIT;
#endif

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

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

   // サンプラーステートの設定
   {
      D3D11_SAMPLER_DESC samplerDesc;
      ::ZeroMemory( &samplerDesc, sizeof( D3D11_SAMPLER_DESC ) );
      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;
   }

   // 画像ファイルを読み込んでテクスチャーリソースビューを作成する
   {
      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;
      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\\Smoke.dds"), &info, NULL, &g_pSRView, NULL );
      if( FAILED( hr ) ) goto EXIT;
   }

   hr = S_OK;

EXIT:

   return hr;
}

// メモリ開放
void Invalidate()
{
   SAFE_RELEASE( g_pSRView );
   SAFE_RELEASE( g_pSamplerState );
   SAFE_RELEASE( g_pConstantBuffers );
   SAFE_RELEASE( g_pLayout );
   SAFE_RELEASE( g_pPixelShader );
   for( int i=0; i<_countof( g_pGeometryShader ); i++ ) SAFE_RELEASE( g_pGeometryShader[i] );
   SAFE_RELEASE( g_pVertexShader );
   for( int i=0; i<_countof( g_pSOBuffer ); i++ ) SAFE_RELEASE( g_pSOBuffer[i] );
   SAFE_DELETE( g_pDebugFontUser );
   SAFE_DELETE( g_pD3D11User );
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;

   D3DXMATRIX matWVP, matWorld, matView, matProj;
   D3DXVECTOR4 v;

   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 );

   // 定数バッファ
   {
      D3DXMATRIX matView, matProj, matWVP, matTranslation;
      CBUFFER* cbuffer;
      D3D11_MAPPED_SUBRESOURCE mappedResource;

      // 射影行列
      D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4.0f, 4.0f / 3.0f, 1.0f, 250.0f );
      // ビュー行列
      D3DXMatrixLookAtLH( &matView,
                           &D3DXVECTOR3( 0.0f, 0.0f, -100.0f ),
                           &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                           &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

      // ワールド行列
      D3DXMatrixTranslation( &matTranslation, 0, 0, 0 );
      matWorld = matTranslation;

      // 行列の合成
      matWVP = matWorld * matView * matProj;

      hr = g_pD3D11User->m_D3DDeviceContext->Map( g_pConstantBuffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
      if( FAILED( hr ) ) goto EXIT;
      cbuffer = (CBUFFER*)(mappedResource.pData);
      // シェーダー内では列優先にしているので転置行列を作成する。
      D3DXMatrixTranspose( &cbuffer->g_matWVP, &matWVP );

      // スケーリング値
      cbuffer->g_Scale = 3.0f;

      // タイマーの最大値
      cbuffer->g_LimitTime = 10.0f;

      g_pD3D11User->m_D3DDeviceContext->Unmap( g_pConstantBuffers, 0 );
   }

   // *********************************************************************************************
   // パーティクルの更新
   // *********************************************************************************************
   {
      // ターゲット出力バッファーを入れ替える
      ID3D11Buffer *pB = g_pSOBuffer[0];
      g_pSOBuffer[0] = g_pSOBuffer[1];
      g_pSOBuffer[1] = pB;

      // 入力アセンブラー ステージに頂点バッファーの配列をバインドする。
      // ID3D11DeviceContext::IASetVertexBuffers
      UINT stride = sizeof( VERTEX );
      UINT offset = 0;
      g_pD3D11User->m_D3DDeviceContext->IASetVertexBuffers( 0, 1, &g_pSOBuffer[0], &stride, &offset );

      // 出力バッファーの配列のオフセット値
      // パイプラインのストリーム出力ステージで使用されるターゲット出力バッファーを設定します。
      // ID3D11DeviceContext::SOSetTargets
      g_pD3D11User->m_D3DDeviceContext->SOSetTargets( 1, &g_pSOBuffer[1], &offset );

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

      // ピクセルシェーダーを無効にした状態で深度ステンシルを有効にしているとエラーになるので無効にする
      ID3D11DepthStencilState*  pDepthStencilState = NULL;
      D3D11_DEPTH_STENCIL_DESC ddsDesc;
      UINT StencilRef;
      ::ZeroMemory( &ddsDesc, sizeof( D3D11_DEPTH_STENCIL_DESC ) );
      g_pD3D11User->m_D3DDeviceContext->OMGetDepthStencilState( &pDepthStencilState, &StencilRef );
      pDepthStencilState->GetDesc( &ddsDesc );
      ddsDesc.DepthEnable = FALSE;  // 深度テストを無効にする
      ddsDesc.StencilEnable = FALSE;
      SAFE_RELEASE( pDepthStencilState );
      hr = g_pD3D11User->m_D3DDevice->CreateDepthStencilState( &ddsDesc, &pDepthStencilState );
      if( FAILED( hr ) ) goto EXIT;
      g_pD3D11User->m_D3DDeviceContext->OMSetDepthStencilState( pDepthStencilState, StencilRef );
      SAFE_RELEASE( pDepthStencilState );

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

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

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

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

      // ジオメトリシェーダーをデバイスに設定する。
      g_pD3D11User->m_D3DDeviceContext->GSSetShader( g_pGeometryShader[0], NULL, 0 );
      // ジオメトリシェーダーに定数バッファを設定する。
      g_pD3D11User->m_D3DDeviceContext->GSSetConstantBuffers( 0, 1, &g_pConstantBuffers );

      // ピクセルシェーダーをデバイスに無効にする。
      g_pD3D11User->m_D3DDeviceContext->PSSetShader( NULL, NULL, 0 );

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

      static bool bInit = true;
      // 最初は頂点数がわかるので ID3D11DeviceContext::Draw() で描画する
      if( bInit )
      {
         g_pD3D11User->m_D3DDeviceContext->Draw( NUM_PARTICLE, 0 );
         bInit = false;
      }         
      // 2回目移行は処理する頂点数が不明なので ID3D11DeviceContext::DrawAuto() で描画する
      else
      {
         // ID3D11DeviceContext::DrawAuto
         g_pD3D11User->m_D3DDeviceContext->DrawAuto();
      }

      ID3D11Buffer* pNullBuffer[] = { NULL };
      // パイプラインのストリーム出力ステージで使用されるターゲット出力バッファーを無効にする。
      g_pD3D11User->m_D3DDeviceContext->SOSetTargets( 1, pNullBuffer, NULL );
   }

   // *********************************************************************************************
   // パーティクル描画
   // *********************************************************************************************
   {
      // プリミティブ タイプおよびデータの順序に関する情報を設定
      g_pD3D11User->m_D3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_POINTLIST );

      // 深度テストを無効にする
      ID3D11DepthStencilState*  pDepthStencilState = NULL;
      D3D11_DEPTH_STENCIL_DESC ddsDesc;
      UINT StencilRef;
      ::ZeroMemory( &ddsDesc, sizeof( D3D11_DEPTH_STENCIL_DESC ) );
      g_pD3D11User->m_D3DDeviceContext->OMGetDepthStencilState( &pDepthStencilState, &StencilRef );
      pDepthStencilState->GetDesc( &ddsDesc );
      ddsDesc.DepthEnable = FALSE;  // 深度テストを無効にする
      ddsDesc.StencilEnable = FALSE;
      SAFE_RELEASE( pDepthStencilState );
      hr = g_pD3D11User->m_D3DDevice->CreateDepthStencilState( &ddsDesc, &pDepthStencilState );
      if( FAILED( hr ) ) goto EXIT;
      g_pD3D11User->m_D3DDeviceContext->OMSetDepthStencilState( pDepthStencilState, StencilRef );
      SAFE_RELEASE( pDepthStencilState );

      // アルファブレンディングを線形合成で設定する
      D3D11_RENDER_TARGET_BLEND_DESC BlendDesc;
      BlendDesc = g_pD3D11User->GetAlignmentBlendDesc();
      g_pD3D11User->SetBlendState( &BlendDesc, 1, FALSE );

      // 入力アセンブラー ステージに頂点バッファーの配列をバインドする。
      UINT stride = sizeof( VERTEX );
      UINT offset = 0;
      g_pD3D11User->m_D3DDeviceContext->IASetVertexBuffers( 0, 1, &g_pSOBuffer[1], &stride, &offset );

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

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

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

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

      // ジオメトリシェーダーをデバイスに設定する。
      g_pD3D11User->m_D3DDeviceContext->GSSetShader( g_pGeometryShader[1], NULL, 0 );
      // ジオメトリシェーダーに定数バッファを設定する。
      g_pD3D11User->m_D3DDeviceContext->GSSetConstantBuffers( 0, 1, &g_pConstantBuffers );

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

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

      // パーティクルの描画
      // 頂点数が不明なので ID3D11DeviceContext::DrawAuto() で描画する
      g_pD3D11User->m_D3DDeviceContext->DrawAuto();
   }

   // デバッグ専用フォント描画
   if( g_pDebugFontUser )
   {
      // FPS表示
      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:

   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;

   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*/ )
{
   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF);

   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, FALSE, FALSE, 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;
}

D3D11USER::CreateGeometryShaderWithStreamOutputFromMemory()メンバ関数とか追加しているので、Direct3D 11.0 初期化を参照して修正してください。

あとSmoke.ddsは適当に作成してください。当サイトのフリー素材集を使ってもいいです。

最後に今回のネタはあまり意味ないかも。Direct3D11だったらコンピュートシェーダー使うのが一般的な気がする。


web拍手 by FC2

Prev Top Next

inserted by FC2 system