いまさらDirect3D11入門

初めてグラフィックスAPIを触る人に向けて

レンダーターゲットとブレンドステート

今回と次のパートでグラフィックスパイプラインの出力結合ステージ(英訳:Output-Merger Stage)について見ていきます。

出力結合ステージはピクセルシェーダの後に実行され、今までのステージの結果から画面に描画するか決めたり、描画先のピクセルとどのようにブレンドするかを決定するものになります。

ドキュメント: グラフィック パイプライン(日本語) Graphics Pipeline(英語)

概要

今回のパートでは出力結合ステージのレンダーターゲットとブレンドステートについて見ていきます。 対応するプロジェクトはPart07_RenderTargetAndBlendStateになります。

  1. レンダーターゲット
  2. ブレンドステート
  3. まとめ
  4. 補足
    • ID3D11RenderTargetViewのクリア関数

レンダーターゲット

レンダーターゲットはグラフィックスパイプラインの出力先となり、同時に複数設定できます。 基本的に画面を表すものなので2次元テクスチャを設定します。

シェーダ

まず、複数のレンダーターゲットに出力するシェーダを見ていきましょう。

// PixelShader.hlsl
void main(
  float4 pos : SV_POSITION,
  float4 color : COLOR0,
  out float4 outColor : SV_Target0,//<- 0番目のレンダーターゲットに出力する
  out float4 outColor2 : SV_Target1)//<- 1番目のレンダーターゲットに出力する
{
  outColor = color;
  outColor2 = color;
}

複数のレンダーターゲットに出力際はピクセルシェーダでその出力を制御します。 SV_Target0SV_Target1のシステムセマンティクスを指定したものが各レンダーターゲットの出力先になります。 SV_Targetの横の数字で出力先を指定し、0~7の数字が使用できます。

設定

レンダーターゲットの設定は以下のように行います。

// Sccene::onRender関数の一部
//レンダーターゲットの設定
std::array<ID3D11RenderTargetView*, 2> ppRTVs = { {
    this->mpRenderTarget1RTV.Get(),
    this->mpRenderTarget2RTV.Get(),
  } };
this->mpImmediateContext->OMSetRenderTargets(static_cast<UINT>(ppRTVs.size()), ppRTVs.data(), nullptr);

上のID3D11DeviceContext::OMSetRenderTargets関数でレンダーターゲットを行っています。
ドキュメント:ID3D11DeviceContext::OMSetRenderTargets (日本語) (英語)

各引数の意味はシェーダリソースビュー等これまで出てきたものと似ており、設定するものがID3D11RenderTargetViewになっただけです。 最後の引数はID3D11DepthStencilViewに次にパートで詳しく見ていきます。

ID3D11RenderTargetViewの作成

ID3D11RenderTargetViewの作成方法ですが、これも他のビューと似ています。

// Scene::onInit関数の一部
//レンダーターゲット2の作成
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = this->width();
desc.Height = this->height();
desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
desc.BindFlags = D3D11_BIND_RENDER_TARGET;
desc.SampleDesc.Count = 1;
desc.MipLevels = 1;
desc.ArraySize = 1;
auto hr = this->mpDevice->CreateTexture2D(&desc, nullptr, this->mpRenderTarget2.GetAddressOf());
if (FAILED(hr)) {
  throw std::runtime_error("レンダーターゲット2の作成に失敗");
}
//ID3D11RenderTargetViewの作成
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Format = desc.Format;
rtvDesc.Texture2D.MipSlice = 0;
hr = this->mpDevice->CreateRenderTargetView(this->mpRenderTarget2.Get(), &rtvDesc, this->mpRenderTarget2RTV.GetAddressOf());
if (FAILED(hr)) {
  throw std::runtime_error("レンダーターゲット2のレンダーターゲットビュー作成に失敗");
}

ID3D11Device::CreateRenderTargetView関数で作成します。 引数に渡しているD3D11_RENDER_TARGET_VIEW_DESCの内容も他のビューの作成に使用した構造体と似ていますので説明を省略します。

ドキュメント:
ID3D11Device::CreateRenderTargetView (日本語) (英語)
D3D11_RENDER_TARGET_VIEW_DESC (日本語) (英語)

レンダーターゲットについては以上です。 説明はあっさりしていますが、現在ハイエンドゲームにおいてよく使われている遅延レンダリング(英訳: Deferred Rendering)やポストエフェクト(英訳: Post Effect)を使うときはこの機能を使っていますので、欠かすことのできないものとなっています。

ブレンドステート

ブレンドステートはピクセルシェーダで出力した値をレンダーターゲットに書き込む際、もともとあった値とどのようにブレンドするかを指定するためのものです。 ID3D11BlendStateをグラフィックスパイプラインに設定することでブレンドの方法を指定することができます。

ドキュメント: ブレンディング機能の構成(日本語) Configuring Blending Functionality(英語)

ブレンドを行う式自体はある程度固定化されています。

// ブレンディングの擬似コード
//ブレンディングはRGBとAで別の式を指定できる
//RGBの計算
float3 rgbDest = ...; //ピクセルシェーダからの出力
float3 rgbSrc = ...;  //出力先のもともとあった値
//下3つはID3D11BlendStateで指定できるもの
D3D11_BLEND rgbDestBlend = ...;
D3D11_BLEND rgbSrcBlend = ...;
D3D11_BLEND_OP rgbOp = ...;
float3 rgbResult = rgbDest * rgbDestBlend rgbOp rgbSrc * rgbSrcBlend;

