2026年5月10日日曜日

ステンシル描画仕様変更

ステンシルの仕組みを使ってアウトラインと遮蔽物の影を描画できるようにスプライト追加時にパラメータ指定できるようにした。


今回作ったステンシルの仕組み


ステンシルで実現する際、通常のスプライト描画とは別でステンシル書き込み専用のシェーダを実行していた。スプライトではまとめて描画したり、Z値によりソートして描画したりで、特定のスプライトだけで描画しているわけではないので、アウトラインで同じアウトライン色で各データをまとめて、ステンシル書き込み専用で描画する。
その後、ステンシル値を指定して単色やスプライトで塗りつぶしたりするんだけど、ちょっと回りくどい。

形状指定でステンシル値を書きこんで、単色やスプライトで塗りつぶす機能だけ残して、アウトラインと遮蔽物の影の仕組みは別で作ることにした。


作り直した仕組み


通常の描画時に深度と同時にステンシルも書き込んで、その後ステンシルマスクを利用するということであれば無駄がなかったんだけど、ステンシル値の為だけの専用描画と、そのステンシル値の部分だけ塗りつぶすという2ステップになってしまっていたので、それを止めて直接描画してしまう。

やってることはステンシルの時と同じで、ちょっと拡大した絵を描画して、透明以外の部分にアウトラインで指定した色を出力。今回は直接アウトラインの色を描き込める。
ただ、アルファブレンドありで透過部分を無視してclip/discardなしでシェーダかけるかなと思ったけど、いろいろ試行錯誤して無理という結論に至り、最大3回同じシェーダを実行する。

1回目:拡大したスプライトを指定色で描画。
2回目:深度バッファを有効にしつつ、比較演算子をLESS_EQUALにして中抜き描画。
3回目:深度バッファを有効にしつつ、比較演算子をGREATERにして遮蔽物の影を描画。

float4 c = Tex[ NonUniformResourceIndex( In.tno )].Sample( Sampler, In.UV ) ;
clip( c.a - 0.5 ) ; // 閾値以下は破棄
uint w = uint( In.Col.w ) ;
switch( w ) {
case 0u :	// 中抜き
	Out.Col = float4( 0, 0, 0, 0 ) ;
	break ;
case 2u :	// 遮蔽物の影
	Out.Col = c * 0.25f ;
	break ;
default :
	Out.Col = In.Col ;
	Out.Col.a *= c.a * 0.75f ;
	break ;
}
Out.Col.rgb *= c.a ;

In.Colで制御していて、float4(0,0,0,0)は中抜き用、float4(0,0,0,2)は遮蔽物の影用の特殊カラー。それ以外の色はアウトライン色になる。



オレンジの部分が遮蔽物。
緑と青がアウトラインで、緑の方だけ遮蔽物の影を描画。

0 件のコメント:

コメントを投稿