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 デバック用フォント描画


今回はステレオグラムをやります。ステレオグラムとは目の焦点を意図的に前後にずらして合わせることで、立体的に見ることが出来る画像のことです。 ( ウィキペディアより )
サンプルでは平行法により画像を作成しています。球体が3つ並んでいるイメージが左右にありますが、左右のイメージはカメラ位置を少しだけずらしているので 同じではありません。カメラ位置のずらし幅は適当ですが、一応視差に基づいて作成しています。 でこの左側のイメージを左目、右側のイメージを右目でみれば立体的に見えるようになるわけですが、なれないと難しいかもしれません。 どうしても立体に見えない方は画像の中心に紙をたてて仕切りを作れば、やりやすくなります。

さて今回は特別なシェーダーは使用してません。どっちかというとマルチビューポートのサンプルになっています。 DirectX 9 のときは出力先のビューポートは1つだけでしたが、Direct3D 11 では複数のビューポートに同時に出力できるようになりました。 この実装方法を紹介するのが今回の趣旨です。 ステレオグラムをゲームでどう使うかはまったく思いつかないので、これについてはただの遊びと思ってください。


---HalfLambert2.hlsl---

// 定数バッファ0
cbuffer cbBuffer0 : register( b0 )
{
   // 列優先
   column_major float4x4 g_matWVP[2] : packoffset( c0 );   // ワールド × ビュー × 射影 行列
   float4 g_vecLight                 : packoffset( c8 );   // ローカル座標系での平行光源の方向ベクトル
};

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

// サンプラーステート
SamplerState  g_Sampler;

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

// 頂点シェーダーの出力パラメータ
struct VS_OUT
{
   float3 pos    : POSITION;
   float3 normal : NORMAL;
   float2 texel  : TEXCOORD0;
};

// 頂点シェーダー
VS_OUT HalfLambert2_VS_Main( VS_IN In )
{
   return In;
}

typedef VS_OUT GS_IN;

// ジオメトリシェーダーの出力パラメータ
struct GS_OUT
{
   float4 pos           : SV_POSITION;
   float3 normal        : NORMAL;
   float2 texel         : TEXCOORD0;
   uint   ViewportIndex : SV_ViewportArrayIndex;
};

[maxvertexcount(6)]   // ジオメトリシェーダーで出力する最大頂点数
// ジオメトリシェーダー
void HalfLambert2_GS_Main( triangle GS_IN In[3],                    // トライアングル リストを構成する頂点配列の入力情報
                           inout TriangleStream<GS_OUT> TriStream   // 頂点情報を追加するためのストリームオブジェクト
                         )
{
   GS_OUT Out;

   for( int i=0; i<2; i++ )
   {
      for( int j=0; j<3; j++ )
      {
         Out.pos = mul( float4( In[j].pos.xyz, 1.0f ), g_matWVP[i] );
         Out.normal = In[j].normal;
         Out.texel = In[j].texel;
         Out.ViewportIndex = i;

         // 頂点を追加する
         TriStream.Append( Out );
      }
      // 現在のストリップを終了し、新しいストリップを開始する。
      TriStream.RestartStrip();
   }
}

typedef GS_OUT PS_IN;

// ピクセルシェーダ
float4 HalfLambert2_PS_Main( PS_IN In ) : SV_TARGET
{
   // ハーフランバート
   float lambert = dot( -g_vecLight.xyz, normalize( In.normal ) );
   lambert = lambert * 0.5f + 0.5f;
   lambert *= lambert;

   return g_Tex.Sample( g_Sampler, In.texel ) * lambert;
}

ハーフランバートのHLSLソースです。以前作成したソースを若干修正してます。 ビューポートごとに出力するためジオメトリシェーダーで頂点を作成するようにします。 どのビューポートに出力するかは SV_ViewportArrayIndex セマンティクスで決定されます。

ところで動的シェーダーリンクを使用して、以前作成したハーフランバートのソースと統一しようかと思いましたが、めんどくさくなってやめました(笑)。

