【ゲーム制作】No.10: 動的頂点バッファ/インデックスバッファの設定について

お久しぶりです。前回の更新が2021年10月13日ですから、約17ヶ月ぶりの更新になってしまいました。
いろいろ生活は代わりましたが、ゲーム開発はそのまま続けています。

環境もDirectX11( Microsoft DirectX SDK (June 2010) ) + Windows11のままです。
今後も定期的に更新をしていきたいと思っています。

今日のテーマは動的な頂点バッファとインデックスバッファの設定方法についてです。

自分は今、動的LOD機能の制作を進めており(これも近いうちに公開したいと思っています)、

その為に頂点バッファとインデックスバッファの動的な読み取りと書込みが必要だったのですが、

これをどう設定するのが良いのかいろいろ苦労した為、それの備忘録になります。

!!注意!!:これは自分の環境での設定です。他の環境での動作は未確認です。


【パターン1】頂点バッファとインデックスバッファで動的な読み取りと書込みを行う場合(イレギュラーな方法)

下記の設定でバッファを作ると、バッファは動的な読み取りと書込みを行うことが出来ます。

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth         = バッファのサイズ;
buffer_desc.Usage                = D3D11_USAGE_DEFAULT;
buffer_desc.BindFlags           = D3D11_BIND_UNORDERED_ACCESS;
buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
buffer_desc.MiscFlags           = 0;
buffer_desc.StructureByteStride = 0;

D3D11_SUBRESOURCE_DATA subresource = { 0 };
subresource.pSysMem          = 開始アドレス;
subresource.SysMemPitch      = 0;
subresource.SysMemSlicePitch = 0;

p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &m_p_vertex_buffer);


特徴として、

①D3D11_BIND_UNORDERED_ACCESSという正しくない設定を使っています。
 動いたから良いかレベルのイレギュラーな設定です。

②D3D11_BIND_VERTEX_BUFFERやD3D11_BIND_INDEX_BUFFERと合わせて使うことは出来ません。

その為、ID3D11DeviceContext::IASetVertexBuffers()やID3D11DeviceContext::IASetIndexBuffer()を使うと
D3D11_BIND_VERTEX_BUFFERやD3D11_BIND_INDEX_BUFFERが立っていない為に、デバッグウインドウに以下のような警告が出るようになります。

D3D11 ERROR: ID3D11DeviceContext::IASetVertexBuffers: A Buffer trying to be bound to slot 0 did not have the appropriate bind flag set at creation time to allow the Buffer to be bound as a VertexBuffer. [ STATE_SETTING ERROR #238: IASETVERTEXBUFFERS_INVALIDBUFFER]

デバッグビルトでは表示コスト以外に影響はありませんが、リファレンスドライバー(D3D_DRIVER_TYPE_REFERENCE)などで作成していると警告ではなくエラーになります。
この警告を修正する為には次のパターン2の方法に切り替える必要があります。


【パターン2】頂点バッファとインデックスバッファで動的な読み取りと書込みを行う場合(より一般的な方法)

上記設定を踏まえて、より一般的な方法として動的に読み取りと書込みを行う頂点バッファとインデックスバッファを作る為には、
動的な読み取りと動的な書込みに分けて設計する必要があります。

動的な読み取り:D3D10_USAGE_STAGINGを使用してバッファを作成します。読み取り前にGPU→CPUにメモリをコピーし、このバッファのMap()でデータの読み取りを行います。

動的な書き込み:D3D11_USAGE_DYNAMICを使用してバッファを作成します。このバッファのMap()でデータの書き込みを行います。


// 1,D3D11_USAGE_DYNAMICを使用してバッファーを作成

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth           = バッファのサイズ;
buffer_desc.Usage               = D3D11_USAGE_DYNAMIC;
buffer_desc.BindFlags           = D3D11_BIND_INDEX_BUFFER;    // 警告を修正する為
buffer_desc.CPUAccessFlags      = D3D11_CPU_ACCESS_WRITE;    // 書き込みを行う為
buffer_desc.MiscFlags           = 0;
buffer_desc.StructureByteStride = 0;

D3D11_SUBRESOURCE_DATA subresource = { 0 };
subresource.pSysMem          = 開始アドレス;
subresource.SysMemPitch      = 0;
subresource.SysMemSlicePitch = 0;

p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &p_index_buffer);


