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

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


 Before

 After

今回も前回に引き続きテッセレーションをやります。今回から本格的にテッセレーションのサンプル作成を行います。 テッセレーションは何度か説明したとおり、ポリゴンの分割を行います。 しかしポリゴンを分割しただけではジオメトリ処理での負荷が高くなるだけで、レンダリング品質は向上しません。 したがって、メッシュの輪郭がうまい具合に滑らかになるように頂点を移動してあげる必要があります。


上記の図では6角形に頂点を追加して12角形を作成しています。 追加した頂点を移動することにより輪郭がより滑らかになったのが確認できると思います。 当然、追加する頂点数を多くすれば円に近似します。

では追加した頂点をどのようにして移動するのでしょうか?これにはパラメトリック曲面を使用します。 パラメトリック曲面については筆者は詳しくないので詳しく説明できません。これについては書籍を読むなり、ネットで調べるなりして自力で理解してください。

ものすごく簡単に説明するとパラメトリック曲面は、コントロール ポイント と 0 〜 1 の範囲内の補完係数により生成された曲面のことです。
コントロール ポイントの追加と座標の計算はハルシェーダー ステージで行います。 今回のサンプルはとりあえず作成しただけなのでいろいろ問題があります。ですので説明しません。

補完係数は固定機能パイプラインのテッセレータ ステージで作成されます。

最後に頂点の座標計算をドメインシェーダー ステージで行います。この座標計算をパラメトリック曲面の式により計算します。 計算方法は、 ATI のドキュメントをぱくって参考にしてます。 Curved PN Triangles
Microsoft DirectX SDK (June 2010) のサンプルにもあります。

---Tessellation03.hlsl---


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

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

// 定数バッファ2
cbuffer cbBuffer2 : register( b2 )
{
  bool g_bLightingFlag    : packoffset( c0.x );   // ライティングフラグ
};

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

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

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

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

// ハルシェーダーのパッチ定数フェーズ用の出力パラメータ
struct CONSTANT_HS_OUT
{
  float Edges[3] : SV_TessFactor;        // パッチのエッジのテッセレーション係数
  float Inside   : SV_InsideTessFactor;  // パッチ内部のテッセレーション係数
  float3 conPos3 : POSITION3;            // 追加するコントロール ポイント
  float3 conPos4 : POSITION4;            // 追加するコントロール ポイント
  float3 conPos5 : POSITION5;            // 追加するコントロール ポイント
};

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

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

// コントロールポイントを作成する
float3 CreateControlPoint( VS_OUT ip1, VS_OUT ip2, float factor )
{
  float3 Out;

  // コントロール ポイントの中心点の座標
  float3 center = ( ip1.pos + ip2.pos ) * 0.5f;

  // 法線ベクトルを合成し、正規化する
  float3 normal = normalize( ip1.normal + ip2.normal );

  // 法線ベクトルの内積を計算し、角度が大きくなるほど値が大きくなるようにする
  float d = 1.0f - ( dot( ip1.normal, ip2.normal ) + 1.0f ) * 0.5f;

  // コントロール ポイントの座標を計算
  Out = center + normal * d * factor;

  return Out;
}

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

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

  return Out;
}

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

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

  // コントロール ポイントを3頂点追加
  Out.conPos3 = CreateControlPoint( ip[0], ip[1], 1.0f );
  Out.conPos4 = CreateControlPoint( ip[1], ip[2], 1.0f );
  Out.conPos5 = CreateControlPoint( ip[2], ip[0], 1.0f );

  return Out;
}

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

  Out.pos = In[i].pos;
  Out.normal = In[i].normal;
  Out.texel = In[i].texel;
  return Out;
}

