2023年2月26日日曜日

FrustumCullingと別カメラの視点

技術関連のページを調べていると、Unityの記事が多い。
Unityのエディタ上で視錐台と各種オブジェクトが表示されている画面と、視錐台の視点(実際の見た目)の画面を同時に見ていたりする。

次は視錐台カリングにチャレンジしてみようと思ったが、まず上記のような別視点で確認できるようにして、本当に裏で消えているのか確認できるようにしたい。


別カメラ画面


まず通常の描画を行い、それをレンダーターゲットではなく中間レンダーターゲットに出力する。
次に、サブカメラを追加してそのカメラ視点の描画を同じシーンに対して行う。こっちの描画はレンダーターゲットに直接行い、通常の描画で作った中間レンダーターゲットを最後に描画する。影の処理は通常処理で済んでいるので、2回目ではスキップできるようにしている。
これはあっさりと出来た。

メインカメラとサブカメラの画像


視錐台の描画


次に視錐台の描画をやってみる。
カスケードシャドウをやったとき、ライトカメラからの視錐台を作っていたのでそれを応用してみる。各頂点はメインカメラからの情報で作って、VertexBufferに書き込む。FarZは30ぐらいにしておく。プリミティブはD3D_PRIMITIVE_TOPOLOGY_LINELISTで描画する。

これもそれっぽく表示されたけどなんか実際のカメラの回転とずれている。
視錐台のWorldマトリクスをどうすればいいのか2日ぐらいハマった。
いろいろ試行錯誤した結果、Viewの逆行列でいいということにたどり着いた。

視錐台描画


視錐台カリング


視錐台カリングで検索するといくつか見つかる。
点であれば、View×ProjectionでXMVector4Transformを呼び出して、あとは-wからwの範囲に、x,y,zが含まれていれば表示対象ということはわかった。
AABBでのやり方も出てくるが、スマートではない。
視錐台と球の判定がやりたい。

マイクロソフトのサンプルにMeshShaderでGPU側でFrustumCullingをやっているものを見つけた。これを参考にすれば出来るかもしれない。
実装した結果それっぽくはなるが、視錐台を横から対象のオブジェクトみて視錐台の左右の壁にぶつけたとき、かなり早めにカリングされてしまう状況になった。

面の作成

	auto oPV = oCam.oCamera.GetViewMatrix() * oCam.oCamera.GetProjectionMatrix() ;
	tDMatrix vp = DirectX::XMMatrixTranspose( oPV ) ;
	tDVector oPlanes[] = {
		DirectX::XMPlaneNormalize( DirectX::XMVectorAdd( vp.r[3], vp.r[0])),		// Left
		DirectX::XMPlaneNormalize( DirectX::XMVectorSubtract( vp.r[3], vp.r[0])),	// Right
		DirectX::XMPlaneNormalize( DirectX::XMVectorAdd( vp.r[3], vp.r[1])),		// Bottom
		DirectX::XMPlaneNormalize( DirectX::XMVectorSubtract( vp.r[3], vp.r[1])),	// Top
//		DirectX::XMPlaneNormalize( vp.r[2]),										// Near
//		DirectX::XMPlaneNormalize( DirectX::XMVectorSubtract( vp.r[3], vp.r[2])),	// Far
	} ;

これも2日ぐらいハマった。
各メッシュの中心と半径でチェックを行う際、World×View×ProjectionのマトリクスでXMVector4Transformを呼び出して、半径はスケール調整して後は面ごとに内積した結果を-半径以下になったら範囲外という判定をする。判定の面はNearとFarは除外した。

判定部分

	auto oV = DirectX::XMVector4Transform( oCenter, oTrans ) ;
	auto r = oM.nRadius * DirectX::XMVectorGetX( oScale ) ;

    for( auto & oP : oPlanes ) {
		auto d = DirectX::XMVectorGetX( DirectX::XMPlaneDotCoord( oP, oV )) ;
        if( d < -r ) {
			o.bCulling = true ;
			break ;
        }
    }

内積の関数をXMVector3Dot、XMVector4Dotを使っていたが、XMPlaneDotCoordの存在を知り変更。若干良くなるがまだ判定がおかしい。
最終的はoTransがWorld×View×Projectionではなく、Worldだけにしたらうまく行った。

視錐台カリング


今回はメッシュのグループ単位でカリングを行ったが、マテリアルが変わる単位でもカリングができそう。ただそれをやるとDrawコールを分ける必要が出てきてむしろ遅くならないかが心配。
いろいろ調べてみると、全部GPU側で判断させてドローコールもGPU側でしてしまうのが速いっぽい。MeshShaderに手をだす日も近いか・・・

 


0 件のコメント:

コメントを投稿