Depth-Stencil機能の構成

ここでは、出力結合 (OM) ステージの深度/ステンシル バッファーと深度/ステンシル ステートを設定する手順について説明します。

深度/ステンシル バッファーおよび対応する深度/ステンシル ステートの使用方法を理解したら、高度なステンシル技法をご覧ください。

Depth-Stencil リソースを作成する

テクスチャ リソースを使用して深度ステンシル バッファーを作成します。

ID3D11Texture2D* pDepthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;
descDepth.Width = backBufferSurfaceDesc.Width;
descDepth.Height = backBufferSurfaceDesc.Height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = pDeviceSettings->d3d11.AutoDepthStencilFormat;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );

深度/ステンシル ステートの作成

深度/ステンシル ステートは、出力結合ステージで、深度/ステンシル テストを実行する方法を指定します。 深度/ステンシル テストは、特定のピクセルを描画するかどうかを決定します。

D3D11_DEPTH_STENCIL_DESC dsDesc;

// Depth test parameters
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D11_COMPARISON_LESS;

// Stencil test parameters
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// Stencil operations if pixel is front-facing
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Stencil operations if pixel is back-facing
dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Create depth stencil state
ID3D11DepthStencilState * pDSState;
pd3dDevice->CreateDepthStencilState(&dsDesc, &pDSState);

DepthEnable と StencilEnable では、深度とステンシルのテストを有効 (および無効) できます。 深度テストを無効にし、深度バッファーへの書き込みを防ぐには、DepthEnable を FALSE に設定します。 ステンシル テストを無効にし、ステンシル バッファーへの書き込みを禁止するには、StencilEnable を FALSE に設定します (DepthEnable が FALSE で StencilEnable が TRUE の場合、深度テストは常にステンシル操作に合格します)。

DepthEnable は出力マージ ステージにのみ影響します。データがピクセル シェーダーに入力される前の値のクリッピング、深度バイアス、またはクランプには影響しません。

深度/ステンシル データの OM ステージへのバインド

深度/ステンシル ステートをバインドします。

// Bind depth stencil state
pDevice->OMSetDepthStencilState(pDSState, 1);

ビューを使用して、深度/ステンシル リソースをバインドします。

D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;

// Create the depth stencil view
ID3D11DepthStencilView* pDSV;
hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, // Depth stencil texture
                                         &descDSV, // Depth stencil desc
                                         &pDSV );  // [out] Depth stencil view

// Bind the depth stencil view
pd3dDeviceContext->OMSetRenderTargets( 1,          // One rendertarget view
                                &pRTV,      // Render target view, created earlier
                                pDSV );     // Depth stencil view for the render target

レンダー ターゲット ビューの配列は ID3D11DeviceContext::OMSetRenderTargets に渡すことができますが、これらのレンダー ターゲット ビューはすべて 1 つの深度ステンシル ビューに対応します。 Direct3D 11 のレンダー ターゲット配列は、アプリケーションがプリミティブ レベルで複数のレンダー ターゲットに同時にレンダリングできるようにする機能です。 レンダー ターゲット配列は、 ID3D11DeviceContext::OMSetRenderTargets (基本的には Direct3D 9 で使用されるメソッド) を複数呼び出してレンダー ターゲットを個別に設定する場合よりもパフォーマンスが向上します。

レンダー ターゲットは、すべて同じタイプのリソースである必要があります。 マルチサンプル アンチエイリアシングを使用する場合、バインドされたすべてのレンダー ターゲットおよび深度バッファーのサンプル カウントが同じでなければなりません。

バッファーをレンダー ターゲットとして使用する場合、深度/ステンシル テストと複数のレンダー ターゲットはサポートされません。

  • 最大 8 個のレンダー ターゲットを同時にバインド可能です。
  • すべてのレンダー ターゲットは、すべての次元 (幅、高さ、3D の場合は奥行、*Array 型の場合は配列サイズ) でサイズが同じでなければなりません。
  • 各レンダー ターゲットでデータ フォーマットが異なっていてもかまいません。
  • 書き込みマスクは、レンダー ターゲットに書き込まれるデータを制御します。 出力書き込みマスクは、レンダー ターゲット単位および成分レベル単位で、レンダー ターゲットに書き込まれるデータを制御します。