// *****************************************************************************************
// ドメインシェーダー
// *****************************************************************************************
[domain("tri")]
DS_OUT DS_Main( CONSTANT_HS_OUT In, float3 uvw : SV_DomainLocation, const OutputPatch<HS_OUT, 3> patch )
{
  DS_OUT Out;

  // 頂点座標
  // 2次ベジェ曲面により追加した頂点の座標を計算する
  float3 p1 = patch[2].pos * pow( uvw.x, 2.0f ) + 
              patch[1].pos * pow( uvw.y, 2.0f ) + 
              patch[0].pos * pow( uvw.z, 2.0f ) + 
              2.0f * In.conPos5 * uvw.x * uvw.z + 
              2.0f * In.conPos4 * uvw.y * uvw.x + 
              2.0f * In.conPos3 * uvw.z * uvw.y;

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

  // 法線ベクトル
  float3 n1 = normalize( patch[2].normal * uvw.x + patch[1].normal * uvw.y + patch[0].normal * uvw.z );
  float color = max( 0.0f, dot( n1, -g_vecLight.xyz ) );
  Out.color = float4( color, color, color, 1 );

  // テクセル
  float2 t1 = patch[2].texel * uvw.x + patch[1].texel * uvw.y + patch[0].texel * uvw.z;
  Out.texel = t1;
  
  return Out;
}

// *****************************************************************************************
// ピクセルシェーダ
// *****************************************************************************************
float4 PS_Main( DS_OUT In ) : SV_TARGET
{
  float4 Out;
  
  switch( g_bLightingFlag )
  {
    case true:
      Out = g_Tex.Sample( g_Sampler, In.texel ) * In.color;
      break;
    case false:
      Out = float4( 1, 1, 1, 1 );
      break;
  }
  
  return Out;
}

---main.cpp---

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

// FBX SDK 用
#include "../../USER/MeshUser.h"
#include "../../USER/FBXSDKMeshLoaderUser.h"

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

#else
// コンパイル済み頂点シェーダー
#include "../../USER/HLSL/Tessellation03_VS_Main.h"
// コンパイル済みハルシェーダー
#include "../../USER/HLSL/Tessellation03_HS_Main.h"
// コンパイル済みドメインシェーダー
#include "../../USER/HLSL/Tessellation03_DS_Main.h"
// コンパイル済みピクセルシェーダー
#include "../../USER/HLSL/Tessellation03_PS_Main.h"
#endif

// アプリケーション名
TCHAR* AppName = _T("DX11_Tutrial16 Curved PN Triangles");

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

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

// 定数定義
typedef struct _CBUFFER0
{
   D3DXMATRIX  g_matWVP;   // ワールド × ビュー × 射影 行列
   D3DXVECTOR4 g_vecLight; // 平行光源の方向ベクトル
}CBUFFER0;

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

typedef struct _CBUFFER2
{
   bool  g_bLightingFlag;     // true:ライティングを有効にする、false:ライティングを無効にする
   float g_Dummy1;
   float g_Dummy2;
   float g_Dummy3;
}CBUFFER2;

// FBX メッシュローダー
FBXSDK_MESHLOADER_USER* g_pMeshLoader = NULL;
// メッシュオブジェクト
BASE_MESH_USER*         g_pMesh = NULL;

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

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

// サンプラーステート
ID3D11SamplerState* g_pSamplerState = NULL;

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

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

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

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

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

   // 頂点情報を取得する
   hr = g_pMeshLoader->LoadMeshData( _T("Res/object.fbx"), &g_pMesh );
   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 },
   };

