DirectX12 で、ジオメトリシェーダーを用いてシングルパスでキューブマップ6面を描画を行う手順を確認しました。以前に各キューブマップ面毎に描画する方法は紹介しました。ジオメトリシェーダーを使うと1回の描画で6面へ描画ができます。
レンダーターゲットビュー・デプスステンシルビューの準備
描画先とするために、レンダーターゲットビューとデプスステンシルビューを用意します。このとき、2Dテクスチャ配列としてビューを生成します。次に RTV の生成コードを示しますが、 DSV についても同様です。
D3D12_RENDER_TARGET_VIEW_DESC rtvDesc{};
rtvDesc.Format = desc.Format;
rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.ArraySize = 6;
rtvDesc.Texture2DArray.FirstArraySlice = 0;
m_cubemapRTV = m_heapRTV->Alloc();
m_device->CreateRenderTargetView(
m_renderCubemap.Get(),
&rtvDesc,
m_cubemapRTV
);
シェーダーコードについて
キューブマップでは6面を必要とするので、6つのビュー行列を生成してシェーダーに渡せるようにする必要があります。これは定数バッファで送られてくるものと想定して、ジオメトリシェーダーを次のようにつくります。
struct VSOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float3 Normal : NORMAL;
};
struct GSOutput
{
float4 Position : SV_POSITION;
float4 Color : COLOR;
float3 Normal : NORMAL;
uint RTIndex : SV_RenderTargetArrayIndex;
};
[maxvertexcount(18)]
void mainGS(triangle VSOutput In[3], uint PrimitiveID : SV_PrimitiveID, inout TriangleStream<GSOutput> stream)
{
for (int f = 0; f < 6; ++f)
{
GSOutput v;
v.RTIndex = f;
float4x4 mtxVP = sceneConstants.viewProj[f];
for (int i = 0; i < 3; ++i)
{
v.Position = mul(In[i].Position, mtxVP);
v.Normal = In[i].Normal;
v.Color = In[i].Color;
stream.Append(v);
}
stream.RestartStrip();
}
}
SV_RenderTargetArrayIndex というセマンティクスを付けた変数によって、出力先の面を指定します。キューブマップの面に合うビュー行列を用いて頂点変換を行います。最後に変換した頂点を出力して、ピクセルシェーダーに処理を回します。1つのプリミティブが6つの面に合わせて複製されるイメージとなります。
キューブマップ描画
キューブマップ描画のために RTV, DSV をセットします。ディスクリプタに情報が記録されているようなので、それぞれ1つのディスクリプタのセットで済みます。
m_commandList->ClearRenderTargetView(rtv, clearColor[0], 0, nullptr);
m_commandList->ClearDepthStencilView(dsv, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, nullptr);
m_commandList->OMSetRenderTargets(1, &rtv, FALSE, &dsv);
キューブマップ用のビュー行列らを定数バッファで渡すなど準備が必要ですが、他の処理は通常のシーン描画と変わりません。
まとめ
今回は1パスでキューブマップ6面を描画するための、ジオメトリシェーダーの使い方について説明しました。ジオメトリシェーダーの登場時にはキューブマップ6面を1回で!と割と定番テクニックのように紹介されていましたが、最近すっかり聞かなくなった気がします。
補足
今回キューブマップということで6面のビューポートが同じだったのでビューポートは1つで処理しました。しかし DirectX12 においてビューポートおよびシザー矩形は複数設定することも出来るようになっています。これを使うと、1つの描画先の中で複数箇所に描画といったことが出来るようになります。
これを使うためには、SV_ViewportArrayIndex というセマンティクスを用います。VRに向けての画像生成のために、左右描画するといったことにも使えると思います。