[Unity] ファーシェーダを移植してみた


[こちらで実装されているファーシェーダ](http://oos.moxiecode.com/js_webgl/fur/)の仕組みをUnityに移植してみました。

参考にしたほうがWebGLで実装されていて、動きもあってだいぶふさふさな感じです。
これをUnityでやれないかとあれこれ調べて、なんとか同じような感じになるようにしてみました。
だいぶ力技部分もありますが・・ｗ
*（余談ですが、WebGLコンテンツを参考にする場合は[Firefoxのシェーダエディタ](https://developer.mozilla.org/ja/docs/Tools/Shader_Editor)が超絶便利です）*

ちなみに作ったサンプルは[GitHub](https://github.com/edom18/Fur-shader-sample)に上げてあります。
（[動くサンプル](http://edom18.github.io/Fur-shader-sample/)もあります）

![fur.png](https://qiita-image-store.s3.amazonaws.com/0/5616/eae4c164-81e4-1a63-3ce3-3626a763c2a4.png "fur.png")
*作ったキャプチャ*

[2014.09.24 追記]
バンプマッピングによるライティングを追加してみました。（[サンプル](http://edom18.github.io/Fur-shader-sample/Fur-shader-with-bump.html)）
[![bump-sample.jpg](https://qiita-image-store.s3.amazonaws.com/0/5616/e6cc38dd-e694-cb71-4b1d-e5d10535ab9e.jpeg "bump-sample.jpg")](http://edom18.github.io/Fur-shader-sample/Fur-shader-with-bump.html)



## まずざっくり概要から

そもそもファーシェーダってなにかというと、3Dでふさふさな「ファー」を表現するシェーダです。
こちらの[ファーシェーダー](http://maverickproj.web.fc2.com/pg54.html)の記事が概念を理解しやすいと思います。

実装方法や実現方法のアプローチはいくつかありますが、上記で書かれているのも今回やってみたのも同じアプローチのものです。

### 層を重ねる

記事の図を見てもらえれば分かりますが、ひとつのモデルを少しずつ膨らませて、何層にも分けてレンダリングするアプローチです。
要は **毛の断面図を何層も重ねたら毛っぽくなるよね** ってことです。

記事では、バンプマッピングを使って毛にもライティングを施しさらにリアルにしていますが、今回はそこまではやっていません。

### テクセルから色をどう取るか

ちなみに今回は以下のテクスチャを使いました。

![kirin.png](https://qiita-image-store.s3.amazonaws.com/0/5616/4457ee85-25cb-b2a5-9a83-5bb7c11e3f0e.png "kirin.png")


このテクスチャは毛の断面ではありません。
なので断面になるようにする工夫が必要です。
そこで、以下のような断面っぽいテクスチャを別途用意します。

![noise.png](https://qiita-image-store.s3.amazonaws.com/0/5616/b122353d-a9c1-4565-5f66-23d1d5bde2aa.png "noise.png")


さて、これをどう使うのか。
毛っぽく見せるわけですから、最初のテクスチャから色を取るところと取らないところを決めればいいわけです。
そうすれば毛の断面図のようなテクスチャとして使えるわけです。

*ちなみに余談ですが、テクセルは「テクスチャのピクセル」でテクセルです。*

## フラグメントシェーダ

結論から言うと、 **上記ふたつ目のテクスチャの中のテクセルが「ある閾値」以下の場合に破棄する** という実装を行います。
（フラグメントシェーダで計算中のピクセルを破棄するには`discard`を使います）

以下がフラグメントシェーダのコードです。
やってること自体はほんとにシンプルですね。

```Cg
// フラグメントシェーダへの入力構造体
struct vert2frag {
	float4 position : POSITION;
	float2 uv       : TEXCOORD0;
	float2 uv2      : TEXCOORD1;
};

// ...中略...

// フラグメントシェーダ
float4 frag(vert2frag i) : COLOR {
	float4 map = tex2D(_SubTex, i.uv2);
	if (map.a <= 0.0 || map.b < FUR_OFFSET) {
		discard;
	}

	float4 color = tex2D(_MainTex, i.uv);
	color.a = 1.1 - FUR_OFFSET;
	return color;
}
```

フラグメントシェーダではふたつのテクスチャを参照しています。
`_MainTex`と`_SubTex`です。

`_MainTex`は普通のテクスチャです。上記の画像では上部の色がついたほうです。
そしてキモとなるのが`_SubTex`で、白黒のほうのテクスチャです。

まず最初にサブテクスチャから、現在計算中のテクセルを取り出します。

```Cg
float4 map = tex2D(_SubTex, i.uv2);
```

そしてその取り出したテクセル情報から、レンダリングすべきかどうかを判断する処理を行います。

```Cg
if (map.a <= 0.0 || map.b < FUR_OFFSET) {
	discard;
}
```

アルファが0以下か、あるいは青成分（`map.b`）が特定の値以下だったらピクセルをレンダリングしない（`discard`）という意味です。

`FUR_OFFSET`はファーのオフセットで、今回は60層に分けてレンダリングしているので「層 / 60」の値が、層ごとに渡ってきます。
（例えば30層目だったら「30 / 60 = 0.5」）


## バーテックスシェーダ

フラグメントシェーダは数行からなるだいぶシンプルな実装でした。
バーテックスシェーダのほうは若干複雑です。

まずはコードから。

```Cg
// 頂点シェーダへの入力構造体
struct vertInput {
	float4 vertex    : SV_POSITION;
	float4 normal    : NORMAL;
	float2 texcoord  : TEXCOORD0;
	float2 texcoord2 : TEXCOORD1;
};

// ...中略...

uniform float4 _Gravity;

vert2frag vert(vertInput v) {

	const float spacing = 0.35;
	
	vert2frag o;
	
	float3 forceDirection = float3(0.0);
	float4 position = v.vertex;
	
	// Wind
	forceDirection.x = sin(_Time.y + position.x * 0.05) * 0.2;
	forceDirection.y = cos(_Time.y * 0.7 + position.y * 0.04) * 0.2;
	forceDirection.z = sin(_Time.y * 0.7 + position.y * 0.04) * 0.2;
	
	float3 displacement = forceDirection + _Gravity.xyz;
	
	float displacementFactor = pow(FUR_OFFSET, 3.0);
	float4 aNormal = v.normal;
	aNormal.xyz += displacement * displacementFactor;
	
	float4 n = normalize(aNormal) * FUR_OFFSET * spacing;
	float4 wpos = float4(v.vertex.xyz + n.xyz, 1.0);
	o.position = mul(UNITY_MATRIX_MVP, wpos);
	o.uv  = v.texcoord;
	o.uv2 = v.texcoord2 * 20.0;

	return o;
}
```

まず、バーテックスシェーダへの入力として以下の情報を受け取ります。

```Cg
// 頂点シェーダへの入力構造体
struct vertInput {
	float4 vertex    : SV_POSITION;
	float4 normal    : NORMAL;
	float2 texcoord  : TEXCOORD0;
	float2 texcoord2 : TEXCOORD1;
};
```

頂点情報として位置と法線、ふたつのテクスチャのUV座標が渡ってきているわけですね。
*（`: SV_POSITION`などはセマンティクスと呼ばれています。この辺りは話がそれるので割愛します）*

### 層のレンダリング

コードには`forceDirection`なる変数がありますが、このあたりは実際に動かした時に風にふかれているような演出をしているだけなのでなくても動きます。
キモとなる部分は以下の部分です。

```Cg
float4 n = normalize(aNormal) * FUR_OFFSET * spacing;
float4 wpos = float4(v.vertex.xyz + n.xyz, 1.0);
o.position = mul(UNITY_MATRIX_MVP, wpos);
o.uv  = v.texcoord;
o.uv2 = v.texcoord2 * 20.0;
```

最初の`n`が、実際にモデルをふくらませるための係数になってます。
（`aNormal`は上記の風を考慮した状態なので、単純に`v.normal`を使っても動きます）

法線情報に、ファーのオフセットと`spacing`という係数を掛けたものを、実際の頂点位置（`v.vertex`）に足しているわけですね。
この`spacing`の値を変えるとファーの層の間隔が変化します。（サンプルでは0.35）

### 注意点

最初、どちらも`float4`なのでそのまま足していました。

```Cg
float4 wpos = v.vertex + n;
```

しかし、こうしてしまうと`w`の値がおかしくなり、モデルの形状がおかしくなってしまうので、`xyz`のみ計算して、`w`の値を`1.0`にする必要があります。

### UV座標を計算

最後の部分がUV座標に関する計算です。
といっても、メインのテクスチャはそのまま、サブテクスチャのみ20倍しているだけですね。
この20倍している部分は別になくても問題ありません。単に、使用している断面のテクスチャの細かさを調整しているだけなので。
なので10倍や30倍などと値を変えると毛の雰囲気も変わります。

ちなみに5倍くらいにしたやつだとこんな感じになります↓（ふさふさ感割増）
![fur2.png](https://qiita-image-store.s3.amazonaws.com/0/5616/57a8d007-70af-71f0-43b2-a0bd07282953.png "fur2.png")


## 複数Passで層をレンダリング

今回移植するにあたって悩んだところが、ひとつのシェーダでどうやって層をレンダリングさせるか、でした。
参考にしたWebGLの実装は、事前にモデルを60個生成して、それぞれレンダリングすることで実現していました。
ただ、Unityの場合はそうした実装ができません。

しかしUnityには`Pass`というコマンドがあり、この`Pass`がまさにひとつのモデルをレンダリングする役割を持っています。
なので、今回の実装が力技だと書いたのは、この`Pass`を単純に60回書いているからです。

### Passの例

```Cg
Pass {
    CGPROGRAM
    
    #pragma vertex vert
    #pragma fragment frag
    
    #define FUR_OFFSET 0.016
    
    #include "FurHelper.cginc"
    
    ENDCG
}

// 以下、0.016ずつ増えるPassを60回書く
```

この、`#define FUR_OFFSET 0.016`となっている部分が微妙に異なる`Pass`を60回書いている、というわけですね。（他にいい方法あったら教えて下さいorz）
（もちろん、実際は60回分の`Pass`を書き出してくれるプログラムを別に作ってますが・・）

ちなみにこの`Pass`を複数回書くヒントは[UnityのForum](http://forum.unity3d.com/threads/fur-shader.4581/)で話されていたものを参考にしました。

だいぶ冗長な書き方ではありますが、とりあえずUnity上で動くものが作れたのでよしとしますｗ


## Unity AssetStoreに並びました

[![fur-shader-sample.png](https://qiita-image-store.s3.amazonaws.com/0/5616/f51e0562-1b4d-f0d9-0f03-07f41bca823e.png "fur-shader-sample.png")](https://www.assetstore.unity3d.com/jp/#!/content/22226)

[Simple Fur Shader | Unity Asset Store](https://www.assetstore.unity3d.com/jp/#!/content/22226)

※ AssetStoreのものはさらに、 **通常のライティングとバンプマッピングによるライティングを施したもの** となっています。


## KAYACのLPでも使ってます

[KAYACのWebGLサイト制作募集のサイト](http://www.kayac.com/service/point/webgl/)。
ここでもファーシェーダが使われています。実装方法はほぼ同じ。
こちらのサイトではさらにいくつかのシェーダを切り替えてロゴの見た目を変えられるようになってます。
iOS8なら実際に動いているところが見れるので見てみてください( *'-')

[![kayac.png](https://qiita-image-store.s3.amazonaws.com/0/5616/c3ff8347-75ad-5ca7-5204-0fe945610534.png "kayac.png")](http://www.kayac.com/service/point/webgl/)