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

■Direct3D 11.0 はじめてのテッセレーション Prev Top Next
関連ページ:Direct3D 11.0 初期化 Direct3D 11.0 デバック用フォント描画
2012/03/11:
  1. マルチレンダーターゲットに対応するためにブレンドステートを設定する関数を修正


Before
After

今回はテッセレーションをやります。テッセレーションとはいわゆるポリゴンの細分化処理を行うことです。 既にいくつかのサイトで紹介されているので今更感がありますが、 Direct3D11 やるならぜひともやっておきたい技術のひとつなのでやります。

テッセレーションの利用目的は LoD( Level of Detail ) などがあげられます。 LoD は視点からの距離に応じて描画する3Dメッシュの頂点数の増減を行う処理のことです。 モデリング時のメッシュはローポリで作成して、アップ時にはテッセレーションによりポリゴン分割して高品質にレンダリングされるようにします。 こうすることでレンダリング品質を落とすことなく、さらに頂点の行列変換にかかる負荷を落としてパフォーマンスの向上が期待できます。 まあ実際の使用方法については次回以降に紹介することにして今回は基礎的なことをやります。


ではテッセレーションの概要を説明していきます。ここでは簡単な説明にとどめますので、詳細を知りたい方はMSDNを参照してください。

テッセレーションは3つのステージで構成されます。
ステージ名 シェーダー 目的
ハルシェーダーステージ プログラマブルシェーダー エッジ テッセレーション係数やパッチ定数データを決定する。
テッセレータ ステージ 固定機能シェーダー パーティションの種類とエッジ テッセレーション係数をもとにパッチを分割し、uv (およびオプションとして w) 座標を決定する。
ドメイン シェーダー ステージ プログラマブルシェーダー パッチ定数データとuv (およびオプションとして w) 座標とコントロールポイントをもとに実際の頂点座標を計算する

次にパイプライン ステージの遷移と入出力パラメータの図を示します。

矢印がいっぱいあってわけわからん状態です。たぶんこんな感じであってると思います。

聞きなれない用語がいくつか出てきましたので、解説します。ですが MSDN に用語の意味は説明されていないので勝手に解釈します。 ですのでもしかすると間違っている可能性があるのでご了承を。

1.コントロールポイント
  テッセレータにより追加された頂点の座標をパラメトリック曲面により計算するときに使用する頂点。制御点ともいう。

2.パッチ定数データ
  パッチはポリゴン分割処理を行う際に使用する頂点セットのことで、パッチ定数データはパッチごとに指定する定数データのこと。
  分割後の頂点の座標を決定する際に使用する。

3.パーティションの種類
  ポリゴンの分割時に使用するアルゴリズム。

4.エッジ テッセレーション係数
  ポリゴンの分割率を指定する係数。この値が大きくなるほどより詳細に分割される。

5.uv (およびオプションとして w) 座標
  テッセレータ ステージから出力される uv 座標は、テッセレーションにより生成された 0 〜 1 の範囲内の補完係数である。


ではソースを見ていきます。

---Tessellation01.hlsl---


// 定数バッファ0
cbuffer cbBuffer0 : register( b0 )
{
  // 列優先
  column_major float4x4 g_matWVP : packoffset( c0 );   // ワールド × ビュー × 射影 行列
};

// 定数バッファ1
cbuffer cbBuffer1 : register( b1 )
{
  float g_TessFactor       : packoffset( c0.x );   // パッチのエッジのテッセレーション係数
  float g_InsideTessFactor : packoffset( c0.y );   // パッチ内部のテッセレーション係数
};

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

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

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

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

// ハルシェーダーのパッチ定数フェーズ用の出力パラメータ
struct CONSTANT_HS_OUT
{
  float Edges[4] : SV_TessFactor;        // パッチのエッジのテッセレーション係数
  float Inside[2] : SV_InsideTessFactor; // パッチ内部のテッセレーション係数
};

// ハルシェーダーのコントロール ポイント フェーズ用の出力パラメータ
struct HS_OUT
{
  float3 pos   : POSITION;
  float2 texel : TEXCOORD0;
};

// ドメインシェーダーの出力パラメータ
struct DS_OUT
{
  float4 pos   : SV_POSITION;
  float2 texel : TEXCOORD0;
};

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

  // 頂点シェーダーではそのまま渡す
  Out.pos = In.pos;
  Out.texel = In.texel;
  return Out;
}

