小ネタ : スクリーンスペースの矩形描画

小ネタです。 以下,DirectX系を考えています。

1つの三角形で矩形のUVを描画する

普通四角形のテクスチャを画面全体に表示しようとすると,こんな感じでスクリーンスペースで2つの三角形を描くかと思います。

f:id:hikita12312:20180125224401p:plain:w300

これと同じことを,大きな三角形一つで実現できます。

f:id:hikita12312:20180125224617p:plain:w400

はみ出たところはクリッピングされるので問題ありません。 画面全体にレンダリングしない場合でも,適当にスケーリングして.ID3D11DeviceContext::RSSetScissorRects()などシザー矩形でクリップすれば大丈夫です。

頂点バッファを利用しないで三角形を描画する

結論から言うと,DirectX11ならば,頂点シェーダーの入力にSV_VertexIDを使うと良いです。 SV_VertexIDは何個目の頂点を処理しているかが入ってくるシステム値です。

実際に先程の大きな三角形で画面全体にUVを張る頂点シェーダーは以下のようになります。

struct OUTPUT 
{
    float4 Position : SV_POSITION;
    float2 UV : TEXCOORD0;
};

OUTPUT VS_TEX2D(uint index : SV_VertexID)
{
    //     0----2
    //     |    /
    //     |  /
    //     1
    const float2 vId = float2(index / 2u, index % 2u); // 三角形を定義する 0 ~ 1 空間

    OUTPUT  Out = (OUTPUT  )0;
    // Positionの計算
    // {(-1,1), (-1,-3), (3, 1)}
    screenPosition = float2(4.0f, -4.0f) * vId.xy+ float2(-1.0f, 1.0f);
    Out.Position = float4(screenPosition.xy, 0.1f, 1.0f);

    // UV座標の計算
    // {(0,0), (0,2), (2,0)}
    Out.aUV[index] = float2(2.0f, 2.0f) * vId.xy;

    return Out;
}

この頂点シェーダーを利用すると,頂点バッファもインデックスバッファも利用せずに,シェーダーだけセットして,

m_pDeviceContext->Draw(3, 0);

みたいなドローコールを直接呼び出すだけで画面全体にUVを張れます。