2023年5月18日木曜日

Outline Shader

今回はアウトラインシェーダについて

調べたところ、ここで4つの方法が紹介されていた。

1つ目の方法は背面法と呼ばれるもので、主流?の方法らしい。
ただ色々問題があって、手間を掛けないとちゃんとした枠線が描けない。

2つ目はステンシルバッファを使った方法。
まだ自分のライブラリにステンシルバッファが組み込まれていないので後回し。
3Dオブジェクトの裏をキャラクタが通った時、ステンシルのアウトラインがそのままシルエット代わりになる方法が使えそうなので、今後やってみる予定。

3つ目は深度テクスチャを使う方法。

4つ目は法線テクスチャを使う方法。

他にも調べていたところ、深度テクスチャ、法線テクスチャの両方を使ってアウトライン描画を行うここのサンプルがシンプルで分かりやすかったので参考にさせてもらい実装してみた。


深度テクスチャを使ったアウトライン描画


対象の点と付近の点(サンプルでは4点)の差分を合算する。


深度テクスチャのアウトライン描画

深度テクスチャだけを使ったアウトライン描画だと、外枠のみが描画される


法線テクスチャを使ったアウトライン描画


深度テクスチャと同じように対象の点と付近の点(サンプルでは4点)の差分を取って、xyzの成分を足し合わせて合算する。

ただこちらの方は結果が思ったようにならず、オブジェクトの正面から見ると線が描画されるけど(でも不完全)、裏に回ってみると、線が全く描画されなくなった。



表から見た場合

裏から見た場合

色々調べてみると法線がView空間になっているらしく、こちらの実装もView空間に変換したら裏側でも線が描画されるようになった。


法線テクスチャのアウトライン描画

法線テクスチャだけを使ったアウトライン描画だと、オブジェクトの中の線が描画される。


深度テクスチャと法線テクスチャを使ったアウトライン描画


両方のテクスチャの値を使って組み合わせるとお互いの結果が補完されていい感じの描画になる。

組み合わせたアウトライン描画

アウトラインシェーダ


float4 OutlineDepthNormal(
	in		float4			Col,		// ベース色
	in		float4			LineCol,	// 枠線色
	in		Texture2D		TexDepth,	// 深度テクスチャ
	in		Texture2D		TexNormal,	// 法線テクスチャ
	in		SamplerState	Smpler,		// サンプラ
	in		float2			UV,			// UV
	in		float			Depth,		// ベース深度
	in		float3			Normal,		// ベース法線
	in		float2			Screen,		// 画面サイズ
	in		float			FarZ,		// Far Z
	in		float4x4		TransV		// View変換行列
) {
	float2 sp = float2( rcp( Screen.x ), rcp( Screen.y )) ;
	float2 Offset[4] = {
		float2( UV.x		, UV.y + sp.y	), // 上
		float2( UV.x		, UV.y - sp.y	), // 下
		float2( UV.x + sp.x	, UV.y			), // 右
		float2( UV.x - sp.x	, UV.y			), // 左
	} ;

	float BaseZ = Depth * FarZ ;
	float OZ = 0.0f ;

	[unroll]
	for( uint i = 0 ; i < 4 ; i++ ) {
		float DZ ;
		DZ = TexDepth.Sample( Smpler, Offset[i]).r * FarZ ;
		OZ += BaseZ - DZ ;
	}

	OZ = saturate( OZ ) ;

	float ON = 0.0f ;
	float3 BaseN = mul( TransV, float4( Normal, 1.0f )).xyz ;

	[unroll]
	for( uint i = 0 ; i < 4 ; i++ ) {
		float3 N ;
		DeferredDecodeNomal( TexNormal.Sample( Smpler, Offset[i] ), N ) ;
		N = BaseN - mul( TransV, float4( N, 1.0f )).xyz ;
		ON += N.x + N.y + N.z ;
	}

	ON = saturate( ON ) ;

	return lerp( Col, LineCol, OZ + ON ) ;
}







0 件のコメント:

コメントを投稿