// *****************************************************************************************
// ハルシェーダーのパッチ定数フェーズ
// *****************************************************************************************
CONSTANT_HS_OUT ConstantsHS_Main( InputPatch<VS_OUT, 4> ip, uint PatchID : SV_PrimitiveID )
{
  CONSTANT_HS_OUT Out;

  // 定数バッファの値をそのまま渡す
  Out.Edges[0] = Out.Edges[1] = Out.Edges[2] = Out.Edges[3] = g_TessFactor;  // パッチのエッジのテッセレーション係数
  Out.Inside[0] = g_InsideTessFactor;  // パッチ内部の横方法のテッセレーション係数
  Out.Inside[1] = g_InsideTessFactor;  // パッチ内部の縦方法のテッセレーション係数
 
  return Out;
}

// *****************************************************************************************
// ハルシェーダーのコントロール ポイント フェーズ
// *****************************************************************************************
[domain("quad")]                             // テッセレートするメッシュの形状を指定する
[partitioning("integer")]                    // パッチの分割に使用するアルゴリズムを指定する
[outputtopology("triangle_ccw")]             // メッシュの出力方法を指定する
[outputcontrolpoints(4)]                     // ハルシェーダーのコントロール ポイント フェーズがコールされる回数
[patchconstantfunc("ConstantsHS_Main")]      // 対応するハルシェーダーのパッチ定数フェーズのメイン関数
HS_OUT HS_Main( InputPatch<VS_OUT, 4> In, uint i : SV_OutputControlPointID, uint PatchID : SV_PrimitiveID )
{
  HS_OUT Out;

  // そのまま渡す
  Out.pos = In[i].pos;
  Out.texel = In[i].texel;
  return Out;
}

// *****************************************************************************************
// ドメインシェーダー
// *****************************************************************************************
[domain("quad")]
DS_OUT DS_Main( CONSTANT_HS_OUT In, float2 uv : SV_DomainLocation, const OutputPatch<HS_OUT, 4> patch )
{
  DS_OUT Out;
  
  // 頂点座標
  float3 p1 = lerp( patch[1].pos, patch[0].pos, uv.x );
  float3 p2 = lerp( patch[3].pos, patch[2].pos, uv.x );
  float3 p3 = lerp( p1, p2, uv.y );

  Out.pos =  mul( float4( p3, 1.0f ), g_matWVP );

  // テクセル
  float2 t1 = lerp( patch[1].texel, patch[0].texel, uv.x );
  float2 t2 = lerp( patch[3].texel, patch[2].texel, uv.x );
  float2 t3 = lerp( t1, t2, uv.y );
  Out.texel = t3;

  return Out;
}

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

ドメインシェーダーのメイン関数である DS_Main() では分割後の実際の頂点座標を計算します。 uv座標には分割後の 0 〜 1 の範囲内の補完係数です。コントロールポイントの座標から分割後の頂点座標を計算します。

---main.cpp---


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

#include "../../USER/DebugFontUser.h"

// コンパイル済み頂点シェーダー
#include "../../USER/HLSL/Tessellation01_VS_Main.h"
// コンパイル済みハルシェーダー
#include "../../USER/HLSL/Tessellation01_HS_Main.h"
// コンパイル済みドメインシェーダー
#include "../../USER/HLSL/Tessellation01_DS_Main.h"
// コンパイル済みピクセルシェーダー
#include "../../USER/HLSL/Tessellation01_PS_Main.h"

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

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

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

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

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

// 定数定義
typedef struct _CBUFFER0
{
   D3DXMATRIX  g_matWVP; // ワールド × ビュー × 射影 行列
}CBUFFER0;

typedef struct _CBUFFER1
{
   float g_fTessFactor;       // ポリゴンのエッジのテッセレーション係数
   float g_fInsideTessFactor; // ポリゴン内部のテッセレーション係数
   float g_Dummy2;
   float g_Dummy3;
}CBUFFER1;

// 頂点バッファ
ID3D11Buffer* g_pVertexBuffer = NULL;
// インデックスバッファ
ID3D11Buffer* g_pIndexBuffer = NULL;

// 頂点シェーダー
ID3D11VertexShader* g_pVertexShader = NULL;
// 入力レイアウト
ID3D11InputLayout* g_pLayout = NULL;
// ハルシェーダー
ID3D11HullShader* g_pHullShader = NULL;
// ドメインシェーダー
ID3D11DomainShader* g_pDomainShader = NULL;
// ピクセルシェーダー
ID3D11PixelShader* g_pPixelShader = NULL;

// 定数バッファ
ID3D11Buffer* g_pConstantBuffers[2] = { NULL, NULL };

// シェーダーリソースビュー
ID3D11ShaderResourceView* g_pSRView = NULL;
// サンプラーステート
ID3D11SamplerState* g_pSamplerState = NULL;

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

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

