• Direct3D 11で深度バッファシャドウ

    2011年07月18日 22時24分
    MMDのシェーダを見ていると、深度バッファシャドウが入っているようなので入れてみます。

    一口に深度バッファシャドウと言っても細かい実装方法が色々ありますので、
    厳密にはMMDの処理とは違うと思います。
    深度バッファシャドウの一例としてご覧下さい。


    今回のソース


    深度バッファシャドウを実現するために必要になるのが
    Z値を保持するテクスチャです。これを生成します。

    ポイントはD3D11_TEXTURE2D_DESC.Usageです。
    Render targetとするためにD3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET
    Shader resourceとするためにD3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCEを設定します。
    1D3D11_TEXTURE2D_DESC renderTextureDesc;
    2renderTextureDesc.Width = width;
    3renderTextureDesc.Height = height;
    4renderTextureDesc.MipLevels = 1;
    5renderTextureDesc.ArraySize = 1;
    6renderTextureDesc.Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT;
    7renderTextureDesc.SampleDesc.Count = 1;
    8renderTextureDesc.SampleDesc.Quality = 0;
    9renderTextureDesc.Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT;
    10renderTextureDesc.BindFlags = D3D11_BIND_FLAG.D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_FLAG.D3D11_BIND_RENDER_TARGET;
    11renderTextureDesc.CPUAccessFlags = 0;
    12if (FAILED(device.CreateTexture2D(&renderTextureDesc, null, &renderTarget_)))
    13{
    14    cleanup();
    15    throw new Error("Failed create depth stencil shadow texture");
    16}


    生成したテクスチャは、Z値を書き込む必要があるため、Render target viewを生成します。
    1if (FAILED(device.CreateRenderTargetView(renderTarget_, null, &renderTargetView_)))
    2{
    3    cleanup();
    4    throw new Error("Failed create depth stencil shadow texture view");
    5}



    また、Z値を参照する必要もあるため、Shader resource viewも生成します。
    1if (FAILED(device.CreateShaderResourceView(renderTarget_, null, &renderTargetResourceView_)))
    2{
    3    cleanup();
    4    throw new Error("Failed create depth stencil shadow texture resource view");
    5}





    このテクスチャにZ値を書き込むには、
    生成したRender target viewを使用してRender targetに設定します。
    1deviceContext.OMSetRenderTargets(1, &renderTargetView_, depthStencilView_);



    この状態でレンダリングします。

    ビュー変換は、ライトをカメラとして変換を行います。
    また、射影変換は正射影変換を使っています。

    そして、ピクセルシェーダの出力が色データではなくZ値になります。
    1return input.pos.z;



    この出力したZ値を色として見るとこうなります。
    深度データテクスチャ




    生成されたZ値テクスチャを、通常のレンダリングするときにピクセルシェーダにセットします。
    1immediateContext_.PSSetShaderResources(1, 1, &depthTextureView);



    頂点シェーダでは、Z値テクスチャを生成した時と同じワールドビュー射影変換も追加で行います。
    このXY座標は-1~1の値になっているので、0~1のテクスチャ座標に変換します。
    1float2 depthTexCoord;
    2depthTexCoord.x = (input.lpos.x + 1) * 0.5;
    3depthTexCoord.y = (1 - input.lpos.y) * 0.5;


    この座標を使ってテクスチャからデータを取ると、深度データが取れます。
    1float minDepth = depthTex.Sample(sam, depthTexCoord).r + 0.0005f;


    取得したテクスチャのZ値と、そのピクセルのZ値(Z値テクスチャを生成した時と同じワールドビュー射影変換した座標)を比較し、
    テクスチャのZ値より大きければ、ライトの処理をキャンセルします。(ライトが遮られた状態と判断出来る)


    結果、このように影が表示されます。
    深度バッファシャドウ適用



    パイプラインの入力と出力には同じデータがセット出来ません。
    次にRender targetにテクスチャをセットするために、ピクセルシェーダにセットしたテクスチャは外しておきます。
    1ID3D11ShaderResourceView nullView = null;
    2immediateContext_.PSSetShaderResources(1, 1, &nullView);


    Render tagetも同様に外す必要がありますが、
    今回の場合、バックバッファをRender targetにした時点で外れるので、明示的には外していません。


    Z値テクスチャを生成するときと、通常のレンダリングをするとき、
    どちらもパイプラインの設定はほとんど同じになるので、エフェクトファイルを使うべきかなぁとも思います。
    手動だとすごく面倒です。

    コメントを書く

    名前
    本文
    編集用パスワード
    管理者のみ閲覧