// 動的な読み取りを行う場合
//
// 2,D3D11_USAGE_STAGINGを使用してバッファーを作成
ID3D11BufferPtr    p_index_buffer_cpu_staging    = NULL;

D3D11_BUFFER_DESC    buffer_desc = { 0 };
buffer_desc.ByteWidth            = バッファのサイズ;
buffer_desc.Usage                = D3D11_USAGE_STAGING;    // GPU→CPUメモリコピーの為
buffer_desc.BindFlags            = 0;
buffer_desc.CPUAccessFlags        = D3D11_CPU_ACCESS_READ;// 読み取りを行う為
buffer_desc.MiscFlags            = 0;
buffer_desc.StructureByteStride    = 0;

uint8_collection    NULL_buffer( バッファのサイズ, 0);

D3D11_SUBRESOURCE_DATA    subresource = { 0 };
subresource.pSysMem = &NULL_buffer.front();
subresource.SysMemPitch = 0;
subresource.SysMemSlicePitch = 0;

hr = p_d3ddevice->CreateBuffer(&buffer_desc, &subresource, &p_index_buffer_cpu_staging);

// 3,頂点バッファからD3D11_USAGE_STAGINGバッファへ頂点データをコピー

p_device_context->CopyResource( p_index_buffer_cpu_staging, p_index_buffer);

// 4,頂点データをコピーされたD3D11_USAGE_STAGINGバッファをMap()し読み出す

D3D11_MAPPED_SUBRESOURCE    mapped_resource    = { 0 };

hr = p_device_context->Map(
    p_index_buffer_cpu_staging,
    0,
    D3D11_MAP_READ,
    0,
    &mapped_resource);


// 動的な書込みを行う場合
//
// 5,D3D11_USAGE_DYNAMICバッファをMap()し書き込む

D3D11_MAPPED_SUBRESOURCE    mapped_resource    = { 0 };

hr = p_device_context->Map(
    p_index_buffer,
    0,
    D3D11_MAP_WRITE_DISCARD,
    0,
    &mapped_resource);


注意点として、動的な書込みはD3D11_MAP_WRITE_DISCARDかD3D11_MAP_WRITE_NO_OVERWRITEになります。
D3D11_MAP_READ_WRITEは使用できずエラーになります。

【ゲーム制作】No.9: シャドウマッピング①

シャドウマッピングによる影生成の整備を行っています。

ただしシャドウマッピングはDirect3D9時代に作ったソースが既にありますので、作業はそれの移植がメインになります。

今はUniformShadowmap_Spotlightのシャドウマッピングは完成済で、PerspectiveShadowmap_Spotlightの移植を行っていますが、影の品質があまりよくありません。

PerspectiveShadowmapって論文で言うような高品質になるケースは少なくて大抵はUniformShadowmapの方が綺麗なんですよね。

問題点ばかりで実際には使い物になるのか?って感じです。

シャドウマッピング構築は時間がかかると思いますのでちょこちょこ進めていければと思います。

 

  • PerspectiveShadowmapの場合。あ、粗いです。

f:id:houz12345:20211013171618p:plain

 

  • UniformShadowmapの場合。解像度2048x2048ということもありとても綺麗です。

f:id:houz12345:20211013171624p:plain

 

【日記】2021年10月8日

今日は、 脳神経外科へ外来受診の日でした。

  • 次回入院して行う手術の内容を説明して頂きました。腫瘍の除去を行う手術だが言語に関わる箇所が腫瘍に沿うように存在する為、適当に取ると言語機能に障害が残る可能性がある。そのリスクを下げる為、意識を覚醒したまま脳機能のテストを行いなら腫瘍の除去を行う覚醒下手術を行う、とのことでした。

  • 気紛れで病院から徒歩で帰ったのですが(13kmほど)、途中で足の裏が痛くてギブアップ、結局電車で帰りました。

【ゲーム制作】No.8: 動的環境マッピング⑤

PMD/PMXモデルでの動的キューブ環境マッピングに対応しました。

屈折モデルへの対応などやるべき課題はまだいろいろありますが環境マップ対応は今回で終了にしたいと思います。

次回からは別課題への対応を行います。

 

f:id:houz12345:20211007202317p:plain

 

【ゲーム制作】No.7: 動的環境マッピング④

