ライトプリパスレンダリング Part0

投稿日:

準備期間を終えて正式スタート。最初の課題は「ライトプリパスレンダリング」です。まずは、必要になりそうな処理、MRT(マルチレンダーターゲット)、G(ジオメトリ)バッファの生成、レンダリング結果をテクスチャとして使用、などを調査検証してみる。

左上:法線 右上:デプス⇒ワールド座標 左下:Diffuse 右下:輪郭線(赤)&影(青)
左上:法線 右上:デプス⇒ワールド座標
左下:Diffuse 右下:輪郭線(赤)&影(青)


ライトプリパスレンダリング(遅延ライティング)実装のための準備

ライトプリパスレンダリングについては遅延シェーディング – Wikipediaなど
3つのパスでレンダリングを行います。1パス目はライティングに必要な法線ベクトル、スペキュラパラメータの作成(Gバッファ)、2パス目はライティング、3パス目は、ライティング結果を参照したレンダリング。

MRT(マルチレンダーターゲット)

遅延シェーディング系で必須になる機能。1回のシェーダ処理で複数のレンダーターゲットに結果を出力します。

MRTの設定
// テクスチャ作成 MRTに必要な個数
D3D11_TEXTURE2D_DESC tex_desc;
//レンダーターゲットとして使用&シェーダリソースとして利用
tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
//デプステクスチャの場合
tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
//他のメンバは省略
Device->CreateTexture2D( &tex_desc, nullptr, &texture );

//Width、Heightは全レンダーターゲットとデプスバッファすべて同じにする

// RenderTargetView作成 MRTに必要な個数
Device->CreateRenderTargetView( texture, nullptr, &rtview );
// デプスバッファ
Device->CreateDepthStencilView( texture, nullptr, &dsview );

// RenderTarget設定
DevContext->OMSetRenderTargets( n, rtviews, dsview );
ピクセルシェーダでの出力
struct PS_OUTPUT
{
    float4 Out0 : SV_Target0;//MRT0への出力
    float4 Out1 : SV_Target1;//MRT1への出力
    float4 Out2 : SV_Target2;
};

//-------------------------
//ピクセルシェーダ
PS_OUTPUT psBasic( PS_INPUT input )
{
    PS_OUTPUT o = (PS_OUTPUT)0;
    o.Out0 = MRT0への出力値;
    o.Out1 = MRT1への出力値;
    o.Out2 = MRT2への出力値;
    return o;
}

画像フォーマットの選別

DirectX11では色(RGBA8bit)以外のフォーマットが用意され、遅延シェーディングやHDRを実装しやすいようになっています。Gバッファ用のフォーマット、HDR(ハイダイナミックレンジ)に対応した色データのフォーマットなど検証します。

法線ベクトル

◆DXGI_FORMAT_R11G11B10_FLOAT
精度、データ量を考えるとこれが最適 >0なのでバイアスが必要
◆DXGI_FORMAT_R8G8B8A8_UNORM
精度不足、A8に別のデータを格納すればメモリ節約になるかも
◆DXGI_FORMAT_R16G16B16A16_FLOAT
オーバースペック、メモリ大
◆DXGI_FORMAT_R16G16_FLOAT
制限有、絶対値が1なのでz=sqrt(1-x^2-y^2) ただしz>0のみ

色データ(HDR対応)

◆DXGI_FORMAT_R16G16B16A16_FLOAT
速度、メモリに問題がなければこれを採用
◆DXGI_FORMAT_R11G11B10_FLOAT
精度に問題なければ使用したい
◆DXGI_FORMAT_R9G9B9E5_SHAREDEXP
共通の指数部を持つfloat、でもRenderTargetに使えない、テクスチャデータ専用?

位置(3次元座標)

未使用、デプス値から計算可能

デプスバッファ

◆DXGI_FORMAT_D24_UNORM_S8_UINT
精度に問題なければこれを採用
◆DXGI_FORMAT_D32_FLOAT
デプス値の精度が必要であれば
◆DXGI_FORMAT_R24G8_TYPELESS
デプス値アクセスのための特殊フォーマット、これでテクスチャ作成
CreateDepthStencilView時にD24_UNORM_S8_UINTに変換
◆DXGI_FORMAT_R24_UNORM_X8_TYPELESS
◆DXGI_FORMAT_X24_TYPELESS_G8_UINT
シェーダでデプスステンシル値を取得するためのフォーマット
CreateShaderResourceViewで指定

RenderTargetとDepthStencilのアクセス

RenderTargetへのレンダリング結果をテクスチャとしてシェーダからアクセスします。その場合テクスチャ作成時にD3D11_BIND_SHADER_RESOURCEを指定する必要があります。DXGI_FORMAT_D24_UNORM_S8_UINTなどシェーダからアクセスできないフォーマットあり。

