今回はアウトラインシェーダについて
調べたところ、ここで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 ) ;
}