今日はPMD/PMXモデルを動的キューブ環境マップに表示する機能を追加しました。

残りはPMD/PMXモデルを反射モデルにするパターンぐらいですかね。

今回は作っていて処理の重さが気になりました。メッシュもそれほど表示してないのに30FPS切っていてこれではまともなゲームは作れそうにありません。一応処理に無駄になる箇所は入れないように気を使っているのですが、メモリの使用量はまったく気にせずSTLコンテナなどは使いまくっています。これが影響しているのかわかりませんが・・

 

こちらは物理演算を無効化した場合、

f:id:houz12345:20211006180907p:plain

 

こちらは物理演算を有効化した場合です。物理演算が無視出来ないほど重いです。

f:id:houz12345:20211006181013p:plain

 

【ゲーム制作】No.6: 動的環境マッピング③

とりあえずキューブ環境マップを表示することが出来ました。

左の球が動的キューブ環境マップ、右のその他のオブジェクトは静的キューブ環境マップになります。

 

  • 以前、動的キューブ環境マップはボックスに設定して表示すると書きましたが、その認識は誤っていました。キューブ環境マップというのは球の反射を模倣するものでボックスの反射はキューブマップではない別な手法で行う必要があるようです。それに気づいてから、まずはその方法を実装しようかとも考えましたが反射する箱を作ってもあまり使い道がないと思いましたので、標準である球によるキューブ環境マップで作る方針に切り替えました。

  • PMD/PMXはまだ動的キューブ環境マップに対応していません。これから検討します。

  • DDSファイル読み込みはDirectXTexで行っているのですが、動作がいろいろおかしい・・。これも後々検討します・・。

  • 制作途中なのでFPS表示などが消えてるなどいろいろ中途半端な状態です。後で直さなければ・・

 

f:id:houz12345:20211005122349p:plain

 

【ゲーム制作】No.5: OMSetRenderTargets()のワーニング

一応環境マッピングの作成はちゃんと進んでいるのですが

以下のワーニングが出て作業が止まっている状態です。

 

D3D11 WARNING: ID3D11DeviceContext::OMSetRenderTargets: Resource being set to OM DepthStencil is still bound on input! [ STATE_SETTING WARNING #9: DEVICE_OMSETRENDERTARGETS_HAZARD]

D3D11 WARNING: ID3D11DeviceContext::OMSetRenderTargets[AndUnorderedAccessViews]: Forcing PS shader resource slot 0 to NULL. [ STATE_SETTING WARNING #7: DEVICE_PSSETSHADERRESOURCES_HAZARD]

 

レンダーターゲットやデブスステンシルに設定したリソースが

シェーダーリソースとして使われてもいて衝突してるよ!ってことなんでしょうが

リソースは使い終わったらちゃんとリリースしているんですよね・・

わからない・・もうDirect3D嫌いです・・

 

その後調査を続けた結果、原因がわかったっぽいです。

私はEffect11をDirect3D11の管理に使用しているのですが、リソースの解放手続きに

不足がありちゃんと解放されていませんでした。

テクスチャを例にとると、

 

    ID3DX11EffectShaderResourceVariable*        m_h_texture;

    ....

    // Effect11オブジェクトにテクスチャを登録する

    //(この時点ではdirect3Dオブジェクトには登録されていない)

    m_h_texture->SetResource(m_p_srv_diffuse_map);

 

    ID3DX11EffectTechnique* h_tech = m_h_technique;
    D3DX11_TECHNIQUE_DESC    tech_desc;
    h_tech->GetDesc(&tech_desc);
     for (uint_t p = 0; p < tech_desc.Passes; ++p)
     {
            // このApply()によってテクスチャがdirect3Dオブジェクトに登録される                            h_tech->GetPassByIndex(p)->Apply(0, device_context);

            ~メッシュを描画する~
     }

     // Effect11オブジェクトからテクスチャを解放する

    //(この時点ではテクスチャはdirect3Dオブジェクトに登録されたまま

    // 今まではここまでしかやっていなかった

     m_h_texture->SetResource(NULL); 

                                                               

    // direct3Dオブジェクトから解放する為にはこのコードが必要だった

    h_tech->GetPassByIndex(0)->Apply(0, device_context); 

 

最後のApply()は空打ちのようでかっこ悪いのですが、代替え案もないので

修正はこれで行こうと思います。