007.頂点の変更とテクスチャラッピング(Dx11版)

 このサンプルはSimplSample007というディレクトリに含まれます。
 BaseCrossDx11.slnというソリューションを開くとDx11版が起動します。

 実行結果は以下のような画面が出ます。

 

図0007a

 


 動画は以下になります。

 

 

【サンプルのポイント】

 今項のサンプルのポイントは同じ描画を複数のオブジェクトで行う場合のサンプルです。例えばテクスチャやシェーダに渡すパラメータのみが違う、別のオブジェクトの場合、描画処理は1つのクラスで行ったほうが、同じようなコードをいくつも書く必要がなくなります。
 また、頂点の変更の方法も今項のテーマと言えます。ポジションを動的に変更する場合もありますが、一番ポピュラーなのはテクスチャUV値の変更でしょう。上記動画を見ればわかる通り、中心にあるオブジェクトは、テクスチャが流れるように描画されます。これは動的にテクスチャUV値の変更を行っています。

【共通解説】

 Dx12、Dx11両方に共通なのはシェーダーです。DxSharedプロジェクト内にシェーダファイルというフィルタがあり、そこに記述されてます。
 今回使用するシェーダは頂点シェーダとピクセルシェーダです。VertexPositionTexture型の頂点を持ち、コンスタントバッファからの入力で、位置を変更させています。
 またテクスチャも描画します。

【Dx11版解説】

 BaseCrossDx11.slnを開くと、BaseCrossDx11というメインプロジェクトがあります。この中のCharacter.h/cppが主な記述個所になります。
 このサンプルでは、Dx12版同様、PTSpriteDrawという描画用のクラスを作成します。背景の壁、そして中心の流れるテクスチャ両方とも同じシェーダで実装可能なので、描画処理を1箇所にまとめてあります。

■初期化■

 初期化は、頂点の初期化、インデックスの初期化、があります。各オブジェクト側の初期化はDx12版と変わりません。描画クラスPTSpriteDrawの初期化は、Dx12リソースのようなものはありませんので、テクスチャリソースの作成のみになります。

■更新処理■

 更新は、流れるテクスチャのオブジェクトのみ、頂点の更新を行います。SquareSprite::UpdateVertex()関数です。
//頂点の変更
void SquareSprite::UpdateVertex(float ElapsedTime) {
    m_TotalTime += ElapsedTime;
    if (m_TotalTime > 1.0f) {
        m_TotalTime = 0;
    }

    auto Dev = App::GetApp()->GetDeviceResources();
    auto pD3D11DeviceContext = Dev->GetD3DDeviceContext();

    //頂点の変更準備
    //D3D11_MAP_WRITE_DISCARDは重要。この処理により、GPUに邪魔されない
    D3D11_MAP mapType = D3D11_MAP_WRITE_DISCARD;
    D3D11_MAPPED_SUBRESOURCE mappedBuffer;
    //頂点のマップ
    if (FAILED(pD3D11DeviceContext->Map(m_SquareMesh->GetVertexBuffer().Get(),
         0, mapType, 0, &mappedBuffer))) {
        // Map失敗
        throw BaseException(
            L"頂点のMapに失敗しました。",
            L"if(FAILED(pID3D11DeviceContext->Map()))",
            L"SquareObject::OnUpdate()"
        );
    }
    //頂点の変更
    VertexPositionTexture* vertices
        = (VertexPositionTexture*)mappedBuffer.pData;
    for (size_t i = 0; i < m_SquareMesh->GetNumVertices(); i++) {
        Vec2 UV = m_BackupVertices[i].textureCoordinate;
        if (UV.x == 0.0f) {
            UV.x = m_TotalTime;
        }
        else if (UV.x == 4.0f) {
            UV.x += m_TotalTime;
        }
        vertices[i] = VertexPositionTexture(
            m_BackupVertices[i].position,
            UV
        );

    }
    //アンマップ
    pD3D11DeviceContext->Unmap(m_SquareMesh->GetVertexBuffer().Get(), 0);
}
 赤くなっているところが重要なわけですが、頂点バッファにverticesという変数(ポインタ)をマップして、その内容を書き換えます。書き換えが終わったらアンマップします。

■描画処理■

 描画処理はPTSpriteDraw::DrawObject()関数で行います。引数にメッシュとコンスタントバッファ構造体の参照を渡します。描画処理では、そのパラメータに合わせて描画処理を行います。壁オブジェクトの描画と流れるテクスチャオブジェクトの描画と違いはありません。

【まとめ】

 今回は同じシェーダで描画できるオブジェクトを一つの描画クラスで描画する方法をサンプル化しました。
 これはフルバージョンにおける描画コンポーネントと同じような動きになります。
 Dx11にせよDx12にせよ描画処理は、GPUに対していろんな設定(パイプライン設定)を行わなければいけないので、各オブジェクトごとに描画をしていたのでは同じようなコードをあちこちに書かなければならないので大変です。バグのもとにもなります。
 ですので同じシェーダは同じ描画という設計にしたほうが、シンプルになると思います。