---HalfLambert2.h---


#ifndef HALFLAMBERT2_H
#define HALFLAMBERT2_H

class HALFLAMBERT2
{
private:
   // 定数バッファ定義( float * 4 バイト数の倍数のサイズである必要がある )
   typedef struct _CBUFFER0
   {
      // ワールド × ビュー × 射影 行列
      D3DXMATRIX  matWVP[2];
      // 平行光源の方向ベクトル
      D3DXVECTOR4 vecLight;
   }CBUFFER0;

   // 入力レイアウト
   ID3D11InputLayout* m_pLayout;
   // 定数バッファ
   ID3D11Buffer* m_pConstantBuffers;
   // 頂点シェーダー
   ID3D11VertexShader* m_pVertexShader;
   // ジオメトリシェーダー
   ID3D11GeometryShader* m_pGeometryShader;
   // ピクセルシェーダー
   ID3D11PixelShader* m_pPixelShader;
   // サンプラーステート
   ID3D11SamplerState* m_pSamplerState;

public:
   HALFLAMBERT2();
   ~HALFLAMBERT2();
   // 初期化
   HRESULT Init( ID3D11Device* pD3DDevice );
   // 定数バッファを設定する
   HRESULT SetConstantBuffers( ID3D11DeviceContext* pD3DDeviceContext,
                               D3DXMATRIX* pMatWVP,
                               UINT NumMatWVP,
                               D3DXVECTOR4* pLightDir );
   // 描画開始処理
   void BeginPass( ID3D11DeviceContext* pD3DDeviceContext );
   // 描画終了処理
   void EndPass(){;};
};

#endif

---HalfLambert2.cpp---



#include "DX11User.h"
#include "D3D11User.h"
#include "HalfLambert2.h"
#include "HalfLambert2_HalfLambert2_VS_Main.h"
#include "HalfLambert2_HalfLambert2_GS_Main.h"
#include "HalfLambert2_HalfLambert2_PS_Main.h"

HALFLAMBERT2::HALFLAMBERT2()
{
   m_pLayout = NULL;
   m_pConstantBuffers = NULL;
   m_pVertexShader = NULL;
   m_pGeometryShader = NULL;
   m_pPixelShader = NULL;
   m_pSamplerState = NULL;
}

HALFLAMBERT2::~HALFLAMBERT2()
{
   SAFE_RELEASE( m_pLayout );
   SAFE_RELEASE( m_pConstantBuffers );
   SAFE_RELEASE( m_pVertexShader );
   SAFE_RELEASE( m_pGeometryShader );
   SAFE_RELEASE( m_pPixelShader );
   SAFE_RELEASE( m_pSamplerState );
}