//Alphaの計算
float alphaDest = ...; //ピクセルシェーダからの出力
float alphaSrc = ...;  //出力先のもともとあった値
//下3つはID3D11BlendStateで指定できるもの
D3D11_BLEND alphaDestBlend = ...;
D3D11_BLEND alphaSrcBlend = ...;
D3D11_BLEND_OP alphaOp = ...;
float alphaResult = alphaDest * rgbDestBlend alphaOp alphaSrc * alphaSrcBlend;

return float4(rgbResult, alphaResult);

ブレンディングの式を変えることで透けた表現や明るさの表現など様々な画面効果を出すことができます。 こちらのサイトでブレンドステートの設定を変えることができますので簡単に確認することができます。 OpenGLを使用していますが、DX11も同じパラメータを使用できますのでそこまで問題にならないでしょう。

作成

それではID3D11BlendStateの作成の仕方について見ていきます。

// Scene::onInit関数の一部
//ブレンドステートの作成
D3D11_BLEND_DESC desc = {};
desc.AlphaToCoverageEnable = false;
desc.IndependentBlendEnable = false;
desc.RenderTarget[0].BlendEnable = true;
desc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ZERO;
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
//ID3D11BlendStateの作成
auto hr = this->mpDevice->CreateBlendState(&desc, this->mpBlendState.GetAddressOf());
if (FAILED(hr)) {
  throw std::runtime_error("ブレンドステートの作成に失敗");
}

上のID3D11Device::CreateBlendState関数で作成します。 D3D11_BLEND_DESCでブレンド方法を指定します。

ドキュメント:
ID3D11Device::CreateBlendState関数 (日本語) (英語)
D3D11_BLEND_DESC (日本語) (英語)

各レンダーターゲットに異なるブレンディングを設定することが可能です。

D3D11_BLEND_DESCのメンバ

  1. AlphaToCoverageEnable

    アルファトゥカバレッジ機能を使用するときはtrueを設定してください。 アルファトゥカバレッジについては以下のリンク先を参考にしてください。
    ドキュメント: ブレンディング機能の構成(日本語) Configuring Blending Functionality(英語)

  2. IndependentBlendEnable

    レンダーターゲットごとに異なるブレンディングを行うか決めるフラグになります。 tureで有効化されます。 falseの場合はRenderTarget[0]の内容がすべてのレンダーターゲットで使用されます。

  3. RenderTarget[8]

    各レンダーターゲットのブレンディングを設定するためのものです。 型はD3D11_RENDER_TARGET_BLEND_DESCになります。
    ドキュメント:D3D11_RENDER_TARGET_BLEND_DESC (日本語) (英語)

    D3D11_RENDER_TARGET_BLEND_DESCのメンバ

    • BlendEnable

      ブレンディングを有効にするかのフラグ。

    • SrcBlend, DestBlend

      ピクセルシェーダからの出力と出力先のもともとあった値のそれぞれのRGBに掛ける値を指定するためのものになります。 Srcがもともとあった値に、Destがピクセルシェーダからの出力を表します。 どのように使われるかは上の擬似コードを参考にしてください。 指定できるものは以下のリンクを参考にしてください。
      ドキュメント D3D11_BLEND(日本語) D3D11_BLEND(英語)

    • BlendOp

      ピクセルシェーダからの出力と出力先のもともとあった値のRGBをどのように組み合わせるかを指定するものになります。 どのように使われるかは上の擬似コードを参考にしてください。 指定できるものは以下のリンクを参考にしてください。
      ドキュメント D3D11_BLEND_OP(日本語) D3D11_BLEND_OP(英語)

    • SrcBlendAlpha, DestBlendAlpha, BlendOpAlpha

      SrcBlend,DestBlend,BlendOpのアルファ成分版になります。

    • RenderTargetWriteMask

      書き込みマスクです。 RGBA内、書き込みたい部分のビットを立てることで書き込む値を制御できます。 値が0なら書き込みが無効になり、 D3D11_COLOR_WRITE_ENABLE_ALLを指定すればすべての成分が書き込まれます。

設定

ブレンドステートの設定は以下のようになります。

// Scene::onRender関数の一部改変したもの
std::array<float, 4> factor = { {
  1, 1, 1, 1
} };
this->mpImmediateContext->OMSetBlendState(this->mpBlendState.Get(), factor.data(), 0xffffffff);

ドキュメント:ID3D11DeviceContext::OMSetBlendState (日本語) (英語)

ID3D11DeviceContext::OMSetBlendStateの引数一部

  1. 第2引数:BlendFactor

    SrcBlend, DestBlendなどにD3D11_BLEND_BLEND_FACTORを使用した時のブレンディング係数になります。

  2. 第3引数:SampleMask

    マルチサンプリングを設定されているレンダーターゲットを使用している時の書くサンプリングのマスク値になります。

ブレンドステートについては以上になります。 ブレンドステートを使う場合はシェーダ側で特別な対応する必要はありません。 なので、同じシェーダに設定が異なるブレンドステートを設定することが可能です。

まとめ

今回、出力結合ステージのレンダーターゲットとブレンドステートについて見てきました。 レンダーターゲットは応用範囲が広い機能になり、ブレンドステートはパーティクルや半透明表現に使われるものになります。

グラフィックパイプラインにはブレンドステートのようにGPU側で処理を行うものがあります。 グラフィックスパイプラインはコンピュートシェーダのように全てこちらで制御することはできませんが、 ブレンドステートなどの機能がありますので活用していきましょう。

補足

ID3D11RenderTargetViewのクリア関数

レンダーターゲットを表すID3D11RenderTargetViewには専用のクリア関数が用意されています。
ドキュメント:ID3D11DeviceContext::ClearRenderTargetView 日本語 英語
クリアに使用する値はリソースのフォーマットにかかわらずfloatになりますので注意してください。

<前 トップ 次>