float TessFactor = 1.0f;        // ポリゴンのエッジのテッセレーション係数の定数
float InsideTessFactor = 1.0f;  // ポリゴン内部のテッセレーション係数の定数
int FillMode = 0;               // 0:SOLID、1:WIREFRAME

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

   D3DX11_IMAGE_LOAD_INFO info;
   // 頂点座標を反時計回りで設定
   VERTEX v[] = {
                  D3DXVECTOR3(  50.0f,  50.0f, 0.0f ), D3DXVECTOR2( 1.0f, 0.0f ),
                  D3DXVECTOR3( -50.0f,  50.0f, 0.0f ), D3DXVECTOR2( 0.0f, 0.0f ),
                  D3DXVECTOR3(  50.0f, -50.0f, 0.0f ), D3DXVECTOR2( 1.0f, 1.0f ),
                  D3DXVECTOR3( -50.0f, -50.0f, 0.0f ), 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 };
   // インデックスバッファを作成する。
   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 },
         { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 12, 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/Tessellation01.hlsl"), "VS_Main", "vs_5_0",
                                                  &g_pLayout, layout, _countof( layout ) );
   if( FAILED( hr ) ) goto EXIT;
#endif

   // ハルシェーダーを作成する
#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreateHullShaderFromMemory( &g_pHullShader, g_HS_Main, sizeof( g_HS_Main ) );
   if( FAILED( hr ) ) goto EXIT;
#else
   hr = g_pD3D11User->CreateHullShaderFromFile( &g_pHullShader, _T("../../USER/HLSL/Tessellation01.hlsl"), "HS_Main", "hs_5_0" );
   if( FAILED( hr ) ) goto EXIT;
#endif

   // ドメインシェーダーを作成する
#ifndef UNCOMPILED_SHADER
   hr = g_pD3D11User->CreateDomainShaderFromMemory( &g_pDomainShader, g_DS_Main, sizeof( g_DS_Main ) );
   if( FAILED( hr ) ) goto EXIT;
#else
   hr = g_pD3D11User->CreateDomainShaderFromFile( &g_pDomainShader, _T("../../USER/HLSL/Tessellation01.hlsl"), "DS_Main", "ds_5_0" );
   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/Tessellation01.hlsl"), "PS_Main", "ps_5_0" );
   if( FAILED( hr ) ) goto EXIT;
#endif

   // 定数バッファを作成する。
   hr = g_pD3D11User->CreateConstantBuffer( &g_pConstantBuffers[0], NULL, sizeof( CBUFFER0 ), D3D11_CPU_ACCESS_WRITE );
   if( FAILED( hr ) ) goto EXIT;
   hr = g_pD3D11User->CreateConstantBuffer( &g_pConstantBuffers[1], NULL, sizeof( CBUFFER1 ), D3D11_CPU_ACCESS_WRITE );
   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 = 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\\Texture.bmp"), &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;

   hr = S_OK;

EXIT:
   return hr;
}

// メモリ開放
void Invalidate()
{
   SAFE_RELEASE( g_pSamplerState );
   SAFE_RELEASE( g_pSRView );
   SAFE_RELEASE( g_pConstantBuffers[1] );
   SAFE_RELEASE( g_pConstantBuffers[0] );
   SAFE_RELEASE( g_pPixelShader );
   SAFE_RELEASE( g_pDomainShader );
   SAFE_RELEASE( g_pHullShader );
   SAFE_RELEASE( g_pLayout );
   SAFE_RELEASE( g_pVertexShader );
   SAFE_RELEASE( g_pIndexBuffer );
   SAFE_RELEASE( g_pVertexBuffer );
   SAFE_DELETE( g_pDebugFontUser );
   SAFE_DELETE( g_pD3D11User );
}

// ラスタライザの描画モードを変更する
void ChangeFillMode( D3D11_FILL_MODE fillmode )
{
   ID3D11RasterizerState* pRasterState = NULL;
   g_pD3D11User->m_D3DDeviceContext->RSGetState( &pRasterState );
   D3D11_RASTERIZER_DESC rsState;
   pRasterState->GetDesc( &rsState );
   SAFE_RELEASE( pRasterState );
   rsState.FillMode = fillmode;
   g_pD3D11User->m_D3DDevice->CreateRasterizerState( &rsState, &pRasterState );
   g_pD3D11User->m_D3DDeviceContext->RSSetState( pRasterState );
   SAFE_RELEASE( pRasterState );
}