// 初期化
HRESULT HALFLAMBERT2::Init( ID3D11Device* pD3DDevice )
{
   HRESULT hr = E_FAIL;

   // 頂点シェーダーの作成
   hr = pD3DDevice->CreateVertexShader( g_HalfLambert2_VS_Main, sizeof( g_HalfLambert2_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 },
         { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
   };
   // シェーダーで使用される入力バッファーを記述するための入力レイアウトオブジェクトを作成する。
   hr = pD3DDevice->CreateInputLayout( layout, _countof( layout ), &g_HalfLambert2_VS_Main[0], sizeof( g_HalfLambert2_VS_Main ), &m_pLayout );
   if( FAILED( hr ) ) goto EXIT;

   // ジオメトリシェーダーの作成
   hr = pD3DDevice->CreateGeometryShader( g_HalfLambert2_GS_Main, sizeof( g_HalfLambert2_GS_Main ), NULL, &m_pGeometryShader );
   if( FAILED( hr ) ) goto EXIT;

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

   // 定数バッファを作成
   D3D11_BUFFER_DESC BufferDesc;
   ::ZeroMemory( &BufferDesc, sizeof( BufferDesc ) );
   BufferDesc.ByteWidth             = sizeof( CBUFFER0 );        // バッファサイズ
   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;

   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 = pD3DDevice->CreateSamplerState( &samplerDesc, &m_pSamplerState );
   if( FAILED( hr ) ) goto EXIT;

   hr = S_OK;

EXIT:
   return hr;
}

// 定数バッファを設定する
HRESULT HALFLAMBERT2::SetConstantBuffers( ID3D11DeviceContext* pD3DDeviceContext,
                                          D3DXMATRIX* pMatWVP,
                                          UINT NumMatWVP,
                                          D3DXVECTOR4* pLightDir )
{
   HRESULT hr = E_FAIL;
   D3D11_MAPPED_SUBRESOURCE mappedResource;

   CBUFFER0* cbuffer;

   hr = pD3DDeviceContext->Map( m_pConstantBuffers, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
   if( FAILED( hr ) ) goto EXIT;
   cbuffer = (CBUFFER0*)(mappedResource.pData);

   if( pMatWVP )  ::CopyMemory( &cbuffer->matWVP[0], pMatWVP, sizeof( D3DXMATRIX ) * NumMatWVP );
   if( pLightDir )::CopyMemory( &cbuffer->vecLight, pLightDir, sizeof( D3DXVECTOR4 ) );

   pD3DDeviceContext->Unmap( m_pConstantBuffers, 0 );

   hr = S_OK;
EXIT:
   return hr;
}

// 描画
void HALFLAMBERT2::BeginPass( ID3D11DeviceContext* pD3DDeviceContext )
{
   // レイアウト設定
   pD3DDeviceContext->IASetInputLayout( m_pLayout );

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

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

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

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

   // ピクセルシェーダーをデバイスに設定する。
   pD3DDeviceContext->PSSetShader( m_pPixelShader, NULL, 0 );
   // ピクセルシェーダーに定数バッファを設定する
   pD3DDeviceContext->PSSetConstantBuffers( 0, 1, &m_pConstantBuffers );
   // ピクセルシェーダーにサンプラステートを設定する。
   pD3DDeviceContext->PSSetSamplers( 0, 1, &m_pSamplerState );

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

---main.cpp---


#include "../../USER/DX11User.h"
#include "../../USER/D3D11User.h"
#include "../../USER/DebugFontUser.h"
#include "../../USER/HalfLambert2.h"

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

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

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

// 頂点定義
typedef struct _VERTEX
{
   // 頂点座標
   D3DXVECTOR3 pos;
   // 法線ベクトル
   D3DXVECTOR3 normal;
   // テクセル
   D3DXVECTOR2 texel;
}VERTEX;

// 頂点数
UINT g_VertexBufferCnt = 0;
// 頂点インデックス数
UINT g_IndexBufferCnt = 0;

// 平行光源の方向ベクトル
D3DXVECTOR4 g_vecLight = D3DXVECTOR4( -0.3f, -0.3f, 0.5f, 0.0f );

// 頂点バッファ
ID3D11Buffer* g_pVertexBuffer = NULL;
// インデックスバッファ
ID3D11Buffer* g_pIndexBuffer = NULL;
// シェーダーリソースビュー
ID3D11ShaderResourceView* g_pSRView = NULL;

// ハーフランバートクラス
HALFLAMBERT2* g_pHalfLambert2 = NULL;

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

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

// 球メッシュを作成する
HRESULT CreateBall( VERTEX** pVertex, UINT** pIndex, float Radius, UINT Division, UINT* pVertexBufferCnt, UINT* pIndexBufferCnt, D3DXVECTOR2* pTexelScale )
{
   HRESULT hr = E_FAIL;

   if( Radius <= 0 || Division < 2 ) goto EXIT;

   // 頂点数を計算
   *pVertexBufferCnt = 2 +                       // 上部、下部の2頂点
                       ( Division - 1 ) *        // 縦方向の分割
                       ( Division * 2 + 1 );     // 水平方向の分割

   (*pVertex) = new VERTEX[*pVertexBufferCnt];
   if( (*pVertex) == NULL ) goto EXIT;

   UINT Index = 0;

   // 上部の頂点
   (*pVertex)[Index].pos = D3DXVECTOR3( 0.0f, Radius, 0.0f );
   D3DXVec3Normalize( &((*pVertex)[Index].normal), &((*pVertex)[Index].pos) );
   (*pVertex)[Index].texel = D3DXVECTOR2( 0.0f, 0.0f );

   Index++;

   float R = D3DX_PI / (float)( Division );
   float Y;
   float Z;
   // 縦方向の分割ループ
   for( UINT i=1; i<Division; i++ )
   {
      Y = Radius * cosf( R * (float)i );
      Z = Radius * sinf( R * (float)i ); 

      // 水平方向の分割ループ
      for( UINT j=0; j<Division * 2 + 1; j++ )
      {
         if( j != Division * 2 )
         {
            (*pVertex)[Index].pos = D3DXVECTOR3( Z * cosf( R * (float)j ), Y, Z * sinf( R * (float)j ) );
            D3DXVec3Normalize( &((*pVertex)[Index].normal), &((*pVertex)[Index].pos) );
         }
         else
         {
            (*pVertex)[Index].pos = (*pVertex)[Index - Division * 2].pos;
            (*pVertex)[Index].normal = (*pVertex)[Index - Division * 2].normal;
         }

         (*pVertex)[Index].texel = D3DXVECTOR2( (float)j / (float)( Division * 2 ) * pTexelScale->x, (float)i / (float)( Division ) * pTexelScale->y );

         Index++;
      }
   }

   // 下部の頂点
   (*pVertex)[Index].pos = D3DXVECTOR3( 0.0f, -Radius, 0.0f );
   D3DXVec3Normalize( &((*pVertex)[Index].normal), &((*pVertex)[Index].pos) );
   (*pVertex)[Index].texel = D3DXVECTOR2( 0.0f, 1.0f );

   // バグチェック
   if( *pVertexBufferCnt != Index + 1 ) goto EXIT;

   // インデックス数を計算
   *pIndexBufferCnt = 3 * Division * 2 * 2 +                   // 上下部分のポリゴン
                      3 * ( Division - 2 ) * Division * 2 * 2; // 上下以外の部分のポリゴン

   (*pIndex) = new UINT[*pIndexBufferCnt];
   if( (*pIndex) == NULL ) goto EXIT;

   Index = 0;

   // 上部
   for( UINT i=0; i<Division*2; i++ )
   {
      (*pIndex)[Index++] = 0;
      (*pIndex)[Index++] = i + 1;
      (*pIndex)[Index++] = i + 2;
   }

   // 側面
   for( UINT i=0; i<Division-2; i++ )
   {
      for( UINT j=0; j<Division*2; j++ )
      {
         (*pIndex)[Index++] = 1 + i * ( Division * 2 + 1 ) + j;
         (*pIndex)[Index++] = 1 + ( i + 1 ) * ( Division * 2 + 1 ) + j;
         (*pIndex)[Index++] = 1 + ( i + 1 ) * ( Division * 2 + 1 ) + 1 + j;

         (*pIndex)[Index++] = 1 + i * ( Division * 2 + 1 ) + j;
         (*pIndex)[Index++] = 1 + ( i + 1 ) * ( Division * 2 + 1 ) + 1 + j;
         (*pIndex)[Index++] = 1 + i * ( Division * 2 + 1 ) + 1 + j;
      }
   }

   // 下部
   for( UINT i=0; i<Division*2; i++ )
   {
      (*pIndex)[Index++] = 1 + ( Division - 2 ) * ( Division * 2 + 1 ) + i;
      (*pIndex)[Index++] = *pVertexBufferCnt - 1;
      (*pIndex)[Index++] = 1 + ( Division - 2 ) * ( Division * 2 + 1 ) + 1 + i;
   }

   // バグチェック
   if( *pIndexBufferCnt != Index ) goto EXIT;

   hr = S_OK;

EXIT:
   return hr;
}

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

   D3DX11_IMAGE_LOAD_INFO info;

   // 頂点配列
   VERTEX* v = NULL;
   // 頂点インデックス配列
   UINT*   Index = NULL;

   // 球作成
   D3DXVECTOR2 texelScale = D3DXVECTOR2( 4.0f, 4.0f );
   hr = CreateBall( &v, &Index, 5.0f, 8, &g_VertexBufferCnt, &g_IndexBufferCnt, &texelScale );
   if( FAILED( hr ) ) goto EXIT;

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

   // インデックスバッファを作成する。
   hr = g_pD3D11User->CreateIndexBuffer( &g_pIndexBuffer, Index, sizeof( UINT ) * g_IndexBufferCnt, 0 );
   if( FAILED( hr ) ) goto EXIT;

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

   // ハーフランバートクラスの初期化
   g_pHalfLambert2 = new HALFLAMBERT2();
   hr = g_pHalfLambert2->Init( g_pD3D11User->m_D3DDevice );
   if( FAILED( hr ) ) goto EXIT;

   DXGI_SWAP_CHAIN_DESC SCDesc;
   // スワップチェーンの設定を取得する
   g_pD3D11User->m_SwapChain->GetDesc( &SCDesc );

   // マルチビューポートを設定する
   {
      D3D11_VIEWPORT NewViewport[2];
      for( int i=0; i<_countof( NewViewport ); i++ )
      {
         NewViewport[i].TopLeftX = (FLOAT)( i * SCDesc.BufferDesc.Width * 0.5f );
         NewViewport[i].TopLeftY = 0.0f;
         NewViewport[i].Width    = (FLOAT)SCDesc.BufferDesc.Width * 0.5f;
         NewViewport[i].Height   = (FLOAT)SCDesc.BufferDesc.Height;

         NewViewport[i].MinDepth = 0.0f;
         NewViewport[i].MaxDepth = 1.0f;
      }

      // マルチビューポートを設定する
      g_pD3D11User->m_D3DDeviceContext->RSSetViewports( _countof( NewViewport ), NewViewport );
   }

   // マルチビューポートに対応するシザー矩形を設定する
   {
      D3D11_RECT rect[2];
      for( int i=0; i<_countof( rect ); i++ )
      {
         rect[i].left   = (LONG)( i * SCDesc.BufferDesc.Width * 0.5f );
         rect[i].top    = 0;
         rect[i].right  = (LONG)( SCDesc.BufferDesc.Width * ( 0.5f + i * 0.5f ) );
         rect[i].bottom = SCDesc.BufferDesc.Height;
      }

      // シザー矩形の配列をバインドする
      g_pD3D11User->m_D3DDeviceContext->RSSetScissorRects( _countof( rect ), rect );

      // ここからシザー矩形カリングを有効にする設定を行う

      ID3D11RasterizerState* pRasterState = NULL;
      g_pD3D11User->m_D3DDeviceContext->RSGetState( &pRasterState );
      D3D11_RASTERIZER_DESC pDesc;
      pRasterState->GetDesc( &pDesc );
      SAFE_RELEASE( pRasterState );
      pDesc.ScissorEnable = TRUE;  // シザー矩形カリングを有効にする
      hr = g_pD3D11User->m_D3DDevice->CreateRasterizerState( &pDesc, &pRasterState );
      if( FAILED( hr ) ) goto EXIT;
      g_pD3D11User->m_D3DDeviceContext->RSSetState( pRasterState );
      SAFE_RELEASE( pRasterState );
   }

   hr = S_OK;

EXIT:
   SAFE_DELETE_ARRAY( v );
   SAFE_DELETE_ARRAY( Index );

   return hr;
}

// メモリ開放
void Invalidate()
{
   SAFE_RELEASE( g_pIndexBuffer );
   SAFE_RELEASE( g_pVertexBuffer );
   SAFE_DELETE( g_pHalfLambert2 );
   SAFE_RELEASE( g_pSRView );
   SAFE_DELETE( g_pDebugFontUser );
   SAFE_DELETE( g_pD3D11User );
}

// 描画処理
HRESULT Render()
{
   HRESULT hr;
   D3DXMATRIX matWVP[2], matWorld, matView[2], matProj;
   D3DXVECTOR4 v;
   const float MatViewOffset = 2.0f;

   // 頂点関連のデータをデバイスコンテキストにセット
   {
      // 頂点バッファ設定
      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 );
   }

   // ビュー × 射影行列( すべてのメッシュで共有する )
   {
      // 射影行列
      // ビューポートが横幅の半分のサイズになるのでアスペクト比を変更する
//      D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4.0f, 4.0f / 3.0f, 1.0f, 250.0f );
      D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4.0f, 2.0f / 3.0f, 1.0f, 250.0f );

      for( int i=0; i<_countof( matWVP ); i++ )
      {
         // ビュー行列
         D3DXMatrixLookAtLH( &matView[i],
                             &D3DXVECTOR3( MatViewOffset - MatViewOffset * 2.0f * i, 0.0f, -10.0f ),
                             &D3DXVECTOR3( MatViewOffset - MatViewOffset * 2.0f * i, 0.0f, 0.0f ),
                             &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

      }
   }

   // レンダーターゲットビューをクリア
   float ClearColor[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
   g_pD3D11User->m_D3DDeviceContext->ClearRenderTargetView( g_pD3D11User->m_RenderTargetView, ClearColor );

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

   // 深度ステンシルを設定
   {
      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 = TRUE;  // 深度テストを有効にする
      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 );
   }

   // *********************************************************************************************
   // 球を描画
   // *********************************************************************************************
   for( int i=0; i<3; i++ )
   {
      {
         // 行列の合成
         {
            D3DXMATRIX matTranslation;

            // ワールド行列
            D3DXMatrixTranslation( &matTranslation, -1.0f + (float)i * 3.0f, -2.0f + (float)i * 4.0f, 20.0f + (float)i * 12.0f );
            matWorld = matTranslation;

            // 行列の合成
            for( int i=0; i<_countof( matWVP ); i++ )
            {
               matWVP[i] = matWorld * matView[i] * matProj;
               // シェーダー内では列優先にしているので転置行列を作成する。
               D3DXMatrixTranspose( &matWVP[i], &matWVP[i] );
            }
         }

         // 平行光源の方向ベクトル
         {
            D3DXMATRIX matInv;

            // ワールド行列の逆行列を作成
            D3DXMatrixInverse( &matInv, NULL, &matWorld );
            D3DXVec4Transform( &v, &g_vecLight, &matInv );
            D3DXVec3Normalize( (D3DXVECTOR3*)&v, (D3DXVECTOR3*)&v );
         }

         // 定数バッファを設定する
         g_pHalfLambert2->SetConstantBuffers( g_pD3D11User->m_D3DDeviceContext, matWVP, _countof( matWVP ), &v );

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

         // テクスチャーをシェーダーリソースに設定する
         g_pD3D11User->m_D3DDeviceContext->PSSetShaderResources( 0, 1, &g_pSRView );

         // ハーフランバートシェーダーの描画準備処理
         g_pHalfLambert2->BeginPass( g_pD3D11User->m_D3DDeviceContext );

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

         // ハーフランバートシェーダーの描画終了処理( 実際には何もしない )
         g_pHalfLambert2->EndPass();
      }
   }

   //// デバッグ専用フォント描画
   //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;
}

シザー矩形カリングの設定をしてますが、ビューポートとシザー矩形の領域指定方法が異なるので注意してください。 ビューポートの場合の Width メンバは矩形領域の横幅となり、シザー矩形の場合の right メンバはスクリーン座標系上での矩形の右辺の座標となります。 もっとも今回のサンプルのようにビューポートの表示領域すべてレンダリングする場合は、シザー矩形カリングする必要はありませんが。


web拍手 by FC2

Prev Top Next

inserted by FC2 system