【Unity】シェーダーで面を凸凹(デコボコ)する方法【Tessellation・Displacement】

shaderの勉強をしていると、こんな疑問が出てきませんか？

**テクスチャの明度に応じて、面をデコボコさせたいけど、どうすればよいんだ？**

ツール(Shader Graph, Shader Forge)を使って、面を凸凹するシェーダーを作成するやり方はwebに色々あるんですが、ツールなしの方法が見当たない…

途方に暮れていたのですが、奇跡的に知り合ったUnityコミュニティに質問したところ、やり方が判明したので、ご紹介します。サンプルソースもありますよ！

ちなみに、タイトルは私のように”シェーダー 平面 凸凹(でこぼこ)”とググっても出てくるように設定しました笑

※追記 2019/1/28
ここでは、頂点を上下させて面を凸凹させる方法をご紹介します。他にも、凹凸が「あるように」見せるバンプマッピングという手法がございます。こちらの方法は、以下の記事をご覧ください。
・[[Unity] バンプマッピング（法線マッピング）をやってみる
](https://qiita.com/edo_m18/items/458e3b1992ef8a3f37a5)

## 結論：できる！！！
結論から述べると、ツール無しでもシェーダーで平面を凸凹することはできます！

じゃぁ、なぜ”シェーダー 平面 凸凹(でこぼこ)”とググっても出てこないか？

それは平面を凸凹する方法は専門用語で存在しており、みんな専門用語で紹介しているからなんですね。なので、専門用語を知らないと、答えには辿り着かないという(^p^)

## キーワードはTessellation・Displacement！
平面凸凹のキーワードは２つです！

- Tessellation(テッセレーション)：ポリゴンメッシュを細分割
- Displacement(ディスプレイス)：テクスチャの高さマップを描画時に評価して頂点を上下

テッセレーションでメッシュを再分割し、凹凸しやすくしてから、ディスプレイス(頂点を上下)します。イメージはこんな感じです！

## サンプルソース
やらたいことがわかっても、肝心なソースがなければ、実装できないですよね。ご安心ください。

なんと、公式が提供している"Standard Assets"にあるんです(マジで神)

ソースの場所と中身がこちら！
<img width="599" alt="スクリーンショット 2019-01-27 10.08.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/314351/d82e57e1-c0ae-7fd2-0155-02ed716ab16f.png">
<img width="704" alt="スクリーンショット 2019-01-27 10.08.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/314351/16719d4b-8041-f23c-0145-0717cfc573cf.png">
<img width="343" alt="スクリーンショット_2019-01-27_10_12_37.png" src="https://qiita-image-store.s3.amazonaws.com/0/314351/f789cdf6-b3a9-320b-b9c7-9f44ff458026.png">

インポート後、`Assets > Standard Assets > Effects > TessellationShaders`にシェーダーやマテリアファイルが入っています！

ソースの中身はこんな感じ！

```pedSpecularDisplacement.shader
Shader "Tessellation/Bumped Specular (displacement)" {
Properties {
	_Color ("Main Color", Color) = (1,1,1,1)
	_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
	_Shininess ("Shininess", Range (0.03, 1)) = 0.078125
	_Parallax ("Height", Range (0.0, 1.0)) = 0.5
	_MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
	_BumpMap ("Normalmap", 2D) = "bump" {}
	_ParallaxMap ("Heightmap (A)", 2D) = "black" {}
	_EdgeLength ("Edge length", Range(3,50)) = 10
}
SubShader { 
	Tags { "RenderType"="Opaque" }
	LOD 800
	
CGPROGRAM
#pragma surface surf BlinnPhong addshadow vertex:disp tessellate:tessEdge
#include "Tessellation.cginc"

struct appdata {
	float4 vertex : POSITION;
	float4 tangent : TANGENT;
	float3 normal : NORMAL;
	float2 texcoord : TEXCOORD0;
	float2 texcoord1 : TEXCOORD1;
	float2 texcoord2 : TEXCOORD2;
};

float _EdgeLength;
float _Parallax;

float4 tessEdge (appdata v0, appdata v1, appdata v2)
{
	return UnityEdgeLengthBasedTessCull (v0.vertex, v1.vertex, v2.vertex, _EdgeLength, _Parallax * 1.5f);
}

sampler2D _ParallaxMap;

void disp (inout appdata v)
{
	float d = tex2Dlod(_ParallaxMap, float4(v.texcoord.xy,0,0)).a * _Parallax;
	v.vertex.xyz += v.normal * d;
}

sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;

struct Input {
	float2 uv_MainTex;
	float2 uv_BumpMap;
};

void surf (Input IN, inout SurfaceOutput o) {
	fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
	o.Albedo = tex.rgb * _Color.rgb;
	o.Gloss = tex.a;
	o.Alpha = tex.a * _Color.a;
	o.Specular = _Shininess;
	o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
}

FallBack "Bumped Specular"
}

```

マテリアルをアタッチすると、凸凹した面ができあがります！
（マテリアルもインポートしたスタンダードアセット内にあります）
![unity_qiita.gif](https://qiita-image-store.s3.amazonaws.com/0/314351/d0ff7ec9-dc27-7b6d-4973-be976eb62cf1.gif)

## 最後に
Tessellation・Displacementを使えば、シェーダーで面を凸凹できます。凸凹はテクスチャによるので、種類は無限大！

平面以外にも、棒などの様々なオブジェクトを凸凹したい時にも使えるので便利です。

公式のシェーダーを使えば簡単に実装できるので、ぜひ気になる方はお試しください！

## 参考記事
- [Unity シェーダーチュートリアル ディスプレイスとテッセレーション](http://tsumikiseisaku.com/blog/shader-tutorial-displace/)
- [UnityのTessellationを使って色々してみる](http://tsubakit1.hateblo.jp/entry/2015/07/12/233342)
- [Unity で距離に応じたテッセレーションを行ってみた](http://tips.hecomi.com/entry/2016/11/28/232726)