2022年8月18日木曜日

距離に応じたテッセレーション

前回の続き

細かくしてどうするのか?


テセレーションについて調べていく中で見つけたのが、ローポリゴンで作ったメッシュをカメラの距離に応じて細分化することにより、近くの場合は細かく詳細に、遠くの場合は荒くすることにより見た目とパフォーマンスを両立させることができるというもの。他にもいろいろ使用例があったけど、これが一番イメージが付きやすかった。

さっそくこれをやってみたい。

前回のプログラムでカメラとの距離に応じて係数を計算するようにしても頂点4つだけのメッシュでうまく反映されない。そこで頂点を11×11で四角が100個(三角が200個)分のデータを用意して描画してみた。

D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST

4頂点に対して、インデックスバッファを6個ずつで、100個分四角を書いているつもりだけど、こんな表示になってしまう。

試しにトポロジを今までのトライアングルリストで描画してみる

D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST

正しく表示され頂点バッファ、インデックスバッファは問題ないみたいだ。

ここでふと思いつく。

トライアングルリストの場合はややこしくて、頂点0(左上)、頂点1(右上)、頂点2(左下)、頂点3(右下)の4頂点ある場合、インデックスバッファは(0,1,2),(3,2,1)というふうに、時計回りで3点ずつ2つの3角形で指定する。

コントロールポイントの場合は、そのポイントがどれかを単純に指定すればいいのではないか?
頂点データは同じで、インデックスバッファは(0,1,2,3)の4つ(D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST
の場合)を単純に指定すればどうか。

D3D_PRIMITIVE_TOPOLOGY_4_CONTROL_POINT_PATCHLIST修正版

正しく表示された。
うまく行ったけど、これってインデックスバッファの中身変えないと使えないのか?
いままでQuadでやっていたけどTriで確認したら、今までのインデックスバッファの指定方法で行けた。もしかしてTriしか使わないかも?

距離に応じたテセレーション


こんな関数を用意した。
	float DistanceTessellationFactor( float4 v, float min, float max, float t )
	{
		float d = distance( v.xyz, gCam.Eye.xyz ) ;
		return clamp( 1.0 - ( d - min ) / ( max - min ), 0.01, 1.0 ) * t ;
	}

カメラの位置と頂点の位置をdistanceに渡してその結果に掛け目を掛けて係数を返す。

#define MINDIST 50.0
#define MAXDIST 120.0
	float3 f ;
	f.x = DistanceTessellationFactor( IP[0].Pos, MINDIST, MAXDIST, gHSParam.EdgeFactor ) ;
	f.y = DistanceTessellationFactor( IP[1].Pos, MINDIST, MAXDIST, gHSParam.EdgeFactor ) ;
	f.z = DistanceTessellationFactor( IP[2].Pos, MINDIST, MAXDIST, gHSParam.EdgeFactor ) ;
	float3 a ;
	float b, c ;
	ProcessTriTessFactorsAvg( f, 1.0f, a, b, c ) ;
	Out.Edge[0] = a.x ;
	Out.Edge[1] = a.y ;
	Out.Edge[2] = a.z ;
	Out.Inside = b ;

Quadなら4つ分の係数を計算して、Process2DQuadTessFactorsAvgまたはProcessQuadTessFactorsAvgを呼び出す。
Triなら3つ分の係数を計算してProcessTriTessFactorsAvgを呼び出す。
それぞれInsideScaleは1.0fを指定しておけば、EdgeFactorに応じていい具合の値を返してくれる。(イマイチbとcの違いがわからず)

Tri版

Quad版

遠くはそのままで、近くは細分化されているのがわかる。

0 件のコメント:

コメントを投稿