高度なステンシル技法

深度/ステンシル バッファーのステンシル部分は、合成、デカール、および輪郭処理などのレンダリング エフェクトを作成するために使用できます。

合成

ステンシル バッファーを使えば、アプリケーションは 2D または 3D イメージを 3D シーンに合成できます。 ステンシル バッファーのマスクを使って、レンダー ターゲット サーフェスの領域をオクルードします。 次に、テキストやビットマップなどの格納 2D 情報をオクルードされた領域に書き込むことができます。 別の方法として、アプリケーションでは追加 3D プリミティブをレンダー ターゲット サーフェスのステンシル マスクされた領域にレンダリングできます。 この場合、シーン全体をレンダリングすることもできます。

ゲームでは、複数の 3D シーンを合成することがよくあります。 たとえば、ドライビング ゲームでは通常バックミラーを表示します。 バックミラーには、運転者の背後の 3D シーンの表示が含まれます。 したがって、バックミラーは、本質的には運転者の前方の風景と合成された 第 2 の 3D シーンと言えます。

デカール

Direct3D アプリケーションでは、デカールを使用して、レンダー ターゲット サーフェスに描画される特定のプリミティブ イメージのピクセルを制御します。 アプリケーションはプリミティブのイメージにデカールを適用して、同一平面上のポリゴンを適切にレンダリングできるようにします。

たとえば、道路にタイヤの跡と黄色い線を付ける場合、その跡は道路の上に直接表示する必要があります。 ただし、タイヤ跡と道路の z 値は同一です。 したがって、深度バッファーでは、これら 2 つを明確に分離できない場合があります。 背面のプリミティブの一部のピクセルが前面のプリミティブの手前にレンダリングされたり、その逆になったりする場合があります。 出力されるイメージは、フレームが変わるたびにちらついているように見えます。 この現象は、z ファイティングまたはフリマリングと呼ばれます。

この問題を解決するには、ステンシルを使用して、デカールが表示される背面プリミティブのセクションをマスクします。 z バッファリングをオフにし、レンダー ターゲット サーフェスのマスク オフされた領域に前面プリミティブのイメージをレンダリングします。

複数のテクスチャ ブレンディングを使用することで、この問題を解決できます。

輪郭とシルエット

輪郭処理やシルエット処理など、より抽象的なエフェクトにステンシル バッファーを使用することができます。

アプリケーションに 2 つのレンダリング パスがある場合、一方はステンシル マスクを生成するためのもので、もう一方はイメージにステンシル マスクを適用するためのものとすると、2 番目のパスのプリミティブが多少小さいときは、出力されるイメージがプリミティブの輪郭のみになります。 その場合、ステンシルでマスクされたイメージの領域を単色で塗りつぶすことで、プリミティブが浮き上がって見えるようにすることができます。

ステンシル マスクが、レンダリングするプリミティブと同じサイズおよび形状の場合、出力されるイメージではプリミティブの位置が穴になります。 その場合、穴を黒で塗りつぶすことで、プリミティブのシルエットを作成できます。

2 面ステンシル

シャドウ ボリュームは、ステンシル バッファーで影を描画するために使用します。 オクルーディング ジオメトリによってキャストされたシャドウ ボリュームは、シルエットの縁を計算し、ライトと反対側の 3D ボリューム セットに押し出すことによって計算されます。 これらのボリュームはその後、ステンシル バッファーに 2 回レンダリングされます。

