DirectX12 でジオメトリシェーダーを使った話をあまり見てなかったので、ちょっと試してみようと思います。ジオメトリシェーダーといえばプリミティブを生成できる特徴があるので、ポイントスプライトなどをやってみました。
描画手順
頂点データとしては位置情報を与えて、それを頂点シェーダーで座標変換します。その後、ジオメトリシェーダーで四角形を生成して描画します。このためには、パイプラインステートオブジェクトを生成するのに次のような感じで設定します。(重要な箇所のみ表示しています)
D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc; psoDesc.VS = // 頂点シェーダーのセット psoDesc.GS = // ジオメトリシェーダーのセット psoDesc.PS = // ピクセルシェーダーのセット psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT;
頂点データは次のような感じで、点を与えています。
XMFLOAT3 bufferVB[] = { XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(2.0f, 0.0f, 0.0f), XMFLOAT3(2.0f, 2.0f, 0.0f) };
これからバッファを作成して、描画のために D3D12_VERTEX_BUFFER_VIEW の構造体を設定します。そして、プリミティブトポロジの設定としてはポイントリストを設定します。 PSO での設定と、IASetPrimitiveTopology と2箇所で “ポイント” を設定するのを忘れないようにします。
D3D12_VERTEX_BUFFER_VIEW vbView; vbView.BufferLocation = m_baseVB->GetGPUVirtualAddress(); vbView.SizeInBytes = sizeof(DirectX::XMFLOAT3) * m_vertexCount; vbView.StrideInBytes = sizeof(DirectX::XMFLOAT3); m_commandList->IASetVertexBuffers(0, 1, &vbView); m_commandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST);
ジオメトリシェーダーのコード
1つの点から3角形2つで構成されるトライアングルストリップを作るシェーダーコードは次のようになります。
[maxvertexcount(6)] void main( point GSInput In[1], uint PrimitiveID : SV_PrimitiveID, inout TriangleStream stream ) { GSOutput v0, v1, v2, v3; float4 color = colors[PrimitiveID]; // カラーのテーブルを用意しておく. v0.Position = In[0].Position + float4(-0.5, 0.5, 0, 0); v0.Color = color; v1.Position = In[0].Position + float4( 0.5, 0.5, 0, 0); v1.Color = color; v2.Position = In[0].Position + float4(-0.5,-0.5, 0, 0); v2.Color = color; v3.Position = In[0].Position + float4( 0.5,-0.5, 0, 0); v3.Color = color; stream.Append(v0); stream.Append(v1); stream.Append(v2); stream.Append(v3); }
ジオメトリシェーダーの入力は点データなので、 “point” ですし、要素数は1で、出力するデータはトライアングル形式なので TriangleStream を使用します。そしてストリームに追加していくと、この場合にはトライアングルストリップとして判断されるようです。
こうやって描画したものが次のアニメGifとなっています。
その他
次のようにすると、トライアングルリストとして出力されてくるようです。
[maxvertexcount(6)] void main( point GSInput In[1], uint PrimitiveID : SV_PrimitiveID, inout TriangleStream stream ) { GSOutput v0, v1, v2, v3; float4 color = colors[PrimitiveID]; v0.Position = In[0].Position + float4(-0.5, 0.5, 0, 0); v0.Color = color; v1.Position = In[0].Position + float4( 0.5, 0.5, 0, 0); v1.Color = color; v2.Position = In[0].Position + float4(-0.5,-0.5, 0, 0); v2.Color = color; v3.Position = In[0].Position + float4( 0.5,-0.5, 0, 0); v3.Color = color; stream.Append(v0); stream.Append(v1); stream.Append(v2); stream.RestartStrip(); stream.Append(v1); stream.Append(v2); stream.Append(v3); stream.RestartStrip(); }
追記
ジオメトリシェーダーを扱う際、頂点シェーダーから頂点を出力するときに注意が必要です。頂点変換が完了した後で SV_POSITION セマンティクスを付けます。
struct GSInput { float4 Position : POSITION; }; struct GSOutput { float4 Position : SV_POSITION; float4 Color : COLOR; };