その5
第5章:モデルをシャドウマップに描けるようにする
この章の目的
通常描画用の Draw() とは別に、シャドウマップ作成用の DrawShadow() を追加する。
この章でも画面は変わらない。
考え方
普通の Draw() は、画面に表示するための描画。
Draw()
色を出す
テクスチャを読む
ライト計算をする
カメラ視点で描く
シャドウマップ用の描画では、色はいらない。
DrawShadow()
色を出さない
テクスチャを読まない
ライト計算もしない
ライト視点のWVPだけ使う
変更ファイル
Engine/Fbx.hEngine/Fbx.cppEngine/Model.hEngine/Model.cpp
実装指示
ステンシルは使わないシャドウマップ実装の第5章です。
FbxとModelに、シャドウマップ生成用の DrawShadow を追加してください。
変更内容:
1. Engine/Fbx.h に void DrawShadow(Transform& transform); を追加する。
2. Engine/Fbx.h の private に、シャドウ用コンスタントバッファ構造体を追加する。
- struct CB_SHADOW { XMMATRIX matLightWVP; };
3. Engine/Fbx.h に ID3D11Buffer* pShadowConstantBuffer_; を追加する。
4. Engine/Fbx.cpp の Fbx コンストラクタで pShadowConstantBuffer_ を nullptr 初期化する。
5. Engine/Fbx.cpp の InitConstantBuffer() で、pShadowConstantBuffer_ を作成する。
- ByteWidth = sizeof(CB_SHADOW)
- Usage = D3D11_USAGE_DYNAMIC
- BindFlags = D3D11_BIND_CONSTANT_BUFFER
- CPUAccessFlags = D3D11_CPU_ACCESS_WRITE
6. Engine/Fbx.cpp に DrawShadow(Transform& transform) を実装する。
- transform.Calculation() を呼ぶ。
- 頂点バッファをセットする。
- matLightWVP = World * Direct3D::GetLightViewMatrix() * Direct3D::GetLightProjectionMatrix() を計算する。
- pShadowConstantBuffer_ に CB_SHADOW を Map / memcpy_s / Unmap で送る。
- VSSetConstantBuffers(0, 1, &pShadowConstantBuffer_) でセットする。
- マテリアルごとのインデックスバッファをセットして DrawIndexed する。
- テクスチャやマテリアル色は使わない。
7. Engine/Model.h に void DrawShadow(int hModel); を追加する。
8. Engine/Model.cpp に Model::DrawShadow(int hModel) を追加し、内部で Fbx::DrawShadow を呼ぶ。
既存の Draw() を壊さないでください。
実装の中心
XMMATRIX matLightWVP = transform.GetWorldMatrix()
* Direct3D::GetLightViewMatrix()
* Direct3D::GetLightProjectionMatrix();
これは、モデルの頂点をライト視点に変換するための行列。
モデル座標
↓ World
ワールド座標
↓ Light View
ライトから見た座標
↓ Light Projection
ライト画面上の座標
この章でのコード変更点
この章でやること
シャドウマップにモデルを描くための関数 DrawShadow() を追加する。
既存の Draw() は一切変更しない。新しい関数を追加するだけ。
変更の概要
| ファイル | 変更内容 |
|---|---|
Engine/Fbx.h |
DrawShadow() 宣言・CB_SHADOW 構造体・pShadowConstantBuffer_ 追加 |
Engine/Fbx.cpp |
コンストラクタ初期化・InitConstantBuffer() 修正・DrawShadow() 実装 |
Engine/Model.h |
DrawShadow(int hModel) 宣言追加 |
Engine/Model.cpp |
DrawShadow() 実装 |
画面は変わらない。まだ DrawShadow() はどこからも呼ばれていないため。
Engine/Fbx.h の変更
ここでやること
DrawShadow() 関数の宣言と、それに必要なバッファの定義を追加する。
CB_SHADOW 構造体をなぜ別に作るか
既存の CONSTANT_BUFFER はこれだけのデータを持っている:
struct CONSTANT_BUFFER
{
XMMATRIX matWVP; // カメラ視点のWVP
XMMATRIX matWorld; // ワールド行列
XMMATRIX matNormal; // 法線変換行列
XMFLOAT4 diffuse; // 色
// ...(たくさん)
};
シャドウマップではライト視点のWVPしか要らない。
色も法線も何もいらない。
struct CB_SHADOW
{
XMMATRIX matLightWVP; // これだけ
};
色やテクスチャを送らない分、軽くて速い。
pShadowConstantBuffer_ をなぜ別に持つか
pConstantBuffer_(通常描画用)を使い回せばいいと思うかもしれないが、それはできない。
理由:バッファのサイズが違う
pConstantBuffer_ → sizeof(CONSTANT_BUFFER) 大きい
pShadowConstantBuffer_ → sizeof(CB_SHADOW) 小さい
サイズが違うバッファを使い回すと、シェーダー側が期待するデータ配置と合わなくなる。
だからシャドウ用のバッファを別に作る。
After(変更後)
class Fbx
{
public:
// ...
void DrawShadow(Transform& transform); // ← 追加
private:
// ...
struct CB_SHADOW
{
XMMATRIX matLightWVP; // ← 追加:ライト視点のWVP行列
};
ID3D11Buffer* pShadowConstantBuffer_; // ← 追加:シャドウ用バッファ
};
Engine/Fbx.cpp の変更
ここでやること
① コンストラクタで pShadowConstantBuffer_ を nullptr 初期化する。
② InitConstantBuffer() でシャドウ用バッファを作成する。
③ DrawShadow() を実装する。
① コンストラクタの初期化
Fbx::Fbx()
{
// ...既存の初期化...
pShadowConstantBuffer_ = nullptr; // ← 追加
}
② InitConstantBuffer() への追加
// シャドウ用コンスタントバッファを作成する
D3D11_BUFFER_DESC cbd = {};
cbd.ByteWidth = sizeof(CB_SHADOW); // CB_SHADOWのサイズ
cbd.Usage = D3D11_USAGE_DYNAMIC; // CPUから毎フレーム書き換える
cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; // コンスタントバッファとして使う
cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // CPUが書き込める
Direct3D::pDevice->CreateBuffer(&cbd, nullptr, &pShadowConstantBuffer_);
③ DrawShadow() の実装
void Fbx::DrawShadow(Transform& transform)
{
// ワールド行列を計算する
transform.Calculation();
// 頂点バッファをセットする(通常のDrawと同じ)
UINT stride = sizeof(VERTEX);
UINT offset = 0;
Direct3D::pContext->IASetVertexBuffers(0, 1, &pVertexBuffer_, &stride, &offset);
// ライト視点のWVP行列を作る
// モデル座標 → ワールド → ライト視点 → ライト画面
XMMATRIX matLightWVP = transform.GetWorldMatrix()
* Direct3D::GetLightViewMatrix()
* Direct3D::GetLightProjectionMatrix();
// CB_SHADOW にデータを詰めてGPUに送る
CB_SHADOW cb;
cb.matLightWVP = matLightWVP;
// Map:GPUのバッファをCPUから書き込めるように開く
D3D11_MAPPED_SUBRESOURCE pdata;
Direct3D::pContext->Map(pShadowConstantBuffer_, 0, D3D11_MAP_WRITE_DISCARD, 0, &pdata);
// memcpy_s:データをコピーする
memcpy_s(pdata.pData, pdata.RowPitch, &cb, sizeof(cb));
// Unmap:書き込みを終了してGPUに返す
Direct3D::pContext->Unmap(pShadowConstantBuffer_, 0);
// バッファを頂点シェーダーの b0 スロットにセット
Direct3D::pContext->VSSetConstantBuffers(0, 1, &pShadowConstantBuffer_);
// マテリアルごとに描画する(色・テクスチャは使わない)
for (int i = 0; i < materialCount_; i++)
{
Direct3D::pContext->IASetIndexBuffer(pIndexBuffer_[i], DXGI_FORMAT_R32_UINT, 0);
Direct3D::pContext->DrawIndexed(indexCount_[i], 0, 0);
}
}
Engine/Model.h / Engine/Model.cpp の変更
ここでやること
Fbx::DrawShadow() を外から呼べるように Model::DrawShadow() を追加する。
// Model.h に追加
void DrawShadow(int hModel);
// Model.cpp に追加
void Model::DrawShadow(int hModel)
{
modelList[hModel]->pfbx_->DrawShadow(modelList[hModel]->transform_);
}
Stage.cpp からは Model::DrawShadow(hDonut_) のように呼ぶ(6章で追加)。
ビルド確認
- ビルドが通れば成功。
- 画面は変わらない。
- まだ
DrawShadow()は呼ばれていないため、見た目に変化はない。
No Comments