最初のレンダリングでは、前向きのポリゴンが描画され、ステンシル バッファーの値が増加します。 2 回目のレンダリングでは、シャドウ ボリュームの後ろ向きのポリゴンが描画され、ステンシル バッファーの値が減少します。 通常、値の増減は、互いに相殺になります。ただし、通常のジオメトリでシーンが既にレンダリングされているため、シャドウ ボリュームがレンダリングされるときに、ピクセルのいくつかが Z バッファー テストで不合格になります。 ステンシル バッファーに残された値は、シャドウのピクセルに対応します。 ステンシル バッファーの残りの内容は、すべてを覆う大きな黒のクワッドをシーンにアルファ ブレンディングするために、マスクとして使用されます。 マスクとして機能するステンシル バッファーを使用すると、シャドウ内のピクセルが暗くなります。

これは、シャドウ ジオメトリが光源ごとに 2 回描画されることを意味するため、GPU の頂点処理のスループットに影響します。 2 面ステンシル機能は、この状況を軽減することを目的として設計されています。 この方法には、(次の) 2 組のステンシル ステートがあります。一方は前向きの三角形のそれぞれに設定され、もう一方は後ろ向きの三角形に設定されます。 このようにして、光源ごとにシャドウ ボリューム単位で 1 つのパスのみが描画されます。

両面ステンシル実装の例は 、ShadowVolume10 サンプルに記載されています。

深度/ステンシル バッファーをテクスチャとして読み取る

アクティブでない深度/ステンシル バッファーを、シェーダーでテクスチャとして読み取ることができます。 深度/ステンシル バッファーをテクスチャとして読み取るアプリケーションでは 2 つのパスでレンダリングします。最初のパスでは深度/ステンシル バッファーに書き込み、2 番目のパスではそのバッファーから読み取ります。 これにより、シェーダーはそれまでにバッファーに書き込んだ深度またはステンシルの値を、現在レンダリング中のピクセルの値と比較できます。 この比較の結果を使用して、シャドー マッピングやソフト パーティクルなどのエフェクトをパーティクル システムで作成できます。

深度ステンシル リソースとシェーダー リソースの両方として使用できる深度ステンシル バッファーを作成するには、「Depth-Stencil リソースの作成」セクションのサンプル コードにいくつかの変更 加える必要があります。

  • 深度ステンシル リソースには、DXGI_FORMAT_R32_TYPELESSなどの型指定なしの形式が必要です。

    descDepth.Format = DXGI_FORMAT_R32_TYPELESS;
    
  • 深度ステンシル リソースでは、D3D10_BIND_DEPTH_STENCILとD3D10_BIND_SHADER_RESOURCEバインド フラグの両方を使用する必要があります。

    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL | D3D10_BIND_SHADER_RESOURCE;
    

さらに、 D3D11_SHADER_RESOURCE_VIEW_DESC 構造体と ID3D11Device::CreateShaderResourceView を使用して、深度バッファー用にシェーダー リソース ビューを作成する必要があります。 シェーダー リソース ビューでは、 DXGI_FORMAT_R32_FLOAT などの型 指定された形式が使用されます。これは、深度ステンシル リソースの作成時に指定された型指定なしの形式と同じです。

最初のレンダー パスでは、「 Depth-Stencil データを OM ステージにバインドする 」セクションで説明されているように、深度バッファーがバインドされます。 D3D11_DEPTH_STENCIL_VIEW_DESCに渡される形式に注意 してください。format には、 DXGI_FORMAT_D32_FLOATなどの型指定された形式が使用されます。 最初のレンダー パスの後、深度バッファーにはシーンの深度値が含まれます。

2 番目のレンダー パスでは 、ID3D11DeviceContext::OMSetRenderTargets 関数を使用して深度ステンシル ビューを NULL または別の深度ステンシル リソースに設定し、シェーダー リソース ビューは ID3D11EffectShaderResourceVariable::SetResource を使用してシェーダーに渡されます。 これにより、シェーダーは最初のレンダリング パスで計算された深度値を検索できます。 最初のレンダー パスの視点が 2 番目のレンダー パスと異なる場合は、深度値を取得するために変換を適用する必要があることに注意してください。 たとえば、シャドウ マッピング手法が使用されている場合、最初のレンダー パスは光源の観点から、2 番目のレンダー パスはビューアーの視点から取得されます。

出力結合 (OM) ステージ

パイプライン ステージ (Direct3D 10)