4 | Silhouette Rendering

## 基礎知識
シルエットレンダリングは、アウトライン（Outline）とも呼ばれる一般的な視覚効果で、非写実的レンダリングでよく見られます。Borderlandsシリーズのようなコミックスタイルの強いゲームでは、多くのアウトラインレンダリングが使用されます。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/7c930ce3-f6fb-33dc-e5c0-6131debb03ff.png)
Borderlandsシリーズのスクリーンショット

一般的な方法の1つは、ジオメトリ空間で、シーンが正常にレンダリングされた後、輪郭を描く必要のあるジオメトリを再レンダリングすることです。ジオメトリは、最初にその頂点位置を法線方向に沿って移動することによって拡大されます。次に、拡大されたジオメトリの背面のみを残して、正方向の面をカリングし、アウトライン効果を形成します。

効果は図のようになります。
![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/29e49a2b-2188-ccd0-6945-82dab925ea39.png)
ジオメトリ空間に基づいた方法は、本節で検討しません。

画面スペースに基づく別の後処理スキームがあり、キーリンクはエッジ検出（Edge Detection）です。エッジ検出の原理は、エッジ検出演算子を使用して画像に対して畳み込み演算を実行することです。一般的に使用されるエッジ検出演算子はSobel演算子で、水平方向と垂直方向の両方の畳み込みカーネルが含まれます。

![image3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/2c97ee4b-98f7-07c1-2c8b-12aa1f756682.png)


色、深度、その他の情報など、エッジにある隣接するピクセル間で特定の属性に明らかな違いがあると見なすことができます。 Sobel演算子を使用して画像を畳み込むと、隣接するピクセル間のこれらの属性の差を取得できます。これは勾配（gradient）と呼ばれ、エッジ部分の勾配値は比較的大きくなります。ピクセルの場合、水平方向と垂直方向にそれぞれ畳み込み演算を実行して、2つの方向の勾配値GxとGyを取得し、それによって全体的な勾配値を取得します：

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/b14bafc3-50b8-b15e-0b37-b8555ad0ab57.png)


フィルタリングするしきい値を設定し、エッジに配置したピクセルを保留し、それらに色を付けてアウトライン効果を形成します。

たとえば、色の変化が少ない3次元オブジェクトの場合、アウトラインの描画には深度情報が使用され、効果は次のようになります。

![image5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/83d11fb2-b43c-5da8-dcf1-2b04904d3c67.png)


## Unityの実装
上記のアルゴリズムに従って、UnityでBuild-inパイプラインを使用してアウトライン効果を実装し、静止画像を選んで色のプロパティの違いに応じて処理します。

まず、Sobel演算子を実装します。
```
half2 SobelUV[9] = { half2(-1,1),half2(0,1),half2(1,1),
					half2(-1,0),half2(0,0),half2(1,0),
					half2(-1,-1),half2(0,-1),half2(1,-1) };
half SobelX[9] = { -1,  0,  1,
					-2,  0,  2,
					-1,  0,  1 };
half SobelY[9] = { -1, -2, -1,
					0,  0,  0,
					1,  2,  1 };
```
演算子に従って画像をサンプリングし、fixed4タイプのカラー値を取得します。RGBAという4つのチャネルが含まれているため、いくつかの重みを設定して明るさの値を計算することができます。たとえば、平均値の計算を選択します。
```
fixed Luminance(fixed4 color)
{
	return 0.33*color.r + 0.33*color.g + 0.34*color.b;
}
```
明るさの値と演算子に従って勾配を計算します。
```
half texColor;
half edgeX = 0;
half edgeY = 0;
for (int index = 0; index < 9; ++index)
{
	texColor = Luminance(tex2D(_MainTex, i.uv + _MainTex_TexelSize.xy*SobelUV[index]));
	edgeX += texColor * SobelX[index];
	edgeY += texColor * SobelY[index];
}
half edge = 1-sqrt(edgeX*edgeX + edgeY * edgeY);
```
変数edgeの値が0に近いほど、境界と見なされます。

次に、描画します。輪郭のみを描画できます。
```
fixed4 onlyEdgeColor = lerp(_EdgeColor, _BackgroundColor, edge);
```
効果は図のようです。
![image6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/d282388c-4bd5-ab55-030a-f6a7f834046a.png)



元の画像をブレンドしてアウトラインすることもできます。
```
fixed4 withEdgeColor = lerp(_EdgeColor, tex2D(_MainTex, i.uv), edge);
```
効果は図のようになります。

![image7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/799ca446-9d8f-4591-477b-8927c8513911.png)


元の画像は次のとおりです。
![image8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/8a8711b9-c7fd-e9fa-e7bf-998d509a8afd.png)



アウトラインの色を調整して、効果は図のようになります。

![image9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653855/1e7c84c5-6df4-4729-87b6-beaee1086d98.png)

---
UWA Technologyは、モバイル/VRなど様々なゲーム開発者向け、**パフォーマンス分析**と**最適化ソリューション**及び**コンサルティングサービス**を提供している会社でございます。

今なら、UWA GOTローカルツールが**15日間に無償試用**できます！！
よければ、ぜひ！

UWA公式サイト：https://jp.uwa4d.com
UWA GOT OnlineレポートDemo：https://jp.uwa4d.com/u/got/demo.html
UWA公式ブログ：https://blog.jp.uwa4d.com