【Unity】SpriteにNormalMapを作って入れてみた

# はじめに
Unityの2DのURPではLight2Dというものがあり、2Dゲームにいい感じのライティング設定をつけられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/51937c43-d2b9-e72e-3e95-187779f2e041.png)

いい感じのライティングの例

https://unity.com/ja/how-to/2d-light-shadow-techniques-in-the-universal-render-pipeline

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/39ab76d6-0719-26ea-1ab4-2df5def7e6cf.png)

公式の雑なスクショですが
* 光が電柱で遮られてそれっぽい影ができていたり(右上)
* NormalMapを使っていたりします(右下)

ならそもそもこのNormalMapはどうやって作ってどうやってUnityに取り込んで使用するのか気になったので作ってみました。
# NormalMapを作成する
**Spriteilluminator**というソフトを使用します。

https://www.codeandweb.com/spriteilluminator

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/9c42ae3f-a288-a977-fd75-613b0bb283dd.png)
こんな感じの画面のソフト

こちらを使用すると取り込んだ画像にNormalMapを調整して入れることができます。
無料版と有料版があり、無料版はおそらく画像の出力ができません。今回は手元でパッと検証を行いたいだけなので無料トライアル版を使用します。これなら出力できました。

ちなみにソフトの使い方は公式サイトの動画を見てください。
なんとなく画面を見ていれば使い方がわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/1dc9c682-c79c-730a-2145-17806361e423.png)
パラメータを調整してベベルがつけれるのでつけます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/800327f7-393a-80a0-a0d3-fb7f34c9a63c.png)

LitTextureを押すと光源を動かしてどんな光り方がするのか確認できます。
遮蔽されててNormalが効いてていい感じなので出力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/6b7c467c-bec0-fb00-d0af-1bc35c964e3f.png)

ツール上部の`ExportNormals`で出力できます。

![portrait_kohaku_01_n.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/cc7bdad8-670c-a23a-d6a7-68f1a3d785f1.png)

これが完成したNormalMapです。

# Unityに入れる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/a07d9076-28fe-eeb6-b82e-a35125cd5454.png)
Unityにドラッグアンドドロップし、Textureの設定を行います。
NormalMapなのでSpriteModeでNormalMapを選択し、Applyします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/4cd474cc-42b1-590d-108e-03b5bb0128fb.png)
元のカラーテクスチャも入れておきます。
こっちは設定することは無いです。

# Unityでライティングする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/837a97e3-8bb4-80fc-5055-a42b8da8c514.png)
2DObjectからSquareを置きます。
`SpriteRenderer`にはさっきのカラーテクスチャを入れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/b11ad98b-e149-44cc-471d-4fed552559f3.png)
このままだと真っ暗で何も見えないのでライトを置きます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/fca7d660-1bf5-94c7-2e42-0b7b4b5d8c43.png)
`GlobalLight2D`を置きます。
光過ぎている場所にいくら光源を置いても明るくて変化がわからないので`Light2D`の`Intensity`を下げておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/c8a1fa05-d92e-b06e-274f-70098b31ed20.png)
0.3に調整した様子。ちょっと暗い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/685fb0f7-3480-5368-4ded-bc6df00f5660.png)
Sprite用の光源を設置します。
QualityをAccurateにし、Distanceを適当な値にします。0.2とかでいいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/9db5fa1e-dcc8-56e0-38c9-e7a8f9ad9650.png)
NormalMapに対応するように設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/337c23c7-17c5-e914-57e7-b2cb7b8e844d.png)
顔だけちょっと明るくなっていていい感じです。
ここに作ったNormalMapを設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/e892785d-b6d7-dee6-d85f-a088378eaafd.png)
元のカラーテクスチャのSpriteEditorを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/5350a15d-1ce6-80bd-5d3d-91996277f6dd.png)
左上から`Secondary Textures`を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/3b31cd73-efe6-144b-aab9-d41e2f89d78b.png)
右下がSpriteのBorderやPivotを設定するモードからSecondaryTextureを設定する画面に切り替わります。
ここに`_NormalMap`という名前で作ったNormalMapを入れます。
必ず`_NormalMap`という命名でないといけないわけではないです。デフォルトのUnityのLight2DのShaderはNormalMapにこの命名でアクセスしているのでデフォルトのShaderを使用するならこの命名でないとアクセスされず、設定されないです。
逆に一からShaderを書く場合は無視して他の命名にしても問題ないです。
設定したらApplyします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/d3c031a5-b700-db99-d695-e18964559aa8.png)
Spriteに`ShadowCaster2D`を付けます。(画像はEditor拡張されていて見た目が通常と異なります。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/5d4281ba-d1d4-33a2-1992-6c05b08414e5.png)
`EditShape`でSpriteの形を設定します(Editor拡張はポリゴンに沿ってこの形を設定するEditor拡張です)

https://github.com/ayaha401/AutoCreateShadowCaster2DShape

ここにソースコードがあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/c266079f-3545-bdbb-db7b-ef128a2b9605.png)
今回はこんな感じ(おかしいところ少しあるが…)


この状態でライトを近づけると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/2fec9c28-afcf-2e6c-bd2f-c59f1a34551c.png)
遮られていますね

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/64592480-67cf-b931-e173-041699edbc75.png)
NormalMapがOffの時

# Shaderを書いてみる
Normalの情報があればいろいろできます。
今回は適当にRimlightの計算を行います。

URPなのでSprite-Lit-DefaultのShaderをコピペし、それを拡張します。

また、Rimlightの計算方法はこれを使います。
```C
inline float CalcRimlight(float3 V, float3 N, float width, float intensity)
{
    float rim = pow(saturate(1. - dot(V, N) + width), intensity);
    rim = saturate(rim);
    return rim;
}
```

"LightMode" = "Universal2D"のPassの中に
```C
#include "Packages/com.unity.render-pipelines.universal/Shaders/2D/Include/NormalsRenderingShared.hlsl"
```
を追加
StructにいくつかNormal用の変数が足りないので追加を行い、
```C
half3   normalWS        : TEXCOORD3;
half3   tangentWS       : TEXCOORD4;
half3   bitangentWS     : TEXCOORD5;
```

`Varyings CombinedShapeLightVertex(Attributes v)`の中にNormal計算に必要なものが足りなくなるので追加
```C
o.normalWS = -GetViewForwardDir();
o.tangentWS = TransformObjectToWorldDir(v.tangent.xyz);
o.bitangentWS = cross(o.normalWS, o.tangentWS) * v.tangent.w;
```

`CombinedShapeLightFragment`の中に
```C
half3 normal = NormalsRenderingShared(main, normalTS, i.tangentWS.xyz, i.bitangentWS.xyz, i.normalWS.xyz);

half rim = 1.0 - CalcRimlight(float3(0.0, 0.0, 1.0), normal, _Width, _Intensity);

// 適当に赤
color.rgb += rim * half3(1.0, 0.0, 0.0);
```
を追加
これを行うことでNormalを利用してRimlightの計算ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608797/794435ad-ce44-d9ed-00b2-40a7220681cd.png)
こんな感じ。オーラっぽい

# おわりに
ノーマルさえ用意できればあとは3Dでの処理とほぼ同じ書き方でいろいろ作れます。
NormalMapを用意するのがソフトを使おうとすると有料ですが簡単にベベルがかけれるのでお勧めです。

Shaderのソースコードはかなり雑ですがここにあります。
https://github.com/ayaha401/Sprite-Rimlight/blob/master/Sprite.shader