UnityでGeometryShaderを使ってワイヤーフレーム表現

Unityでモデルをワイヤーフレームで表示しようとした際に、
元のモデルを表示しながらワイヤーフレームでも描画する方法が見つからなかった時に作った物です


![FireFrame1.png](https://qiita-image-store.s3.amazonaws.com/0/211811/4bc89380-8be9-03cc-f6ee-6f607142fe1b.png)

以下コード

~~~
Shader "Custom/Wireframe" {
	Properties{
		_WireFrameColor("WireFrame Color", Color) = (0,0,0,0)
		_MainTex ("Albedo (RGB)", 2D) = "white" {}
	}
	SubShader{
		Tags{ "Queue" = "Transparent" }
		
		// Pass1
		Pass{
			ZWrite On
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM

			#pragma vertex vert
			#pragma geometry geom
			#pragma fragment frag

			#include "UnityCG.cginc"
			#pragma target 5.0

			sampler2D _MainTex;
			float4 _WireFrameColor;

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2g
			{
				float4 vertex : SV_POSITION;
				float2 uv : TEXCOORD0;
				float4 color : TEXCOORD1;
			};

			struct g2f
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float4 color : TEXCOORD1;
			};

			v2g vert(appdata v)
			{
				v2g o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = v.uv;
				o.color = _WireFrameColor;

				return o;
			}

			[maxvertexcount(6)]
			void geom(triangle v2g input[3], inout LineStream<g2f> outStream)
			{
				v2g p0 = input[0];
				v2g p1 = input[1];
				v2g p2 = input[2];


				g2f out0;
				out0.pos = p0.vertex;
				out0.uv = p0.uv;
				out0.color = p0.color;

				g2f out1;
				out1.pos = p1.vertex;
				out1.uv = p1.uv;
				out1.color = p1.color;

				g2f out2;
				out2.pos = p2.vertex;
				out2.uv = p2.uv;
				out2.color = p2.color;

				outStream.Append(out0);
				outStream.Append(out1);
				outStream.RestartStrip();

				outStream.Append(out1);
				outStream.Append(out2);
				outStream.RestartStrip();

				outStream.Append(out2);
				outStream.Append(out0);
				outStream.RestartStrip();
			}

			fixed4 frag(g2f i) : SV_Target
			{
				return i.color;
			}
			ENDCG
		}

		// Pass2
		Cull Back
		CGPROGRAM
		#pragma surface surf Lambert

		sampler2D _MainTex;

		struct Input 
		{
			float2 uv_MainTex;
		};

		void surf(Input IN, inout SurfaceOutput o)
		{
			o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
		}
		ENDCG
	}
}
~~~

#__Geometryの中身について__
`void geom(triangle v2g input[3], inout LineStream<g2f> outStream)`でgeometory用の関数を宣言しています
#####入力
`triangle v2g input[3]` 
ポリゴンを形成する三角形の頂点を`Input[3]`の仮引数で取得
ここでのInput[]はv2g構造体の情報を持っています
#####出力
`inout LineStream<g2f> outStream`
`LineStream<g2f>`と記述することにより、ストリーム情報から二つの頂点を結ぶ直線を生成してくれます
ほかにも三角形を生成するTriangleStream<>やPointStream<>などがあります
#####geometory内の処理

```
g2f out0;
out0.pos = p0.vertex;
out0.uv = p0.uv;
out0.color = p0.color;

g2f out1;
out1.pos = p1.vertex;
out1.uv = p1.uv;
out1.color = p1.color;

g2f out2;
out2.pos = p2.vertex;
out2.uv = p2.uv;
out2.color = p2.color;
```
上記は出力がg2f構造体になるため、入力をg2fに変換しています
次に


```
outStream.Append(out0);
outStream.Append(out1);
outStream.RestartStrip();

outStream.Append(out1);
outStream.Append(out2);
outStream.RestartStrip();

outStream.Append(out2);
outStream.Append(out0);
outStream.RestartStrip();
```
`outStream.Append()`に出力用の頂点を渡すことによって現在のストリームに頂点を追加することができます
今回は一つのストリームに二つの頂点を追加します
二つ頂点を追加したら`outStream.RestartStrip()`で現在のストリームを終了して新しいストリームを開始することができます

実は今回の場合は上の記述を

```
outStream.Append(out0);
outStream.Append(out1);
outStream.Append(out2);
```
この三行に置き換えても、出力をLineStreamにしているので勝手に三つの頂点をそれぞれ結んだ直線を生成してくれます

最後に、作ったShaderをオブジェクトのマテリアルに取り付けます

#__完成__
![WireFrame.png](https://qiita-image-store.s3.amazonaws.com/0/211811/c577ec37-6f33-e7a7-be5e-21850daef631.png)
こんな感じで表示されれば完成
マテリアルの_WireFrameColorでワイヤーフレームの色を変えることができます
ワイヤーフレームだけの表示にしたい場合は

```
// Pass2
Cull Back
CGPROGRAM
#pragma surface surf Lambert

sampler2D _MainTex;

struct Input 
{
	float2 uv_MainTex;
};

void surf(Input IN, inout SurfaceOutput o)
{
	o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
```
コード内の上記の部分を削除するとワイヤーフレームだけになると思います

曲に合わせて頂点の高さや色を変えれば
こんな感じになったり
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">曲に合わせてわらわら動く<br>♬おとわび/古都に咲く花 <a href="https://t.co/b5t7jZlEGs">pic.twitter.com/b5t7jZlEGs</a></p>&mdash; どな (@dona_ddna) <a href="https://twitter.com/dona_ddna/status/982493094204469248?ref_src=twsrc%5Etfw">2018年4月7日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

###__注意__
GeometryShaderを使っている関係で一部端末で動かないかもしれません

#__おわりに__
Shaderは工夫一つで面白い表現ができるので楽しいですよね！
普段何気なく見ていたワイヤーフレームの表現も自分で実際に作ってみると意外と難しくて驚きました。

あと、記事内の説明で間違っている部分があるかもしれません、その場合はコメントを頂けるとありがたいです。