深度/ステンシル機能の構成 (Direct3D 10)

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

  • 深度/ステンシル リソースの作成
  • 深度/ステンシル ステートの作成
  • 深度/ステンシル データの OM ステージへのバインド

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

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

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

 ID3D10Texture2D* pDepthStencil = NULL; D3D10_TEXTURE2D_DESC descDepth; descDepth.Width = backBufferSurfaceDesc.Width; descDepth.Height = backBufferSurfaceDesc.Height; descDepth.MipLevels = 1; descDepth.ArraySize = 1; descDepth.Format = pDeviceSettings->d3d10.AutoDepthStencilFormat; descDepth.SampleDesc.Count = 1; descDepth.SampleDesc.Quality = 0; descDepth.Usage = D3D10_USAGE_DEFAULT; descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL; descDepth.CPUAccessFlags = 0; descDepth.MiscFlags = 0; hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil ); 

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

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

 D3D10_DEPTH_STENCIL_DESC dsDesc;  // Depth test parameters dsDesc.DepthEnable = true; dsDesc.DepthWriteMask = D3D10_DEPTH_WRITE_MASK_ALL; dsDesc.DepthFunc = D3D10_COMPARISON_LESS;  // Stencil test parameters dsDesc.StencilEnable = true; dsDesc.StencilReadMask = 0xFF; dsDesc.StencilWriteMask = 0xFF;  // Stencil operations if pixel is front-facing dsDesc.FrontFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilDepthFailOp = D3D10_STENCIL_OP_INCR; dsDesc.FrontFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.FrontFace.StencilFunc = D3D10_COMPARISON_ALWAYS;  // Stencil operations if pixel is back-facing dsDesc.BackFace.StencilFailOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilDepthFailOp = D3D10_STENCIL_OP_DECR; dsDesc.BackFace.StencilPassOp = D3D10_STENCIL_OP_KEEP; dsDesc.BackFace.StencilFunc = D3D10_COMPARISON_ALWAYS;  // Create depth stencil state ID3D10DepthStencilState * pDSState; pDevice->CreateDepthStencilState(&dsDesc, &pDSState); 

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

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

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

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

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

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

 D3D10_DEPTH_STENCIL_VIEW_DESC descDSV; descDSV.Format = DXGI_FORMAT_D32_FLOAT; descDSV.ResourceType = D3D10_RESOURCE_TEXTURE2D; descDSV.Texture2D.FirstArraySlice = 0; descDSV.Texture2D.ArraySize = 1; descDSV.Texture2D.MipSlice = 0;  // Create the depth stencil view ID3D10DepthStencilView* pDSV; hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, // Depth stencil texture                                          &descDSV, // Depth stencil desc                                          &pDSV );  // [out] Depth stencil view  // Bind the depth stencil view pd3dDevice->OMSetRenderTargets( 1,          // One rendertarget view                                 &pRTV,      // Render target view, created earlier                                 pDSV );     // Depth stencil view for the render target 

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

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

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

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

高度なステンシル技法

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

  • 合成
  • デカール
  • 輪郭とシルエット
  • 2 面ステンシル
  • 深度/ステンシル バッファーをテクスチャーとして読み取る

合成

ステンシル バッファーを使えば、アプリケーションは 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 つのパスのみが描画されます。

2 面ステンシルの実装例については「ShadowVolume10 サンプル」を参照してください。

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

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

深度ステンシル リソースとしてもシェーダー リソースとしても使用できる深度ステンシル バッファーを作成するには、「深度/ステンシル リソースの作成」のサンプル コードを若干変更する必要があります。

  • 深度ステンシル リソースは、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;
    

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

1 番目のレンダリング パスでは、「深度/ステンシル データの OM ステージへのバインド」の説明にあるように深度バッファーがバインドされます。D3D10_DEPTH_STENCIL_VIEW_DESC.Format に渡すフォーマットでは、DXGI_FORMAT_D32_FLOAT などの型定義されたフォーマットを使用します。1 番目のレンダリング パスが終了すると、深度バッファーにはシーンの深度値が格納されています。

2 番目のレンダリング パスでは、ID3D10Device::OMSetRenderTargets 関数を使用して深度ステンシル ビューを NULL または別の深度ステンシル リソースに設定し、ID3D10EffectShaderResourceVariable::SetResource を使用してシェーダー リソース ビューをシェーダーに渡します。これにより、1 番目のレンダリング パスで計算した深度値をシェーダーで検索できるようになります。1 番目のレンダリング パスと 2 番目のレンダリング パスで視点が異なる場合、深度値を取得するには変換が必要になります。たとえば、シャドー マッピング テクニックを使用している場合、1 番目のレンダリング パスは光源から見たものであり、2 番目のレンダリング パスはビューアーから見たものです。

深度ステンシル バッファーをテクスチャーとしてレンダリングする例については、「softparticles サンプル」を参照してください。

関連項目

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