#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/Tessellation03.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/Tessellation03.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/Tessellation03.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/Tessellation03.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;
   hr = g_pD3D11User->CreateConstantBuffer( &g_pConstantBuffers[2], NULL, sizeof( CBUFFER2 ), D3D11_CPU_ACCESS_WRITE );
   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_DELETE( g_pMesh );
   SAFE_DELETE( g_pMeshLoader );
   SAFE_RELEASE( g_pSamplerState );
   SAFE_RELEASE( g_pConstantBuffers[2] );
   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_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 matScaling, matRotationY, matTranslation, matWorld, matInv, matView, matProj, matWVP;
   D3DXVECTOR4 v;
   CBUFFER0* cbuffer0;
   CBUFFER1* cbuffer1;
   CBUFFER2* cbuffer2;
   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( MESH_USER::VERTEX_USER );
   UINT offset = 0;
   g_pD3D11User->m_D3DDeviceContext->IASetVertexBuffers( 0, 1, &g_pMesh->MeshUser[0].VertexBuffer, &stride, &offset );

   // インデックスバッファ設定
   g_pD3D11User->m_D3DDeviceContext->IASetIndexBuffer( g_pMesh->MeshUser[0].IndexBuffer, DXGI_FORMAT_R32_UINT, 0 );

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

   // メッシュが 3 つの頂点(コントロールポイント)を持つと指定する
   g_pD3D11User->m_D3DDeviceContext->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_3_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, -5.0f ),
                       &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ),
                       &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

   // 射影行列
   D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI / 2.0f, 4.0f / 3.0f, 1.0f, 50.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);
      // スケーリング行列
      D3DXMatrixScaling( &matScaling, 3.0f, 3.0f, 3.0f );
      // Y軸回転
      static float Y = 0.0f;
      Y += 0.0005f;
      if( Y >= D3DX_PI * 2.0f )
         Y -= D3DX_PI * 2.0f;
      D3DXMatrixRotationY( &matRotationY, Y );
      // 平行移動行列
      D3DXMatrixTranslation( &matTranslation, 0.0f, 0.0f, 0.0f );
      matWorld = matScaling * matRotationY * matTranslation;

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

      // 逆行列
      // ワールド行列の逆行列を作成
      D3DXMatrixInverse( &matInv, NULL, &matWorld );
      D3DXVec4Transform( &v, &g_vecLight, &matInv );
      D3DXVec3Normalize( (D3DXVECTOR3*)&v, (D3DXVECTOR3*)&v );
      ::CopyMemory( &cbuffer0->g_vecLight, &v, sizeof( D3DXVECTOR4 ) );

      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;
      g_pD3D11User->m_D3DDeviceContext->Unmap( g_pConstantBuffers[1], 0 );
   }   

   // ライティングフラグを定数バッファにセット
   {
      hr = g_pD3D11User->m_D3DDeviceContext->Map( g_pConstantBuffers[2], 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource );
      if( FAILED( hr ) ) goto EXIT;
      cbuffer2 = (CBUFFER2*)(mappedResource.pData);
      switch( FillMode )
      {
      case 0:
         cbuffer2->g_bLightingFlag = true;
         break;
      case 1:
         cbuffer2->g_bLightingFlag = false;
         break;
      }
      g_pD3D11User->m_D3DDeviceContext->Unmap( g_pConstantBuffers[2], 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->PSSetConstantBuffers( 2, 1, &g_pConstantBuffers[2] );
   // ピクセルシェーダーにサンプラーステートを設定する。
   g_pD3D11User->m_D3DDeviceContext->PSSetSamplers( 0, 1, &g_pSamplerState );
   // ピクセルシェーダーにテクスチャーを設定する。
   ID3D11ShaderResourceView* pSRV = NULL;
   g_pMesh->MeshUser[0].GetTexture( _T("DiffuseColor"), &pSRV );
   g_pD3D11User->m_D3DDeviceContext->PSSetShaderResources( 0, 1, &pSRV );

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

   // インデックスバッファを使用した描画
   g_pD3D11User->m_D3DDeviceContext->DrawIndexed( g_pMesh->MeshUser[0].IndexesCount, 0, 0 );

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

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

      char str[100];

      switch( FillMode )
      {
      case 0:
         g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, "SOLID", 0, 0.1f );
         break;
      case 1:
         g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, "WIREFRAME", 0, 0.1f );
         break;
      }

      sprintf_s( str, sizeof( str ), "TessFactor: %f", TessFactor );
      g_pDebugFontUser->RenderDebugText( g_pD3D11User->m_D3DDeviceContext, str, 0, 0.2f );
   }

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

   case WM_KEYDOWN:
      if( wParam == 'Q' )
         TessFactor += 1.0f;
      if( wParam == 'A' && TessFactor > 1.0f )
         TessFactor -= 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));

   // メモリリーク検出
   _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_EVERY_1024_DF);

   // 表示モードを記述するための構造体。
   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

   // 自作のメッシュローダーの初期化
   g_pMeshLoader = new FBXSDK_MESHLOADER_USER();
   g_pMeshLoader->Initialize( g_pD3D11User->m_D3DDevice );

   // リソースの初期化
   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;
}

3Dメッシュをプログラムで作成するのが面倒だったので、FBX 使いました。

あと念のため言っておくとあんまり複雑な形状のメッシュを使用すると頂点の移動が期待通りに行われない可能性があります。 今回はとりあえず作成したサンプルなので、この辺をつっこまないように!!


web拍手 by FC2

Prev Top Next

inserted by FC2 system