ShaderResourceViewの作成
// テクスチャ作成時と同じフォーマット
Device->CreateShaderResourceView( textrue, nullptr, &srview );

// テクスチャ作成時とは違うフォーマットでアクセス
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;//
//他のメンバは省略
Device->CreateShaderResourceView( textrue, &srv_desc, &srview );

// 上2つのShaderResourceViewは同じテクスチャバッファを
// 異なるフォーマットとしてアクセス
DepthStencilのアクセス

DXGI_FORMAT_D24_UNORM_S8_UINTのテクスチャからはShaderResourceViewを作成できないため、TYPELESS系のフォーマットを使用します。

// デプスステンシル用テクスチャ作成
D3D11_TEXTURE2D_DESC& depth_decs;
depth_decs.Format = DXGI_FORMAT_R24G8_TYPELESS;//TYPELESSフォーマット
depth_decs.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE;
//他のメンバは省略
Device->CreateTexture2D( &d, nullptr, &texture );

// デプスステンシルViewの作成
D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc;
dsv_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
//他のメンバは省略
Device->CreateDepthStencilView( texture, &dsv_desc, &dsview );

// デプス値取得用シェーダリソースView
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
//他のメンバは省略
Device->CreateShaderResourceView( texture, &srv_desc, &depth_srview );

// ステンシル値取得用シェーダリソースView
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
srv_desc.Format = DXGI_FORMAT_X24_TYPELESS_G8_UINT;
//他のメンバは省略
Device->CreateShaderResourceView( texture, &srv_desc, &stencil_srview );

//デプス値、ステンシル値を同時に取得できるフォーマットはないみたい
//個別にシェーダリソースView作成

シェーダからのアクセス デプス値から3次元位置に逆変換

基本的にポリゴン描画時のテクスチャと同じです。ステンシル値だけはUINT型でフィルタを利用できないためSample関数ではなくLoad関数を使用します。Load関数でのテクスチャ座標はテクセル単位(整数)なのでテクスチャの幅高さ情報が必要になります。

// デプス DXGI_FORMAT_R24_UNORM_X8_TYPELESS
Texture2D<float> txDepth : register( t0 );
SamplerState samDepth : register( s0 );

// ステンシル DXGI_FORMAT_X24_TYPELESS_G8_UINT
Texture2D<uint> txStencil : register( t1 );

// デプス値とスクリーン座標(-1~+1)からView座標系の位置を求める
float dep = txDepth.Sample( samDepth, texcoord);
float2 spos =  スクリーン座標(-1~+1);
matrix mtx_proj = プロジェクション行列;
float3 vpos;//View座標
//逆変換 単純な計算なので逆行列は使用しない
vpos.z = -mtx_proj._m32/(mtx_proj._m22 - dep);
vpos.x = spos.x*vpos.z/mtx_proj._m00;
vpos.y = spos.y*vpos.z/mtx_proj._m11;
// vposを使って光源計算など 光源の位置情報をView座標系に変換すると効率が良い

//ステンシル値の読み込み
int3 tex_wh;
txStencil.GetDimensions(0, tex_wh.x, tex_wh.h, tex_wh.z)//テクスチャの幅高さ(テクセル);
int2 texcrd = tex_whを使ってテクセル単位に変換;
uint stencil = txStencil.Load(int3(texcrd ,0));//0:ミップマップレベル

サンプルプログラム

“ライトプリパス準備と検証” をダウンロード dx11LightPrePass0.zip – 1187 回のダウンロード – 69 KB

説明

検証用プログラムのため中途半端な状態です。

zg_dx11.cpp CreateMRT();

MRT用リソース作成、画像フォーマット、シェーダリソースView作成

shader/screen_fill.fx

シェーダからレンダリング結果アクセス、MRTの結果を4分割表示
デプス値、ステンシル値の取得、デプス値から3次元座標の逆変換

DirectXTK

フォント表示にDirectXTKを使用しています。FPSなどデバッグ情報の表示を有効にするには、フォントデータascii.spritefontを作成する必要があります。作成にはDirectXTKに含まれるMakeSpriteFontを使います。

// コマンドプロンプトでフォントデータ作成
MakeSpriteFont "MS ゴシック" ascii.spritefont /FontSize:9

リンクなど

DirectXTK

DirectX Tool Kit
DirectX11開発支援ライブラリ。スプライト表示、モデル表示、フォント表示など。

スクリーンショットのモデルとモーション

モデルデータ

ままま式GUMIβ版

モーションデータ

【第9回MMD杯Ex】星間飛行GUMI/ニコニコ動画

「ライトプリパスレンダリング Part0」への1件のフィードバック

コメントを残す

メールアドレスが公開されることはありません。

認証:数字を入力してください(必須) * Time limit is exhausted. Please reload CAPTCHA.