2022年7月8日金曜日

Shadowmap

3Dの記事を見てると、その描画に必要だった材料を目視で確認できるように画面の端にワイプで表示させてたりする。
影を描画するのに深度バッファを参照して云々みたいな時に、その時の深度バッファを同時に表示させていてどうやってやるんだろうと思っていた。

で、やり方はレンダーターゲットがテクスチャとして使えるみたいに、深度バッファも同じようにテクスチャとして使える。単純にそれだけだった。

影用の深度バッファ書き込みだけならレンダリングターゲットの設定も、PSのシェーダも必要ないみたい。


テクスチャとして深度バッファを使う場合、フォーマットをDXGI_FORMAT_D32_FLOATではなくDXGI_FORMAT_R32_TYPELESSで作成する。
	D3D12_RESOURCE_DESC oDesc = {} ;
	oDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D ;
	oDesc.Width = tNum( oSize.W()) ;
	oDesc.Height = tNum( oSize.H()) ;
	oDesc.Alignment = 0 ;
	oDesc.DepthOrArraySize = 1 ;
	oDesc.MipLevels = 0 ;
//	oDesc.Format = DXGI_FORMAT_D32_FLOAT ;
	oDesc.Format = DXGI_FORMAT_R32_TYPELESS ;
	oDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN ;
	oDesc.SampleDesc.Count = 1 ;
	oDesc.SampleDesc.Quality = 0 ;
	oDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL ;

	D3D12_CLEAR_VALUE oCV ;
	oCV.Format = DXGI_FORMAT_D32_FLOAT ;	// こっちはD32のまま
	oCV.DepthStencil.Depth = 1.0f ;
	oCV.DepthStencil.Stencil = 0 ;
パイプラインステート設定
	D3D12_GRAPHICS_PIPELINE_STATE_DESC	oGPSD = {} ;

	// PSは設定無し
	D3D12_SHADER_BYTECODE & oSB = oGPSD.PS ;
	oSB.pShaderBytecode = nullptr ;
	oSB.BytecodeLength = 0 ;

	// 深度バッファは有効
	auto & oDSS = oGPSD.DepthStencilState ;
	oDSS.DepthEnable = TRUE ;
	oDSS.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL ;
	oDSS.DepthFunc = D3D12_COMPARISON_FUNC_LESS ;
	oDSS.StencilEnable = FALSE ;
	oDSS.StencilReadMask = D3D12_DEFAULT_STENCIL_READ_MASK ;
	oDSS.StencilWriteMask = D3D12_DEFAULT_STENCIL_WRITE_MASK ;
	const D3D12_DEPTH_STENCILOP_DESC defaultStencilOp = { D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_STENCIL_OP_KEEP, D3D12_COMPARISON_FUNC_ALWAYS } ;
	oDSS.FrontFace = defaultStencilOp ;
	oDSS.BackFace = defaultStencilOp ;
	oGPSD.DSVFormat = DXGI_FORMAT_D32_FLOAT ;

	// レンダーターゲットも0
	oGPSD.NumRenderTargets = 0 ;
	oGPSD.RTVFormats[0] = DXGI_FORMAT_UNKNOWN ;
描画準備
	// レンダーターゲットはないけど、ビューポートとシザー矩形の指定は必須
	D3D12_VIEWPORT oVP = pDB->GetViewPort() ;
	pGCL->RSSetViewports( 1, &oVP ) ;
	tRECT oSR = pDB->GetRect() ;
	pGCL->RSSetScissorRects( 1, &oSR ) ;

	// レンダーターゲットは0で、深度バッファのみ指定
	auto oDBDH = p3D->GetCPUDH( pDB->Type(), pDB->SlotDB()) ;
	pGCL->OMSetRenderTargets( 0, nullptr, false, &oDBDH ) ;
影用の深度バッファを描画したら、次は普通の描画を行う際に上記の深度バッファをテクスチャとして設定する。
その際、バリアで深度バッファのリソースステートを「DEPTH_WRITE」から「PIXEL_SHADER_RESOURCE」へ変更する。
参照が終わったら、逆に戻す。 
	D3D12_RESOURCE_BARRIER oRB = {} ;
	oRB.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION ;
	oRB.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE ;
	oRB.Transition.StateBefore = D3D12_RESOURCE_STATE_DEPTH_WRITE ;
	oRB.Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE ;
	oRB.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES ;
	oRB.Transition.pResource = pR->GetResource() ;
 某書籍では、そのテクスチャのシェダー定義を「Texture2D<float>」にすると書かれていたけど、「Texture2D」のままで大丈夫だった。 
渡したリソースにより勝手に変わってくれるので「Texture2D」のままのほうが便利。 ただ、「Texture2D<float>>」の方はそのテクスチャがfloatと解っているので最適化されて処理が速いとかあったらやだな。



右上のは通常描画時の深度バッファで、その下がシャドウマップ用の深度バッファ。 
モデルのまわりでライトを動かして見た感じ。
セルフシャドウと、床のシャドウができた。
平面の床限定の簡易的な方法もあるみたいだけど、これができればいらなさそう。

0 件のコメント:

コメントを投稿