// 描画処理
HRESULT Render()
{
   HRESULT hr = E_FAIL;
   D3DXMATRIX matWorld, matView, matProj, matWVP;
   CBUFFER0* cbuffer0;
   CBUFFER1* cbuffer1;
   D3D11_MAPPED_SUBRESOURCE mappedResource;

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

   // メッシュが 4 つの頂点(コントロールポイント)を持つと指定する
   g_pD3D11User->m_D3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST );

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

   // ハルシェーダーをデバイスに設定する。
   g_pD3D11User->m_D3DDeviceContext->HSSetShader( g_pHullShader, NULL, 0 );

   // ドメインシェーダーをデバイスに設定する。
   g_pD3D11User->m_D3DDeviceContext->DSSetShader( g_pDomainShader, NULL, 0 );

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

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

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

   // ビュー行列
   D3DXMatrixLookAtLH( &matView,
                       &D3DXVECTOR3( 0.0f, 0.0f, -60.0f ),
                       &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                       &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

   // 射影行列
   D3DXMatrixPerspectiveFovLH( &matProj, 3.1415926f / 2.0f, 4.0f / 3.0f, 1.0f, 250.0f );

   // アルファブレンディングを無効
   D3D11_RENDER_TARGET_BLEND_DESC BlendDesc;
   BlendDesc = g_pD3D11User->GetDefaultBlendDesc();
   g_pD3D11User->SetBlendState( &BlendDesc, 1, FALSE );

   // 行列を定数バッファにセット
   {
      hr = g_pD3D11User->m_D3DDeviceContext->Map( g_pConstantBuffers[0], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
      if( FAILED( hr ) ) goto EXIT;
      cbuffer0 = (CBUFFER0*)(mappedResource.pData);
      // ワールド行列
      D3DXMatrixTranslation( &matWorld, 0.0f, -10.0f, 10.0f );
      // 行列を合成
      matWVP = matWorld * matView * matProj;
      // シェーダー内では列優先にしているので転置行列を作成する。
      D3DXMatrixTranspose( &cbuffer0->g_matWVP, &matWVP );
      g_pD3D11User->m_D3DDeviceContext->Unmap( g_pConstantBuffers[0], 0 );
   }

   // テッセレーション係数を定数バッファにセット
   {
      hr = g_pD3D11User->m_D3DDeviceContext->Map( g_pConstantBuffers[1], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
      if( FAILED( hr ) ) goto EXIT;
      cbuffer1 = (CBUFFER1*)(mappedResource.pData);
      cbuffer1->g_fTessFactor = TessFactor;
      cbuffer1->g_fInsideTessFactor = InsideTessFactor;
      g_pD3D11User->m_D3DDeviceContext->Unmap( g_pConstantBuffers[1], 0 );
   }   

   // ハルシェーダーに定数バッファを設定する。
   g_pD3D11User->m_D3DDeviceContext->HSSetConstantBuffers( 1, 1, &g_pConstantBuffers[1] );

   // ドメインシェーダーに定数バッファを設定する。
   g_pD3D11User->m_D3DDeviceContext->DSSetConstantBuffers( 0, 1, &g_pConstantBuffers[0] );

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

   // 描画モードを変更する
   switch( FillMode )
   {
   case 0:
      ChangeFillMode( D3D11_FILL_SOLID );
      break;
   case 1:
      ChangeFillMode( D3D11_FILL_WIREFRAME );
      break;
   }

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

   if( g_pDebugFontUser )
   {
      // 描画モードを変更する
      ChangeFillMode( D3D11_FILL_SOLID );

      // デバッグ専用フォント描画
      g_pDebugFontUser->RenderFPS( g_pD3D11User->m_D3DDeviceContext, 0, 0 );
      g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, "Change FillMode:        Push M Key", 0, 0.1f );
      g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, "Change TessFactor:      Push Q or A Key", 0, 0.2f );
      g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, "Change InsideTessFactor:Push W or S Key", 0, 0.3f );
   }

   // レンダリングされたイメージをユーザーに表示。
   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;

      if( wParam == 'M' )
         FillMode = 1 - FillMode;

      if( wParam == 'Q' )
         TessFactor += 1.0f;
      if( wParam == 'A' && TessFactor > 1.0f )
         TessFactor -= 1.0f;

      if( wParam == 'W' )
         InsideTessFactor += 1.0f;
      if( wParam == 'S' && InsideTessFactor > 1.0f )
         InsideTessFactor -= 1.0f;

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

ハルシェーダーおよび、ドメインシェーダーは SM 5.0 で使用できます。 HLSL のコンパイル時に正しく指定するようにしてください。

次にプリミティブトポロジについてです。テッセレーションを使用する場合は、頂点データをパッチ リストとして解釈させる必要があります。 該当の関数は ID3D11DeviceContext::IASetPrimitiveTopology() です。 詳細はD3D11_PRIMITIVE_TOPOLOGY参照ってことで。

さてこんなところでいいいでしょうか。 ぶっちゃけ説明書いてる自分自身がどうにも理解が怪しいところがありますが、まあ今後勉強していけば解決するでしょう。きっと。


web拍手 by FC2

Prev Top Next

inserted by FC2 system