レンダリング フレームワーク II: ゲームのレンダリング
Note
このトピックは、「DirectX を使った単純なユニバーサル Windows プラットフォーム (UWP) ゲームの作成」チュートリアル シリーズの一部です。 リンク先のトピックでは、このシリーズのコンテキストを説明しています。
レンダリング フレームワーク I では、シーン情報を取得して表示画面に表示する方法について説明しました。 ここでは、一歩前に戻って、レンダリング用にデータを準備する方法について説明します。
Note
このサンプルの最新ゲーム コードをダウンロードしていない場合は、Direct3D サンプル ゲームのページに移動してください。 このサンプルは、UWP 機能サンプルの大規模なコレクションの一部です。 サンプルをダウンロードする方法については、「Windows 開発用のサンプル アプリケーション」をご覧ください。
目的
目的を簡単に要約します。 これは、UWP DirectX ゲームのグラフィックス出力を表示する基本的なレンダリング フレームワークを設定する方法を理解することです。 これら 3 つの手順に疎にグループ化できます。
- グラフィックス インターフェイスへの接続を確立する
- 準備: グラフィックスを描画するために必要なリソースを作成する
- グラフィックスを表示する: フレームをレンダリングする
レンダリング フレームワーク I: レンダリングの概要 手順 1 と 3 を説明して、グラフィックスのレンダリング方法について説明しました。
この記事では、このフレームワークの他の部分を設定し、レンダリングが行われる前に必要なデータを準備する方法について説明します。これはプロセスの手順 2 です。
レンダラーを設計する
レンダラーは、ゲーム ビジュアルの生成に使用されるすべての D3D11 オブジェクトと D2D オブジェクトの作成と保守を担当します。 GameRenderer クラスは、このサンプル ゲームのレンダラーであり、ゲームのレンダリングニーズを満たすように設計されています。
ゲームのレンダラーの設計に役立ついくつかの概念を次に示します。
- Direct3D 11 API は COM API として定義されているため、これらの API で定義されているオブジェクトへの ComPtr 参照を指定する必要があります。 これらのオブジェクトは、アプリが終了したときに最後の参照がスコープ外になったときに自動的に解放されます。 詳細については、「 ComPtr」を参照してください。 これらのオブジェクトの例: 定数バッファー、シェーダー オブジェクト - vertex シェーダー、 ピクセル シェーダー、シェーダー リソース オブジェクト。
- 定数バッファーは、レンダリングに必要なさまざまなデータを保持するために、このクラスで定義されます。
- 周波数が異なる複数の定数バッファーを使用して、フレームごとに GPU に送信する必要があるデータの量を減らします。 このサンプルでは、更新する必要がある頻度に基づいて、定数を異なるバッファーに分割します。 これは、Direct3D プログラミングのベスト プラクティスです。
- このサンプル ゲームでは、4 つの定数バッファーを定義します。
- m_constantBufferNeverChanges には照明パラメーターが含まれます。 FinalizeCreateGameDeviceResources メソッドで 1 回設定され、もう一度変更されることはありません。
- m_constantBufferChangeOnResize にはプロジェクション マトリックスが含まれます。 投影マトリックスは、ウィンドウのサイズと縦横比に依存します。 CreateWindowSizeDependentResources で設定されFinalizeCreateGameDeviceResources メソッドにリソースが読み込まれた後に更新されます。 3D でレンダリングする場合は、フレームごとに 2 回も変更されます。
- m_constantBufferChangesEveryFrame にはビュー マトリックスが含まれます。 このマトリックスは、カメラの位置と外観の方向 (投影に対する法線) に依存し、 Render メソッドでフレームごとに 1 回変更します。 これについては、Rendering フレームワーク I: レンダリングの概要GameRenderer::Render メソッドで説明。
- m_constantBufferChangesEveryPrim には各プリミティブのモデル マトリックスとマテリアル プロパティが含まれます。 モデル マトリックスは、頂点をローカル座標からワールド座標に変換します。 これらの定数は各プリミティブに固有であり、描画呼び出しごとに更新されます。 これについては、 レンダリング フレームワーク I: レンダリングの概要 Primitive レンダリングで説明しました。
- プリミティブのテクスチャを保持するシェーダー リソース オブジェクトも、このクラスで定義されます。
- 一部のテクスチャは、事前に定義された (DDS 圧縮および圧縮されていないテクスチャを格納するために使用できるファイル形式です。DDS テクスチャは、ワールドの壁と床、および弾薬球に使用されます)。
- このゲームのサンプルで、シェーダー リソース オブジェクトは、m_sphereTexture、m_cylinderTexture、m_ceilingTexture、m_floorTexture、m_wallsTexture です。
- シェーダー オブジェクトは、プリミティブとテクスチャを計算するために、このクラスで定義されます。
- このサンプル ゲームで、シェーダー オブジェクトは、m_vertexShader、m_vertexShaderFlat、m_pixelShader、m_pixelShaderFlat です。
- 頂点シェーダーはプリミティブと基本的な照明を処理し、ピクセル シェーダー (フラグメント シェーダーとも呼ばれます) はテクスチャとピクセルごとの効果を処理します。
- これらのシェーダーには、異なるプリミティブをレンダリングするための 2 つのバージョン (標準とフラット) があります。 バージョンが異なる理由は、フラット バージョンの方がはるかにシンプルで、反射ハイライトやピクセル単位の照明効果を行わないということです。 これらは壁に使用され、低電力デバイスでのレンダリングを高速化します。
GameRenderer.h
次に、サンプル ゲームのレンダラー クラス オブジェクトのコードを見てみましょう。
// Class handling the rendering of the game
class GameRenderer : public std::enable_shared_from_this<GameRenderer>
{
public:
GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources);
void CreateDeviceDependentResources();
void CreateWindowSizeDependentResources();
void ReleaseDeviceDependentResources();
void Render();
// --- end of async related methods section
winrt::Windows::Foundation::IAsyncAction CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game);
void FinalizeCreateGameDeviceResources();
winrt::Windows::Foundation::IAsyncAction LoadLevelResourcesAsync();
void FinalizeLoadLevelResources();
Simple3DGameDX::IGameUIControl* GameUIControl() { return &m_gameInfoOverlay; };
DirectX::XMFLOAT2 GameInfoOverlayUpperLeft()
{
return DirectX::XMFLOAT2(m_gameInfoOverlayRect.left, m_gameInfoOverlayRect.top);
};
DirectX::XMFLOAT2 GameInfoOverlayLowerRight()
{
return DirectX::XMFLOAT2(m_gameInfoOverlayRect.right, m_gameInfoOverlayRect.bottom);
};
bool GameInfoOverlayVisible() { return m_gameInfoOverlay.Visible(); }
// --- end of rendering overlay section
...
private:
// Cached pointer to device resources.
std::shared_ptr<DX::DeviceResources> m_deviceResources;
...
// Shader resource objects
winrt::com_ptr<ID3D11ShaderResourceView> m_sphereTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_cylinderTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_ceilingTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_floorTexture;
winrt::com_ptr<ID3D11ShaderResourceView> m_wallsTexture;
// Constant buffers
winrt::com_ptr<ID3D11Buffer> m_constantBufferNeverChanges;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangeOnResize;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangesEveryFrame;
winrt::com_ptr<ID3D11Buffer> m_constantBufferChangesEveryPrim;
// Texture sampler
winrt::com_ptr<ID3D11SamplerState> m_samplerLinear;
// Shader objects: Vertex shaders and pixel shaders
winrt::com_ptr<ID3D11VertexShader> m_vertexShader;
winrt::com_ptr<ID3D11VertexShader> m_vertexShaderFlat;
winrt::com_ptr<ID3D11PixelShader> m_pixelShader;
winrt::com_ptr<ID3D11PixelShader> m_pixelShaderFlat;
winrt::com_ptr<ID3D11InputLayout> m_vertexLayout;
};
Constructor
ここでは、サンプル ゲームの GameRenderer コンストラクターについて説明し、DirectX 11 アプリ テンプレートで提供されている Sample3DSceneRenderer コンストラクターと比較してみます。
// Constructor method of the main rendering class object
GameRenderer::GameRenderer(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
m_gameInfoOverlay(deviceResources),
m_gameHud(deviceResources, L"Windows platform samples", L"DirectX first-person game sample")
{
// m_gameInfoOverlay is a GameHud object to render text in the top left corner of the screen.
// m_gameHud is Game info rendered as an overlay on the top-right corner of the screen,
// for example hits, shots, and time.
CreateDeviceDependentResources();
CreateWindowSizeDependentResources();
}
DirectX グラフィック リソースを作成して読み込む
サンプル ゲーム (および Visual Studio の DirectX 11 アプリ (ユニバーサル Windows) テンプレート) では、ゲームのリソースの作成と読み込みは、GameRenderer コンス トラクターから呼び出される次の 2 つのメソッドを使用して実装されます。
CreateDeviceDependentResources メソッド
DirectX 11 アプリ テンプレートでは、このメソッドを使用して、頂点シェーダーとピクセル シェーダーを非同期的に読み込み、シェーダーと定数バッファーを作成し、位置と色情報を含む頂点を含むメッシュを作成します。
サンプル ゲームでは、シーン オブジェクトのこれらの操作は、代わりに CreateGameDeviceResourcesAsync および FinalizeCreateGameDeviceResources メソッドに分割されます。
このサンプル ゲームでは、このメソッドに何が含まれているでしょうか。
- リソースを非同期的に読み込んでいるため、レンダリングに進む前にリソースが読み込まれているかどうかを示す、インスタンス化された変数 (m_gameResourcesLoaded = false と m_levelResourcesLoaded = false)。
- HUD とオーバーレイレンダリングは別々のクラス オブジェクト内に存在するため、ここで GameHud::CreateDeviceDependentResources および GameInfoOverlay::CreateDeviceDependentResources メソッドを呼び出します。
GameRenderer::CreateDeviceDependentResources のコードを次に示します。
// This method is called in GameRenderer constructor when it's created in GameMain constructor.
void GameRenderer::CreateDeviceDependentResources()
{
// instantiate variables that indicate whether resources were loaded.
m_gameResourcesLoaded = false;
m_levelResourcesLoaded = false;
// game HUD and overlay are design as separate class objects.
m_gameHud.CreateDeviceDependentResources();
m_gameInfoOverlay.CreateDeviceDependentResources();
}
次に、リソースを作成して読み込むために使用するメソッドを示します。
- CreateDeviceDependentResources
- CreateGameDeviceResourcesAsync (追加)
- FinalizeCreateGameDeviceResources (追加)
- CreateWindowSizeDependentResources
リソースの作成と読み込みに使用される他のメソッドについて説明する前に、最初にレンダラーを作成し、それがゲーム ループにどのように適合するかを確認しましょう。
レンダラーを作成する
GameRenderer は、GameMain のコンストラクターに作成されます。 また、リソースの作成と読み込みに役立つ CreateGameDeviceResourcesAsync と FinalizeCreateGameDeviceResources の 2 つの他のメソッドも呼び出します。
GameMain::GameMain(std::shared_ptr<DX::DeviceResources> const& deviceResources) : ...
{
m_deviceResources->RegisterDeviceNotify(this);
// Creation of GameRenderer
m_renderer = std::make_shared<GameRenderer>(m_deviceResources);
...
ConstructInBackground();
}
winrt::fire_and_forget GameMain::ConstructInBackground()
{
...
// Asynchronously initialize the game class and load the renderer device resources.
// By doing all this asynchronously, the game gets to its main loop more quickly
// and in parallel all the necessary resources are loaded on other threads.
m_game->Initialize(m_controller, m_renderer);
co_await m_renderer->CreateGameDeviceResourcesAsync(m_game);
// The finalize code needs to run in the same thread context
// as the m_renderer object was created because the D3D device context
// can ONLY be accessed on a single thread.
// co_await of an IAsyncAction resumes in the same thread context.
m_renderer->FinalizeCreateGameDeviceResources();
InitializeGameState();
...
}
CreateGameDeviceResourcesAsync メソッド
ゲームのリソースを非同期的に読み込んでいるため、create_task ループで GameMain コンストラクター メソッドから CreateGameDeviceResourcesAsync を呼び出します。
CreateDeviceResourcesAsync は、ゲーム リソースを読み込む非同期タスクの個別のセットとして実行されるメソッドです。 別のスレッドで実行することが想定されているため、Direct3D 11 デバイス メソッド ( ID3D11Device で定義されているもの) にのみアクセスでき、デバイス コンテキスト メソッド ( ID3D11DeviceContext で定義されているメソッド) にはアクセスできないため、レンダリングは実行されません。
FinalizeCreateGameDeviceResources メソッドはメイン スレッドで実行され、Direct3D 11 デバイス コンテキスト メソッドにアクセスできます。
原則:
- CreateGameDeviceResourcesAsyncID3D11Device メソッドのみを使用します。これは、フリースレッドであるためです。つまり、任意のスレッドで実行できます。 また、 GameRenderer が作成されたものと同じスレッドで実行されないことも想定されます。
- ここでは ID3D11DeviceContext メソッドは、 GameRenderer と同じスレッドで実行する必要があるため、使用しないでください。
- 定数バッファーを作成するには、このメソッドを使用します。
- このメソッドを使用して、テクスチャ (.dds ファイルなど) とシェーダー情報 (.cso ファイルなど) を シェーダーに読み込みます。
このメソッドは、次の用途に使用されます。
- 4 つの定数バッファー (m_constantBufferNeverChanges、m_constantBufferChangeOnResize、m_constantBufferChangesEveryFrame、m_constantBufferChangesEveryPrim) を作成します。
- テクスチャのサンプリング情報をカプセル化する sampler-state オブジェクトを作成する
- メソッドによって作成されたすべての非同期タスクを含むタスク グループを作成します。 これらの非同期タスクがすべて完了するまで待機し、 FinalizeCreateGameDeviceResources を呼び出します。
- Basic Loader を使用してローダーを作成します。 ローダーの非同期読み込み操作をタスクとして、先ほど作成したタスク グループに追加します。
- BasicLoader::LoadShaderAsync や BasicLoader::LoadTextureAsync などのメソッドを使用して読み込みます。
- コンパイル済みのシェーダー オブジェクト (VertextShader.cso、VertexShaderFlat.cso、PixelShader.cso、PixelShaderFlat.cso)。 詳細については、「 のシェーダー ファイル形式を参照してください。
- ゲーム固有のテクスチャ (Assets\seafloor.dds、metal_texture.dds、cellceiling.dds、cellfloor.dds、cellwall.dds)。
IAsyncAction GameRenderer::CreateGameDeviceResourcesAsync(_In_ std::shared_ptr<Simple3DGame> game)
{
auto lifetime = shared_from_this();
// Create the device dependent game resources.
// Only the d3dDevice is used in this method. It is expected
// to not run on the same thread as the GameRenderer was created.
// Create methods on the d3dDevice are free-threaded and are safe while any methods
// in the d3dContext should only be used on a single thread and handled
// in the FinalizeCreateGameDeviceResources method.
m_game = game;
auto d3dDevice = m_deviceResources->GetD3DDevice();
// Define D3D11_BUFFER_DESC. See
// https://learn.microsoft.com/windows/win32/api/d3d11/ns-d3d11-d3d11_buffer_desc
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
// Create the constant buffers.
bd.Usage = D3D11_USAGE_DEFAULT;
...
// Create the constant buffers: m_constantBufferNeverChanges, m_constantBufferChangeOnResize,
// m_constantBufferChangesEveryFrame, m_constantBufferChangesEveryPrim
// CreateBuffer is used to create one of these buffers: vertex buffer, index buffer, or
// shader-constant buffer. For CreateBuffer API ref info, see
// https://learn.microsoft.com/windows/win32/api/d3d11/nf-d3d11-id3d11device-createbuffer.
winrt::check_hresult(
d3dDevice->CreateBuffer(&bd, nullptr, m_constantBufferNeverChanges.put())
);
...
// Define D3D11_SAMPLER_DESC. For API ref, see
// https://learn.microsoft.com/windows/win32/api/d3d11/ns-d3d11-d3d11_sampler_desc.
D3D11_SAMPLER_DESC sampDesc;
// ZeroMemory fills a block of memory with zeros. For API ref, see
// https://learn.microsoft.com/previous-versions/windows/desktop/legacy/aa366920(v=vs.85).
ZeroMemory(&sampDesc, sizeof(sampDesc));
sampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
sampDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
sampDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
...
// Create a sampler-state object that encapsulates sampling information for a texture.
// The sampler-state interface holds a description for sampler state that you can bind to any
// shader stage of the pipeline for reference by texture sample operations.
winrt::check_hresult(
d3dDevice->CreateSamplerState(&sampDesc, m_samplerLinear.put())
);
// Start the async tasks to load the shaders and textures.
// Load compiled shader objects (VertextShader.cso, VertexShaderFlat.cso, PixelShader.cso, and PixelShaderFlat.cso).
// The BasicLoader class is used to convert and load common graphics resources, such as meshes, textures,
// and various shader objects into the constant buffers. For more info, see
// https://learn.microsoft.com/windows/uwp/gaming/complete-code-for-basicloader.
BasicLoader loader{ d3dDevice };
std::vector<IAsyncAction> tasks;
uint32_t numElements = ARRAYSIZE(PNTVertexLayout);
// Load shaders asynchronously with the shader and pixel data using the
// BasicLoader::LoadShaderAsync method. Push these method calls into a list of tasks.
tasks.push_back(loader.LoadShaderAsync(L"VertexShader.cso", PNTVertexLayout, numElements, m_vertexShader.put(), m_vertexLayout.put()));
tasks.push_back(loader.LoadShaderAsync(L"VertexShaderFlat.cso", nullptr, numElements, m_vertexShaderFlat.put(), nullptr));
tasks.push_back(loader.LoadShaderAsync(L"PixelShader.cso", m_pixelShader.put()));
tasks.push_back(loader.LoadShaderAsync(L"PixelShaderFlat.cso", m_pixelShaderFlat.put()));
// Make sure the previous versions if any of the textures are released.
m_sphereTexture = nullptr;
...
// Load Game specific textures (Assets\\seafloor.dds, metal_texture.dds, cellceiling.dds,
// cellfloor.dds, cellwall.dds).
// Push these method calls also into a list of tasks.
tasks.push_back(loader.LoadTextureAsync(L"Assets\\seafloor.dds", nullptr, m_sphereTexture.put()));
...
// Simulate loading additional resources by introducing a delay.
tasks.push_back([]() -> IAsyncAction { co_await winrt::resume_after(GameConstants::InitialLoadingDelay); }());
// Returns when all the async tasks for loading the shader and texture assets have completed.
for (auto&& task : tasks)
{
co_await task;
}
}
FinalizeCreateGameDeviceResources メソッド
FinalizeCreateGameDeviceResources メソッドは、 CreateGameDeviceResourcesAsync メソッド内のすべての読み込みリソース タスクが完了した後に呼び出されます。
- ライトの位置と色を使用して constantBufferNeverChanges を初期化します。 ID3D11DeviceContext::UpdateSubresource へのデバイス コンテキスト メソッド呼び出しを使用して、初期データを定数バッファーに読み込みます。
- 非同期に読み込まれたリソースの読み込みが完了したので、適切なゲーム オブジェクトに関連付けます。
- ゲーム オブジェクトごとに、読み込まれたテクスチャを使用してメッシュとマテリアルを作成します。 次に、メッシュとマテリアルをゲーム オブジェクトに関連付けます。
- ターゲット ゲーム オブジェクトの場合、同心円色のリングで構成されるテクスチャは、上部に数値が表示され、テクスチャ ファイルから読み込まれません。 代わりに、 TargetTexture.cppのコードを使用して手続き的に生成されます。 TargetTexture クラスは、初期化時にテクスチャをオフスクリーン リソースに描画するために必要なリソースを作成します。 結果のテクスチャは、適切なターゲット ゲーム オブジェクトに関連付けられます。
FinalizeCreateGameDeviceResources および CreateWindowSizeDependentResources は、次のようなコード部分を共有します。
- SetProjParamsを使用して、カメラが正しい投影マトリックスを持っていることを確認します。 詳しくは、「 カメラと座標空間」をご覧ください。
- 3D 回転マトリックスをカメラの投影マトリックスに乗算して、画面の回転を処理します。 次に、 ConstantBufferChangeOnResize 定数バッファーを結果のプロジェクション マトリックスで更新します。
- m_gameResourcesLoaded Boolean グローバル変数を設定して、リソースがバッファーに読み込まれ、次の手順に進む準備ができたことを示します。 最初に、この変数を GameRenderer のコンストラクター メソッドの FALSE として、GameRenderer::CreateDeviceDependentResources メソッドを使用して初期化したことを思い出してください。
- この m_gameResourcesLoaded が TRUE である場合、シーン オブジェクトのレンダリングを実行できます。 これについては、GameRenderer::Render メソッドのレンダリングフレームワーク I: レンダリングの概要記事で説明。
// This method is called from the GameMain constructor.
// Make sure that 2D rendering is occurring on the same thread as the main rendering.
void GameRenderer::FinalizeCreateGameDeviceResources()
{
// All asynchronously loaded resources have completed loading.
// Now associate all the resources with the appropriate game objects.
// This method is expected to run in the same thread as the GameRenderer
// was created. All work will happen behind the "Loading ..." screen after the
// main loop has been entered.
// Initialize the Constant buffer with the light positions
// These are handled here to ensure that the d3dContext is only
// used in one thread.
auto d3dDevice = m_deviceResources->GetD3DDevice();
ConstantBufferNeverChanges constantBufferNeverChanges;
constantBufferNeverChanges.lightPosition[0] = XMFLOAT4(3.5f, 2.5f, 5.5f, 1.0f);
...
constantBufferNeverChanges.lightColor = XMFLOAT4(0.25f, 0.25f, 0.25f, 1.0f);
// CPU copies data from memory (constantBufferNeverChanges) to a subresource
// created in non-mappable memory (m_constantBufferNeverChanges) which was created in the earlier
// CreateGameDeviceResourcesAsync method. For UpdateSubresource API ref info,
// go to: https://msdn.microsoft.com/library/windows/desktop/ff476486.aspx
// To learn more about what a subresource is, go to:
// https://msdn.microsoft.com/library/windows/desktop/ff476901.aspx
m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
m_constantBufferNeverChanges.get(),
0,
nullptr,
&constantBufferNeverChanges,
0,
0
);
// For the objects that function as targets, they have two unique generated textures.
// One version is used to show that they have never been hit and the other is
// used to show that they have been hit.
// TargetTexture is a helper class to procedurally generate textures for game
// targets. The class creates the necessary resources to draw the texture into
// an off screen resource at initialization time.
TargetTexture textureGenerator(
d3dDevice,
m_deviceResources->GetD2DFactory(),
m_deviceResources->GetDWriteFactory(),
m_deviceResources->GetD2DDeviceContext()
);
// CylinderMesh is a class derived from MeshObject and creates a ID3D11Buffer of
// vertices and indices to represent a canonical cylinder (capped at
// both ends) that is positioned at the origin with a radius of 1.0,
// a height of 1.0 and with its axis in the +Z direction.
// In the game sample, there are various types of mesh types:
// CylinderMesh (vertical rods), SphereMesh (balls that the player shoots),
// FaceMesh (target objects), and WorldMesh (Floors and ceilings that define the enclosed area)
auto cylinderMesh = std::make_shared<CylinderMesh>(d3dDevice, (uint16_t)26);
...
// The Material class maintains the properties that represent how an object will
// look when it is rendered. This includes the color of the object, the
// texture used to render the object, and the vertex and pixel shader that
// should be used for rendering.
auto cylinderMaterial = std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, .5f),
XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f),
15.0f,
m_cylinderTexture.get(),
m_vertexShader.get(),
m_pixelShader.get()
);
...
// Attach the textures to the appropriate game objects.
// We'll loop through all the objects that need to be rendered.
for (auto&& object : m_game->RenderObjects())
{
if (object->TargetId() == GameConstants::WorldFloorId)
{
// Assign a normal material for the floor object.
// This normal material uses the floor texture (cellfloor.dds) that was loaded asynchronously from
// the Assets folder using BasicLoader::LoadTextureAsync method in the earlier
// CreateGameDeviceResourcesAsync loop
object->NormalMaterial(
std::make_shared<Material>(
XMFLOAT4(0.5f, 0.5f, 0.5f, 1.0f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 1.0f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
150.0f,
m_floorTexture.get(),
m_vertexShaderFlat.get(),
m_pixelShaderFlat.get()
)
);
// Creates a mesh object called WorldFloorMesh and assign it to the floor object.
object->Mesh(std::make_shared<WorldFloorMesh>(d3dDevice));
}
...
else if (auto cylinder = dynamic_cast<Cylinder*>(object.get()))
{
cylinder->Mesh(cylinderMesh);
cylinder->NormalMaterial(cylinderMaterial);
}
else if (auto target = dynamic_cast<Face*>(object.get()))
{
const int bufferLength = 16;
wchar_t str[bufferLength];
int len = swprintf_s(str, bufferLength, L"%d", target->TargetId());
auto string{ winrt::hstring(str, len) };
winrt::com_ptr<ID3D11ShaderResourceView> texture;
textureGenerator.CreateTextureResourceView(string, texture.put());
target->NormalMaterial(
std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
5.0f,
texture.get(),
m_vertexShader.get(),
m_pixelShader.get()
)
);
texture = nullptr;
textureGenerator.CreateHitTextureResourceView(string, texture.put());
target->HitMaterial(
std::make_shared<Material>(
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.8f, 0.8f, 0.8f, 0.5f),
XMFLOAT4(0.3f, 0.3f, 0.3f, 1.0f),
5.0f,
texture.get(),
m_vertexShader.get(),
m_pixelShader.get()
)
);
target->Mesh(targetMesh);
}
...
}
// The SetProjParams method calculates the projection matrix based on input params and
// ensures that the camera has been initialized with the right projection
// matrix.
// The camera is not created at the time the first window resize event occurs.
auto renderTargetSize = m_deviceResources->GetRenderTargetSize();
m_game->GameCamera().SetProjParams(
XM_PI / 2,
renderTargetSize.Width / renderTargetSize.Height,
0.01f,
100.0f
);
// Make sure that the correct projection matrix is set in the ConstantBufferChangeOnResize buffer.
// Get the 3D rotation transform matrix. We are handling screen rotations directly to eliminate an unaligned
// fullscreen copy. So it is necessary to post multiply the 3D rotation matrix to the camera's projection matrix
// to get the projection matrix that we need.
auto orientation = m_deviceResources->GetOrientationTransform3D();
ConstantBufferChangeOnResize changesOnResize;
// The matrices are transposed due to the shader code expecting the matrices in the opposite
// row/column order from the DirectX math library.
// XMStoreFloat4x4 takes a matrix and writes the components out to sixteen single-precision floating-point values at the given address.
// The most significant component of the first row vector is written to the first four bytes of the address,
// followed by the second most significant component of the first row, and so on. The second row is then written out in a
// like manner to memory beginning at byte 16, followed by the third row to memory beginning at byte 32, and finally
// the fourth row to memory beginning at byte 48. For more API ref info, go to:
// https://msdn.microsoft.com/library/windows/desktop/microsoft.directx_sdk.storing.xmstorefloat4x4.aspx
XMStoreFloat4x4(
&changesOnResize.projection,
XMMatrixMultiply(
XMMatrixTranspose(m_game->GameCamera().Projection()),
XMMatrixTranspose(XMLoadFloat4x4(&orientation))
)
);
// UpdateSubresource method instructs CPU to copy data from memory (changesOnResize) to a subresource
// created in non-mappable memory (m_constantBufferChangeOnResize ) which was created in the earlier
// CreateGameDeviceResourcesAsync method.
m_deviceResources->GetD3DDeviceContext()->UpdateSubresource(
m_constantBufferChangeOnResize.get(),
0,
nullptr,
&changesOnResize,
0,
0
);
// Finally we set the m_gameResourcesLoaded as TRUE, so we can start rendering.
m_gameResourcesLoaded = true;
}
CreateWindowSizeDependentResource メソッド
CreateWindowSizeDependentResources メソッドは、ウィンドウ サイズ、向き、ステレオ対応レンダリング、または解像度が変更されるたびに呼び出されます。 サンプル ゲームでは、ConstantBufferChangeOnResize でプロジェクション マトリックスを更新します。
ウィンドウ サイズのリソースは、次の方法で更新されます。
- アプリ フレームワークは、ウィンドウの状態の変化を示すいくつかのイベントの 1 つを取得します。
- メイン ゲーム ループはイベントについて通知され、メイン クラス (GameMain) インスタンスでCreateWindowSizeDependentResources を呼び出し、ゲーム レンダラー (GameRenderer) クラスの CreateWindowSizeDependentResources 実装を呼び出します。
- このメソッドの主なジョブは、ウィンドウのプロパティが変更されたために、ビジュアルが混乱したり無効にならないようにすることです。
このサンプル ゲームでは、多くのメソッド呼び出しが FinalizeCreateGameDeviceResources メソッドと同じです。 コードチュートリアルについては、前のセクションを参照してください。
ゲーム HUD とオーバーレイ ウィンドウ サイズのレンダリング調整については、「 ユーザー インターフェイスの追加」で説明します。
// Initializes view parameters when the window size changes.
void GameRenderer::CreateWindowSizeDependentResources()
{
// Game HUD and overlay window size rendering adjustments are done here
// but they'll be covered in the UI section instead.
m_gameHud.CreateWindowSizeDependentResources();
...
auto d3dContext = m_deviceResources->GetD3DDeviceContext();
// In Sample3DSceneRenderer::CreateWindowSizeDependentResources, we had:
// Size outputSize = m_deviceResources->GetOutputSize();
auto renderTargetSize = m_deviceResources->GetRenderTargetSize();
...
m_gameInfoOverlay.CreateWindowSizeDependentResources(m_gameInfoOverlaySize);
if (m_game != nullptr)
{
// Similar operations as the last section of FinalizeCreateGameDeviceResources method
m_game->GameCamera().SetProjParams(
XM_PI / 2, renderTargetSize.Width / renderTargetSize.Height,
0.01f,
100.0f
);
XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();
ConstantBufferChangeOnResize changesOnResize;
XMStoreFloat4x4(
&changesOnResize.projection,
XMMatrixMultiply(
XMMatrixTranspose(m_game->GameCamera().Projection()),
XMMatrixTranspose(XMLoadFloat4x4(&orientation))
)
);
d3dContext->UpdateSubresource(
m_constantBufferChangeOnResize.get(),
0,
nullptr,
&changesOnResize,
0,
0
);
}
}
次のステップ
これは、ゲームのグラフィックス レンダリング フレームワークを実装するための基本的なプロセスです。 ゲームが大きいほど、オブジェクトの種類とアニメーションの動作の階層を処理するために、より多くの抽象化を配置する必要があります。 メッシュやテクスチャなどのアセットを読み込んで管理するには、より複雑なメソッドを実装する必要があります。 次に、ユーザー インターフェイス 追加する方法について説明します。