Unity のシェーダー開発方法のまとめと備忘録・頂点アニメーションテクスチャ（VAT）シェーダー

![](https://dl.dropbox.com/s/1q0e71hasapigzo/VATKit_HeroWithText.png)


Unity のみで完結できる頂点アニメーションテクスチャー（VAT・Vertex Animation Texture）のエクスポーターとシェーダーを作りました。


<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Vertex Animation Texture shader, <a href="https://twitter.com/hashtag/unity3d?src=hash&amp;ref_src=twsrc%5Etfw">#unity3d</a>.<br><br>202 frames@30fps animation, 4,472 tris each mesh.<br><br>--<br><br>GeForce GTX 1080 on Unity Editor<br><br>90fps＝1,500mesh<br>60fps＝3,000mesh<a href="https://twitter.com/hashtag/madewithunity?src=hash&amp;ref_src=twsrc%5Etfw">#madewithunity</a> <a href="https://twitter.com/hashtag/unity?src=hash&amp;ref_src=twsrc%5Etfw">#unity</a> <a href="https://t.co/ctxG7GT4tg">pic.twitter.com/ctxG7GT4tg</a></p>&mdash; サトー (@sator_imaging) <a href="https://twitter.com/sator_imaging/status/1209306860739674114?ref_src=twsrc%5Etfw">December 24, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



<!--
https://qiita.com/sator_imaging/items/25fd2ed3a9d12486a95d
-->



--

こちらの記事はとりあえず動くシェーダーは作れる、だけど… という内容です。

拾い物やネットからのコピペ、アセットストア経由など、プロジェクトは雑多なアセットのチャンプルーが常で、たまーにシェーダーを作ったり、という方や、プロジェクトの規模に合うであろう内容になっています。




### もくじ


- [はじめに](#はじめに)
    - [目標](#目標)
        - [対応させたい Unity 標準機能の一覧](#対応させたい-unity-標準機能の一覧)
        - [Universal Render Pipeline（旧LWRP）について](#universal-render-pipeline旧lwrpについて)
        - [Shader Graph について](#shader-graph-について)
- [Unity の標準機能への対応方法](#unity-の標準機能への対応方法)
    - [Global Illumination への対応](#global-illumination-への対応)
        - [Light Probe（球面調和）の影響を受けるようにする](#light-probe球面調和の影響を受けるようにする)
        - [Reflection Probe に映るようにする](#reflection-probe-に映るようにする)
        - [Reflection Probe や天球・スカイボックスが映り込むようにする](#reflection-probe-や天球スカイボックスが映り込むようにする)
        - [ライトマップのベイクに対応する](#ライトマップのベイクに対応する)
        - [Unity のライトマップをシェーダーに適用する](#unity-のライトマップをシェーダーに適用する)
    - [影を受ける](#影を受ける)
        - [Unity のライティング情報を取得する](#unity-のライティング情報を取得する)
    - [影を落とす](#影を落とす)
        - [頂点シェーダー等で変形させている場合の注意点](#頂点シェーダー等で変形させている場合の注意点)
        - [FallBack 法](#fallback-法)
        - [UnityStandardShadow.cginc 法](#unitystandardshadowcginc-法)
        - [全自前実装法](#全自前実装法)
    - [ステレオレンダリングへの対応](#ステレオレンダリングへの対応)
    - [インスタンス及び関連技術への対応（GPU Instancing）](#インスタンス及び関連技術への対応gpu-instancing)
        - [Material Property Block への対応](#material-property-block-への対応)
        - [Single pass instanced モードのステレオレンダリング](#single-pass-instanced-モードのステレオレンダリング)
    - [フォグへの対応](#フォグへの対応)
        - [フォグ対応の注意点](#フォグ対応の注意点)
- [プラットフォームごとの違いを吸収する方法](#プラットフォームごとの違いを吸収する方法)
- [ShaderLab の構造・文法の備忘録](#shaderlab-の構造文法の備忘録)
    - [自作シェーダー作成の際の注意点](#自作シェーダー作成の際の注意点)
    - [シェーダーのテンプレート](#シェーダーのテンプレート)
    - [構造体やシェーダープログラムのおススメと注意点](#構造体やシェーダープログラムのおススメと注意点)
- [Properties](#properties)
    - [プロパティーの命名に迷った場合](#プロパティーの命名に迷った場合)
    - [マテリアルインスペクターのふるまいを調整](#マテリアルインスペクターのふるまいを調整)
        - [CustomEditor を指定して完全にカスタマイズする](#customeditor-を指定して完全にカスタマイズする)
    - [Cull や ZTest 等に Inspector で設定したプロパティーの値を使う](#cull-や-ztest-等に-inspector-で設定したプロパティーの値を使う)
        - [Enum がある場合はプロパティーに設定しておく](#enum-がある場合はプロパティーに設定しておく)
    - [プロパティーにシェーダーからアクセスする](#プロパティーにシェーダーからアクセスする)
    - [テクスチャ解像度や時間などの特殊なプロパティー](#テクスチャ解像度や時間などの特殊なプロパティー)
- [SubShader と Pass](#subshader-と-pass)
    - [アルファチャンネルを使用したカットアウト（Cutout）処理](#アルファチャンネルを使用したカットアウトcutout処理)
    - [パフォーマンスに応じてシェーダーを切り替える LOD 処理](#パフォーマンスに応じてシェーダーを切り替える-lod-処理)
    - [SubShader の Tags](#subshader-の-tags)
    - [SubShader の Pass](#subshader-の-pass)
- [CGPROGRAM ~ ENDCG](#cgprogram--endcg)
    - [CGINCLUDE ~ ENDCG](#cginclude--endcg)
    - [シェーダーモデルのターゲットレベル](#シェーダーモデルのターゲットレベル)
    - [multi_compile vs. shader_feature](#multi_compile-vs-shader_feature)
    - [if/else の使用は避ける](#ifelse-の使用は避ける)
    - [TEXCOORD が不足した場合](#texcoord-が不足した場合)
    - [頂点シェーダーの注意点](#頂点シェーダーの注意点)
    - [フラグメントシェーダーの注意点](#フラグメントシェーダーの注意点)
- [おわりに](#おわりに)
    - [おまけ](#おまけ)





## はじめに


Project ウインドウから Unlit Shader を作り、それをベースに自作シェーダーを作ると、影を落とさない＆受けない、Global Illumination の各種プローブ、ライトマップの適用やベイクにも反応しないシェーダーが出来上がります。


加えて、Unity のバージョンアップによって古くなった情報がいまでも検索に引っかかる為、専業ではない人間ではなかなか新しい情報に巡り合えず、開発に無駄な時間がかかってしまうのが Unity シェーダー開発のあるあるかと思います。

この記事は、VAT シェーダーを開発する過程で調べた、Unity の各標準機能に対応したシェーダーを開発する為に必要な情報と ShaderLab の備忘録をまとめたものです。


こんな方におススメ：

- バージョンは Unity 2018 LTS
- なんだかんだで Built-in Render Pipeline (Legacy) を使っている
- グラス型AR・VR やモバイル等で Forward Rendering を使っている





### 目標

前述の通り、Unity の標準機能に一通り対応させることです。特定のプロジェクトだけではなく汎用的に使うことができ、アセットストアへの出品も可能なものを目指します。


#### 対応させたい Unity 標準機能の一覧

- GI やライトマップ、各種プローブへの対応。
    - 天球やスカイボックス、Reflection Probe が反射に映り込む。
    - Light Probe がある環境で影響を受ける。
    - ライトマップが適用される。
    - ライトマップのベイク時に周辺環境に影響を与える。
    - Reflection Probe のレンダリングに映る。
- 影を落とす＆受ける。
- GPU Instancing と関連技術に対応している。
- グラス型AR・VR でのステレオレンダリングで、Single Pass が使える。
    - Single Pass（Instanced) にも対応している。
    ※ 全てのアセットに目が行き届く開発環境でないと使ったことが無い人が多いのでは・・・？
- Lighting 設定のフォグの影響を受ける。



#### Universal Render Pipeline（旧LWRP）について

先日ベータ版から抜けて正式リリースとなった Universal Render Pipeline。
ここではシェーダーの基本的なところは抑えていますが、Built-in 向けのシェーダーを URP に対応させるにはどうしたらいいか、については扱っていません。


ステレオレンダリング＆フォワードレンダリングやモバイルに関わることが多いという事もありますが、冒頭で述べたように、自身の管理外のアセットの対応状況に左右されることなどを考えると、中小規模のプロジェクトではまだ時期尚早かなと思っています。

以下の表を見て、うっ、となったというのもあります。

- [Feature comparison table | Universal RP | 7.1.7](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@7.1/manual/universalrp-builtin-feature-comparison.html)





#### Shader Graph について

Maya のような DCC ツールもノードベースで見栄えをいじりますが、最後の最後で結局、欲しい情報を取れるノードが無くて望むものが作り切れない、なんて事も多いので、コーディングせずに Unity Shader Graph で、、、も扱っていません。

個人的には、ノードベースでは作りきれない、となった時に Shader Graph から ShaderLab のコードを書き出して自分で実装する、という選択もあると思うので、Unity でのシェーダー開発の方法は理解しておいて損はないと思います。









## Unity の標準機能への対応方法

まずは Unity 標準機能への対応方法の紹介です。

ShaderLab の基本的なところは、備忘録として次の章にまとめています。





### Global Illumination への対応


#### Light Probe（球面調和）の影響を受けるようにする


ライトプローブに対応するには、`UnityCG.cginc` の `ShadeSH9` を使用します。このマクロは `Pass` のタグ設定で `LightMode` を `ForwardBase` に設定した状態で使用する必要があります。



```cpp
#include "UnityCG.cginc"
...
Tags
{
    "LightMode" = "ForwardBase"
}
```



##### 球面調和は可能な限り頂点シェーダーで処理する


球面調和（Spherical Harmonics）はけして軽い処理ではないので、頂点シェーダーで十分な結果を得られる場合はピクセルシェーダーで処理をしない方が良いです。

以下のサンプル画像を見てわかるように、Light Probe と Lighting > Environment Lighting の影響を計算するだけなので、ノーマルマッピング等を行わない限りにおいてはクッキリはっきりとした変化のある絵は出ないからです。


※ ロボットの下手側の肩のライティング結果に、わずかながら違いが見受けられます。

<p><img src="https://dl.dropbox.com/s/r2k3lak58og3nba/VATKit_ShadeSH9.png" /></p>



例（頂点シェーダー）：

```cpp
o.color.rgb *= ShadeSH9( half4(UnityObjectToWorldNormal(v.normal), 1) );
```

※ 頂点シェーダーで設定したカラーはフラグメントシェーダーに渡される際に、GPU で頂点間を線形補完された状態になります。


例（ピクセルシェーダー）：

```cpp
col.rgb *= ShadeSH9( half4(UnityObjectToWorldNormal(i.normal), 1) );
```




#### Reflection Probe に映るようにする

カスタムシェーダー側で何もしなくても Reflection Probe に映り込み、頂点シェーダーの変形結果も反映されます。もしも映らない場合は Reflection Probe Static の設定を忘れていないかを確認します。

※ VAT Kit ではシェーダー内で `_Time` を使用してアニメーションさせていますが、ベイクする度に映り込む位置が１フレームずつ進んでしまいました。Unity & ShaderLab ではベイク用のパスなどは用意されていないので、必要に応じて対策を施す必要があります。

<p><img src="https://dl.dropbox.com/s/zt5z9ll9zpmf83o/VATKit_ReflectionProbe.PNG" /></p>





#### Reflection Probe や天球・スカイボックスが映り込むようにする

Reflection Probe 及び Lighting > Environment で設定された天球・スカイボックスの情報の取得方法は、こちらの公式ドキュメントに載っています。

- [Unity - Manual: Vertex and fragment shader examples](https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html)


ただし、上記の公式ドキュメントの実装だと、なぜか複数の Reflection Probe を跨いだ際のブレンドには対応していません。



##### Reflection Probe のブレンドに対応する

ブレンディングに対応するにはフラグメントシェーダーで追加の処理が必要になります。


※ Reflection Probe のブレンドに対応した例
<p><img src="https://dl.dropbox.com/s/d0cs8loismic2kn/VATKit_ReflectionProbeBlending.gif" /></p>



リフレクションの計算には、ワールド空間の法線と視線ベクトルが必要になります。

ノーマルマッピング等を行わない場合は、頂点シェーダーでリフレクションの法線を事前に計算しておき、映り込みのサンプリングのみフラグメントシェーダーで行います。


※ 視線ベクトルの計算に必要な頂点のワールド座標は、頂点シェーダーで計算した場合も GPU で線形補完されるので精度は変わらない（ハズ）なので、ノーマルマッピングをする時は頂点の座標のみ頂点シェーダーで計算しておくのも良いかもしれません。



###### Reflection Probe 対応のサンプルコード

各手法を切り替えられるようにコメントとして残したサンプルです。

```cpp
struct v2f
{
    ...
    half3 worldRefl : TEXCOORD2;
    half3 worldPos : TEXCOORD3; // not required if worldRefl is calculated in vertex shader
    ...
};
```

※ `worldRefl` を頂点シェーダーで計算した場合は、`worldPos` をフラグメントシェーダーに渡す必要はなくなります。`v2f` から消して、頂点シェーダーの `o.worldPos` を `half3 worldPos` に変更します。

頂点シェーダー

```cpp
v2f vert(appdata v)
{
    ...
    o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    float3 worldViewDir = normalize( UnityWorldSpaceViewDir(o.worldPos) );
    float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    o.worldRefl = reflect(-worldViewDir, worldNormal);
    ...
}
```


フラグメントシェーダー
※ 映り込みのボケはミップマップの LOD レベルを調整することで実現します。

```cpp
fixed4 frag(v2f i) : SV_Target
{
    ...
    half3 worldRefl;
    ///////half3 worldPos;
    half mipmap_lod_level = 0;

    // blurry reflection
    ////////mipmap_lod_level = roughness * UNITY_SPECCUBE_LOD_STEPS;

    // worldspace data from vertex shader
    worldRefl = i.worldRefl;
    ///////worldPos = i.worldPos;

    // calculate worlspace data in fragment shader
    ////////worldPos = mul(unity_ObjectToWorld, i.pos).xyz;
    ////////worldRefl = reflect( -normalize(UnityWorldSpaceViewDir(worldPos)), UnityObjectToWorldNormal(i.normal) );

    // reflection probe blending
    half4 skyData0 = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, worldRefl, mipmap_lod_level);
    half4 skyData1 = UNITY_SAMPLE_TEXCUBE_SAMPLER_LOD(unity_SpecCube1, unity_SpecCube0, worldRefl, mipmap_lod_level);
    half3 reflColor0 = DecodeHDR(skyData0, unity_SpecCube0_HDR);
    half3 reflColor1 = DecodeHDR(skyData1, unity_SpecCube1_HDR);

    // resulting reflection color
    half3 reflColor = lerp(reflColor1, reflColor0, unity_SpecCube0_BoxMin.w);
    ...
}
```



リフレクションプローブのブレンディングはこちらを参考にさせて頂きました。
（Unity 2018 LTS よりバージョンが古い Unity についての記事です）

- [【Unity】【シェーダ】Reflection Probeの影響を受けるシェーダを書く](http://light11.hatenadiary.com/entry/2018/07/08/212014)






#### ライトマップのベイクに対応する

ライトマップのベイク時に周辺環境に影響を与えるようにする方法は、以下の通りです。



##### 自作の自己発光マテリアルを使ってベイクした結果

まずは結果です。中央の黒い球体が、ライトマップのベイクの際に使われる Meta パスのみを実装したカスタムシェーダーで、周辺環境に対して光を放って影響を与えています。

<p><img src="https://dl.dropbox.com/s/z4722fto2dcs5oz/VATKit_EmissiveMaterial.PNG" /></p>





##### 自己発光させて周囲を照らしたい時の注意点

上図のように自己発光しているマテリアルで周囲を照らしたい場合、シェーダーの実装だけでは実現できません。


以下の通り `Material.globalIlluminationFlags` を適切に設定しておく必要があります。
※ これは自己発光（Emission）が周辺環境に影響を与える時のみに必要になる設定で、Albedo や Specular のカラーブリーディングは設定をしていなくても反映されます。



（これは分かりませんよ）

> Yes. The thing is the color used by the baked lighting doesn't come from the usual vertex/fragment passes people are familiar with. Those come from a special "Meta" pass. See the Illumin-VertexLit.shader from the built in shader source for a basic example. However for realtime emission there's one more step that needs to be done. There's a flag that needs to be set on the material from script. Usually this is done with a custom material editor. It can be manually set via material.globalIlluminationFlags or automatically set with MaterialEditor.FixupEmissiveFlag().
> https://forum.unity.com/threads/emmisive.492609/#post-3206559




ビルトインのスタンダードシェーダーの `CustomEditor` として使われている `StandardShaderGUI.cs` では、以下のような処理をしています。

```csharp
// A material's GI flag internally keeps track of whether emission is enabled at all, it's enabled but has no effect
// or is enabled and may be modified at runtime. This state depends on the values of the current flag and emissive color.
// The fixup routine makes sure that the material is in the correct state if/when changes are made to the mode or color.
MaterialEditor.FixupEmissiveFlag(material);
bool shouldEmissionBeEnabled = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled);
```





###### MaterialGlobalIlluminationFlags を設定するスクリプト

エディターから設定する場合は、メッシュに対して以下のようなコンポーネントを適用します。設定が済めばアセット自体が更新されるので、剥がしても大丈夫（なハズ）です。


```csharp
using UnityEngine;

public class GlobalIlluminationFlags : MonoBehaviour
{
    public MaterialGlobalIlluminationFlags flag = MaterialGlobalIlluminationFlags.AnyEmissive;

    void OnValidate()
    {
        var rend = GetComponent<Renderer>();
        if (rend && rend.sharedMaterial)
        {
            rend.sharedMaterial.globalIlluminationFlags = flag;
        }
    }

}
```



##### ライトマップのベイク時に使用する "LightMode"="Meta" パスの実装


GI のベイクの際に使われる Meta パスは、`UnityMetaPass.cginc` で定義されている構造体に必要な値を設定して返してあげれば良いだけです。

```cpp
struct UnityMetaInput
{
    half3 Albedo;
    half3 Emission;
    half3 SpecularColor;
};
```


###### Meta パス実装のサンプルコード


Unity built-in Shader の Standard Shader の Meta パスをベースに、フラグメントシェーダーのみカスタマイズして実装するのが最も簡単な手法です。



※ 強い光を設定する事が多い自己発光カラーのプロパティーは、`[HDR]` を付けておくと Inspector 上でスーパーホワイトの設定が出来るようになるので便利です。


```cpp
[HDR] _EmissionColor("Emission Color", Color) = (0, 0, 0, 1)
```




サンプルの Meta パスの実装は以下の通りです。

※ `#pragma fragment frag_meta_custom` 等に名前を書き換えて自作シェーダーを実装します。
※ `UnityMetaInput` は `UnityMetaFragment` を通して返します。
※ `_EmissionColor` など、`UnityStandardInput.cginc` で定義済みのプロパティー変数の定義は不要です。（再定義するなとエラーが出ます）



```cpp
Pass
{
    Name "META"
    Tags { "LightMode" = "Meta" }

    Cull Off

    CGPROGRAM
    #include "UnityStandardMeta.cginc"

    #pragma vertex vert_meta // change name and implement if customizing vertex shader
    #pragma fragment frag_meta_custom // changed to customized fragment shader name

    #pragma shader_feature _EMISSION
    #pragma shader_feature _METALLICGLOSSMAP
    #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
    #pragma shader_feature ___ _DETAIL_MULX2
    #pragma shader_feature EDITOR_VISUALIZATION

    // pre-defined in UnityStandardInput.cginc from UnityStandardMeta.cginc
    //half _Glossiness;
    //half4 _Color;
    //half4 _EmissionColor;
    half3 _SpecularColor;

    // customized fragment shader
    float4 frag_meta_custom(v2f_meta i) : SV_Target
    {
        UnityMetaInput o;
        UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o);
        
        // required but no effect for EnergyConservationBetweenDiffuseAndSpecular
        half oneMinusReflectivity = 0;

        // do it
        half smoothness = _Glossiness;
        half3 albedo = _Color;
        half3 specularColor = _SpecularColor;
        half3 emissionColor = _EmissionColor;

        // assign result to output
        albedo = EnergyConservationBetweenDiffuseAndSpecular(albedo, specularColor, oneMinusReflectivity);
        #if defined(EDITOR_VISUALIZATION)
            o.Albedo = albedo;
        #else
            o.Albedo = UnityLightmappingAlbedo(albedo, specularColor, smoothness);
        #endif
        o.SpecularColor = specularColor;
        o.Emission = emissionColor;

        return UnityMetaFragment(o);
    }

    ENDCG
}
```




##### Diffuse と Specular と Smoothness の関係性


Unity の Standard シェーダーの Meta パスでは、Diffuse と Specular と Smoothness に基づいて重み付けを行う Unity のマクロが２つ使用されていて、その状態でベイクを行うと以下のような結果になります。
（当然ですが、カラーブリーディングに合わせて自身の見た目を実装する必要があります）


※ 表面が滑らかだと Diffuse（赤）のみが拡散し、ザラついていると Specular（青）が混ざる。
<p><img src="https://dl.dropbox.com/s/eam61eryk8805ug/VATKit_SmoothnessAffectsGI.png" /></p>



表面が滑らかだとスペキュラー成分が拡散しないのではなく、本来であれば指向性の強い反射が起こるハズですが、2018 LTS ではライトマッパーがその現象に対応していません。（Enlighten、Progressive CPU / GPU ともに）

※ 表面が滑らかな曲面を使って、指向性の強い反射光を集めたレンダリング例
<p><img src="https://dl.dropbox.com/s/k89ijecn90dsow8/VATKit_mentalrayA.png" /></p>



<!--
※ 指輪のサンプルが示されることが多いこの効果ですが、場合によっては Smoothness に関わらず光を反射させてしまう Meta パスを実装をするのも手かと思います。
<p><img src="https://dl.dropbox.com/s/9eg67gidoomiuad/VATKit_mentalrayRings.png" /></p>
-->



###### Standard シェーダーの Meta パスで使用されているマクロ関数


Standard シェーダーの Meta パスで使用されている Unity ビルトインのマクロ関数は、エネルギー保存の法則にしたがって Albedo の調整を行うものと、ライトマッパーに合わせてさらに Albedo の調整を行うものの２つで、以下の通り定義されています。


※ `SpecularStrength` は `specColor` の `.rgb` の３つのうち最大の値を返します。
※ `UnityStandardCore.cginc` の `UNITY_SETUP_BRDF_INPUT` = `SpecularSetup` 内で一度 diffuse の調整を行い、焼きこむ際には次に示す `UnityLightmappingAlbedo` マクロ関数でさらにライトマッパー向けの調整を行っているようです。

```cpp
// Diffuse/Spec Energy conservation
inline half3 EnergyConservationBetweenDiffuseAndSpecular (
	half3 albedo, half3 specColor, out half oneMinusReflectivity
) {
	oneMinusReflectivity = 1 - SpecularStrength(specColor);
	#if !UNITY_CONSERVE_ENERGY
		return albedo;
	#elif UNITY_CONSERVE_ENERGY_MONOCHROME
		return albedo * oneMinusReflectivity;
	#else
		return albedo * (half3(1, 1, 1) - specColor);
	#endif
}
```


※ Unity はエディタープレビュー時には上記の調整のみを行った Albedo を表示し、焼きこむ時のみ以下のマクロ関数で smoothness に基づいたライトマッパー向けの調整を加え、その結果を Albedo に設定しているようです。
※ プレビューか否かの処理はマクロ内ではなく Meta パス内に直接記述されているので、将来のアップデートに伴ってコード修正が必要になりそうな気がしています。

```cpp
half3 UnityLightmappingAlbedo (half3 diffuse, half3 specular, half smoothness)
{
    half roughness = SmoothnessToRoughness(smoothness);
    half3 res = diffuse;
    res += specular * roughness * 0.5;
    return res;
}
```







##### 公式ドキュメントの Global Illumination Meta パスの実装例

公式ドキュメントにも、自己発光に対応していない実装サンプルがあります。

- [Unity - Manual: Material properties and the GI system](https://docs.unity3d.com/Manual/MetaPass.html#ExampleMetaPass)







#### Unity のライトマップをシェーダーに適用する


※ Unity でベイクしたライトマップをカスタムシェーダーに適用した状態。

<p><img src="https://dl.dropbox.com/s/uuatn7hg710n4my/VATKit_Lightmapping.png" /></p>



Unity がベイクしたライトマップをシェーダーに適用する方法は、以下の通りです。


※ モデルインポーターの「Generate Lightmap UVs」の設定に関わらず、`TEXCOORD1` にはライトマップ用の UV が入っています。

```cpp
struct appdata
{
    ...
    float2 texcoord1 : TEXCOORD1;
    ...
};
```


```cpp
struct v2f
{
    ...
    float2 texcoord1 : TEXCOORD1;
    ...
};
```


※ Unity のライトマップは複数のオブジェクトを一つのテクスチャアトラスにまとめた状態になっているので、ライトマップ内の指定の位置に UV をスケール＆移動します。

```cpp
v2f vert(appdata v)
{
    ...
    o.texcoord1 = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    ...
}
```

```cpp
fixed4 frag(v2f i) : SV_Target
{
    ...
    col.rgb *= DecodeLightmap( tex2D(unity_Lightmap, i.texcoord1.xy) );
    ...
}
```



こちらの公式ドキュメントにも実装例があります。

- [Unity - Manual: Rendering Optimizations](https://docs.unity3d.com/Manual/MobileOptimizationPracticalRenderingOptimizations.html)




### 影を受ける


影の情報をシェーダーで得る方法は、こちらの記事に非常に分かりやすくまとまっています。


- [Unityでマリオオデッセイの雲海を再現してみた #シャドウがかかっている - Qiita](https://qiita.com/kaiware007/items/bca3b228704f68a58d68#%E3%82%B7%E3%83%A3%E3%83%89%E3%82%A6%E3%81%8C%E3%81%8B%E3%81%8B%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B)




#### Unity のライティング情報を取得する


影を受ける以外にも、`LIGHT_ATTENUATION` マクロで Unity が提供するライティングの情報も取得できます。こちらの記事が非常に参考になります。


- [[Unity] AssetStoreのファーシェーダをupdateしたので分かったことを書いてみる #unityで準備されている機能でシャドウを実装する - Qiita](https://qiita.com/edo_m18/items/21d3b37596da3fd4b32b#unity%E3%81%A7%E6%BA%96%E5%82%99%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E6%A9%9F%E8%83%BD%E3%81%A7%E3%82%B7%E3%83%A3%E3%83%89%E3%82%A6%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B)






### 影を落とす


カスタムシェーダーを割り当てたオブジェクトの影を落とす一番手っ取り早い方法は、後述の FallBack 法です。



#### 頂点シェーダー等で変形させている場合の注意点

頂点シェーダーやジオメトリシェーダーによる変形がある場合は、変形処理を `ShadowCaster` パスでも同様に適用する必要があります。

その為、以下の FallBack 法や UnityStandardShadow.cginc 法は使用できません。


※ 変形した場所ではなく、オレンジで囲われた本来の位置を元に影が落ちてしまっている。
<p><img src="https://dl.dropbox.com/s/604oif95evu8gh7/VATKit_WrongShadowing.PNG" /></p>


`ShadowCaster` パスでも同様の変形を行う必要があるので、`VATKit_vertShader.cginc` 等のように頂点シェーダーを外部ファイル化して `#include` する等、重複した処理を書かずに済む方法も開発の早い段階から検討しておかないと、後々の切り分け作業で大変なことになりそうです。


参考：

- [Vertex and Fragment shader with vertex displacement *and* shadows - Unity Answers](https://answers.unity.com/questions/1147625/vertex-and-fragment-shader-with-vertex-displacemen.html)




#### FallBack 法

上記のシェーダーテンプレートにも入っていますが、

- `FallBack "Mobile/VertexLit"`

として処理を任せてしまうのが、将来的な保守も不要で一番です。

※ `ShadowCaster` パスが存在するとフォールバックが起きないので、パスが存在しない状態にしておきます。




#### UnityStandardShadow.cginc 法

こちらは Standard Shader の ShadowCaster パスをそのまま使う方法です。

将来的なアップデートで動かなくなる可能性も無くはないので、FallBack を本来の用途以外で使うのはためらわれる、という場合以外は `FallBack "Mobile/VertexLit"` を使う方が良いと思います。


参考：

- [StandardShaderを編集して、裏面も描画されるようにする](http://onoty3d.hatenablog.com/entry/2015/06/19/131205)


<!--
抜粋

```cpp
// ------------------------------------------------------------------
//  Shadow rendering pass
Pass {
    Name "ShadowCaster"
    Tags { "LightMode" = "ShadowCaster" }
    
    ZWrite On ZTest LEqual

    CGPROGRAM
    #pragma target 3.0
    // TEMPORARY: GLES2.0 temporarily disabled to prevent errors spam on devices without textureCubeLodEXT
    #pragma exclude_renderers gles
    
    // -------------------------------------


    #pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
    #pragma multi_compile_shadowcaster

    #pragma vertex vertShadowCaster
    #pragma fragment fragShadowCaster

    #include "UnityStandardShadow.cginc"

    ENDCG
}
```
-->



オフィシャルサイトのアーカイブから、「ダウンロード（Win or Mac）」→「ビルトインシェーダー」を選択して最新のシェーダーコードをダウンロードすることもできます。


- [Get Unity - Download Archive - Unity](https://unity3d.com/jp/get-unity/download/archive)





#### 全自前実装法


頂点シェーダーで変形している場合など、ShadowCaster パスを自身で実装する方法は、こちらが非常に参考になります。




- [UNITYで影をつくる〜ShadowCaster](http://murasame-labo.hatenablog.com/entry/2018/09/19/160653)
    - ※ `VertexInput` を `appdata` に、`VertexOutput` を `v2f` にすると見慣れたものになります。







### ステレオレンダリングへの対応


マルチパスのステレオレンダリング（単に複数のカメラで2回描画しているだけ）にはデフォルトで対応していますが、左右の目の画像を横に並べてレンダリングする、Single pass モードのステレオレンダリングに対応する為には、頂点シェーダー等を対応させる必要があります。


ざっくり言うとスクリーン座標など、画面が左右横並びで２倍幅になると影響を受ける処理は、Unity謹製のマクロを使おう、というお話です。
（例：`mul(UNITY_MATRIX_MVP, v);` ではなく `UnityObjectToClipPos()` を使う）


- [Unity - Manual: Single Pass Stereo rendering (Double-Wide rendering)](https://docs.unity3d.com/Manual/SinglePassStereoRendering.html)


加えて、Single-pass instanced（2018 LTS ではプレビュー版） モードへの対応もあります。そちらは以下に続きます。



### インスタンス及び関連技術への対応（GPU Instancing）

シェーダーをインスタンス対応にすると、マテリアルのインスペクターに「Enable GPU Instancing」というチェックボックスが表示されるようになります。

逆を言えば、Enable GPU Instancing が無いものはインスタンスに未対応となり、インスタンスだけではなく以下の関連技術の全てが無効であるという事になります。


まず最初に、インスタンスへの対応方法はこちらです。

- [Unity - Manual: GPU instancing](https://docs.unity3d.com/Manual/GPUInstancing.html)




#### Material Property Block への対応

シェーダーをインスタンスに対応させることで、`MaterialPropertyBlock` への対応も可能になります。

ただし、あくまでも似通ったマテリアルが大量に必要になる事態を防ぐための機能であって、インスタンスへの対応が必要だからと言って Batch 数や SetPass calls、Draw call が減ったりすることは無いようです。


※ Saved by batching: 0、Batches / SetPass calls ともに 5,404 で、減っている様子はない。
<p><img src="https://dl.dropbox.com/s/0wseesdd7ipbqxd/VATKit_MaterialPropertyBlock.png" /></p>


VAT シェーダーでは Time Offset が MaterialPropertyBlock に対応しているので、冒頭で紹介した動画のようにメッシュとマテリアルが一対でも、それぞれのアニメーションのタイミングを変えることが可能になっています。

※ 対応しても GPU Instancing のように Inspector の表示が変わるわけではないので、残念ながら外側からではどのプロパティーが対応しているのかは分かりません。




##### Material Property Block への対応方法

以下のページにある、「Adding per-instance data」セクションに対応方法の記載があります。

- [Unity - Manual: GPU instancing](https://docs.unity3d.com/Manual/GPUInstancing.html)




##### Material Property Block の使用方法

Material Property Block を使ったプロパティーの設定方法は、既存のものを取得 → 無ければ作成＆セット → レンダラーへ適用の流れです。

- [Unity - Scripting API: Renderer.GetPropertyBlock](https://docs.unity3d.com/ScriptReference/Renderer.GetPropertyBlock.html)

- [Unity - Scripting API: MaterialPropertyBlock.SetColor](https://docs.unity3d.com/ScriptReference/MaterialPropertyBlock.SetColor.html)

- [Unity - Scripting API: Renderer.SetPropertyBlock](https://docs.unity3d.com/ScriptReference/Renderer.SetPropertyBlock.html)







#### Single pass instanced モードのステレオレンダリング

Single pass Instanced モードへの対応方法の詳細はこちらです。

- [Unity - Manual: Single Pass Instanced rendering](https://docs.unity3d.com/Manual/SinglePassInstancing.html)






### フォグへの対応

Project ウインドウから作成できる、Unlit Shader のテンプレートで既に対応がなされていますが、

1. `#pragma multi_compile_fog` を追加
2. `struct v2f` に `UNITY_FOG_COORDS(1);`（1 は TEXCOORD# の # 部分）
3. `vert()` に `UNITY_TRANSFER_FOG(o, o.vertex);`
4. `frag()` に `UNITY_APPLY_FOG(i.fogCoord, apply_to_this_fixed4_color);`

で対応させることができます。こちらの Unity Answers の記事が詳しいです。

- [shaders : what is the use of UNITY_FOG_COORDS, UNITY_TRANSFER_FOG, UNITY_APPLY_FOG](https://answers.unity.com/questions/981538/shaders-what-is-the-use-of-unity-fog-coords-unity.html)




#### フォグ対応の注意点

フォグのマクロは `LightMode` が `ForwardAdd` に設定されている場合は、なにもしてくれません。`ForwardBase` もしくは `LightMode` 無しのパスでのみ動作します。


`ForwardAdd` を単純に実装して複数のライトで照らした場合は、2つ目以降のライトがフォグを無視することになるので、少しおかしな結果になった気がします（うろ覚えですが…）この辺りはフォワードライティングという手法の限界かもしれませんね。

※ `i.fogCoord` の情報が ForwardAdd でも取得できるようなら、それを元にライティング側が配慮すれば対応は出来そうです。






## プラットフォームごとの違いを吸収する方法

OpenGL や DirectX など、プラットフォームごとに UV 座標やデプスバッファの扱いなどに違いがあります。

何もしなくても Unity が吸収してくれる場合や、そうではない場合があります。詳細や対応方法は以下のページから確認できます。

- [Unity - Manual: Platform-specific rendering differences](https://docs.unity3d.com/Manual/SL-PlatformDifferences.html)



※ 抜粋：クリップ後の座標に違いがある。

> - Direct3D-like: The clip space depth goes from +1.0 at the near plane to 0.0 at the far plane. This applies to Direct3D, Metal and consoles.
> - OpenGL-like: The clip space depth goes from –1.0 at the near plane to +1.0 at the far plane. This applies to OpenGL and OpenGL ES.


※ 抜粋：デプスが黒→白ではなく、白→黒のプラットフォームがある。

> - Use SystemInfo.usesReversedZBuffer to find out if you are on a platform using reversed depth (Z).



※ 抜粋：UV 座標系の違いに対応する方法。

```cpp
// Flip sampling of the Texture: 
// The main Texture
// texel size will have negative Y).

#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
    uv.y = 1 - uv.y;
#endif
```


```cpp
v2f vert(appdata v)
{
    ...
    o.uv = v.uv;
    // This example is rendering with upside-down flipped projection,
    // so flip the vertical UV coordinate too
    if (_ProjectionParams.x < 0)
    {
        o.uv.y = 1 - o.uv.y;
    }
    ...
}
```


`UNITY_UV_STARTS_AT_TOP` や `_ProjectionParams` については、こちらに詳細があります。


- [Unity - Manual: Predefined Shader preprocessor macros](https://docs.unity3d.com/Manual/SL-BuiltinMacros.html)

- [Unity - Manual: Built-in shader variables](https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html)







## ShaderLab の構造・文法の備忘録

ここからは、シェーダーの中身の構造・文法についてです。



### 自作シェーダー作成の際の注意点

基本的にサーフェイスシェーダーは使わない方が良いと思います。ある程度まではパッと作れますが、後々制限に悩まされます。

特に、サーフェイスシェーダーの情報を扱っている人が少ないという点がネックです。


※ 以下のようなコードが載っている場合はサーフェイスシェーダーの情報です。

- #pragma surface surf Lambert
- void surf(Input IN, inout SurfaceOutput o)
- SurfaceOutputStandard
- uv_MainTex



### シェーダーのテンプレート

シェーダー開発のテンプレートとして、リファレンスの URL をコメントとして記載した物を作ってみました。


```cpp
Shader "Path/To/The/Shader"
{
    // https://docs.unity3d.com/Manual/SL-Properties.html
    // https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html
    Properties
    {
    }

    // https://docs.unity3d.com/Manual/SL-SubShader.html
    SubShader
    {
        // https://docs.unity3d.com/Manual/SL-SubShaderTags.html
        Tags
        {
            "Queue" = "Geometry"
            "RenderType" = "Opaque"
        }

        // https://docs.unity3d.com/Manual/SL-Pass.html
        // Cull, ZTest, etc...


        CGINCLUDE
        #include "UnityCG.cginc"
        #include "Lighting.cginc"
        #include "AutoLight.cginc"
        #pragma target 3.0
        ENDCG


        // https://docs.unity3d.com/Manual/SL-Pass.html
        Pass
        {
            // https://docs.unity3d.com/Manual/SL-PassTags.html
            Tags
            {
            }

            // https://docs.unity3d.com/Manual/SL-Pass.html
            // Cull, ZTest, etc...


            CGPROGRAM
            ENDCG

        }

    }
    FallBack "Mobile/VertexLit"
    // https://docs.unity3d.com/Manual/SL-CustomEditor.html
    //CustomEditor "ClassName"
}
```




### 構造体やシェーダープログラムのおススメと注意点


印象ベースですが、各要素の名前は下記のものがおススメです。


特に、`v2f` の `pos` は、テンプレとして使う事も多い Unlit Shader `NewUnlitShader.shader` では `vertex` になっていますが、ライティング、影関連のマクロの影響で `pos` に変更しておいた方が良いです。
（`TRANSFER_VERTEX_TO_FRAGMENT(o)` というマクロで `pos` がハードコードされています）



※ `struct` は最後にセミコロン

```cpp
struct appdata
{
    ...
};
```

```cpp
struct v2f
{
    float4 pos : SV_POSITION;
    ...
};
```

```cpp
v2f vert(appdata v)
{
    v2f o;
    ...
    return o;
}
```

```cpp
fixed4 frag(v2f i) : SV_Target
{
    ...
}
```




--



## Properties


Properties ブロックでは、シェーダーに必要なテクスチャやその他のプロパティーを設定します。
※ シェーダープログラムからアクセスするには、後述の手続きが必要です。

- [Unity - Manual: ShaderLab: Properties](https://docs.unity3d.com/Manual/SL-Properties.html
)


例：

```cpp
_MyTexture ("My texture", 2D) = "white" {}
_MyNormalMap ("My normal map", 2D) = "bump" {}  //Grey
_MyInt ("My integer", Int) = 2
_MyFloat ("My float", Float) = 1.5
_MyRange ("My range", Range(0.0, 1.0)) = 0.5

_MyColor ("My colour", Color) = (1, 0, 0, 1)    // (R, G, B, A)
_MyVector ("My Vector4", Vector) = (0, 0, 0, 0)    // (x, y, z, w)
```


### プロパティーの命名に迷った場合

プロパティーの命名に迷ったら、`UnityStandardInput.cginc` を参考にすると良いかもしれません。

- [UnityStandardInput.cginc - TwoTailsGames/Unity-Built-in-Shaders](https://github.com/TwoTailsGames/Unity-Built-in-Shaders/blob/master/CGIncludes/UnityStandardInput.cginc)


抜粋：

```glsl
//---------------------------------------
half4       _Color;
half        _Cutoff;

sampler2D   _MainTex;
float4      _MainTex_ST;

sampler2D   _DetailAlbedoMap;
float4      _DetailAlbedoMap_ST;

sampler2D   _BumpMap;
half        _BumpScale;

sampler2D   _DetailMask;
sampler2D   _DetailNormalMap;
half        _DetailNormalMapScale;

sampler2D   _SpecGlossMap;
sampler2D   _MetallicGlossMap;
half        _Metallic;
half        _Glossiness;
half        _GlossMapScale;

sampler2D   _OcclusionMap;
half        _OcclusionStrength;

sampler2D   _ParallaxMap;
half        _Parallax;
half        _UVSec;

half4       _EmissionColor;
sampler2D   _EmissionMap;

//-------------------------------------------------------------------------------------
```


### マテリアルインスペクターのふるまいを調整


チェックボックスやスライダー付きの整数プロパティーなど、Unity のビルトインシェーダーのようなインスペクターを再現するには、以下のページにある Material Property Drawer アトリビュートを追加で設定する必要があります。

- [Unity - Scripting API: MaterialPropertyDrawer](https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html
)

- [Unity - Manual: ShaderLab: Properties](https://docs.unity3d.com/Manual/SL-Properties.html)
    - 「Property attributes and drawers」セクションに Material Property Drawer のページに記載のないアトリビュートがあります。




抜粋：

- 整数プロパティーにスライダーが欲しい場合は、`[IntRange]`
- 単に Inspector でチェックボックスを表示したいだけなら、`[MaterialToggle]`
    - チェックボックスに応じて 0 or 1 がプロパティーに設定されます。
- `[Toggle]` はキーワードを設定したい時に使用する。
    - `#pragma shader_feature` や `#pragma multi_compile` で使用します。


#### CustomEditor を指定して完全にカスタマイズする

上記のシェーダーテンプレートの、`FallBack` の並びに `CustomEditor` を追加してクラス名を指定すると、指定のエディター拡張でインスペクターの描画を完全にコントロールできるようになります。



```cpp
CustomEditor "ShaderInspectorClassName"
```

参考 ※指定のクラスを含むエディタースクリプトは `Editor` フォルダ以下に配置します。

- [Unity - Scripting API: ShaderGUI](https://docs.unity3d.com/ScriptReference/ShaderGUI.html)





### Cull や ZTest 等に Inspector で設定したプロパティーの値を使う

Cull 等にインスペクターで設定した値を渡したい場合は、`[プロパティ名]` と記述します。

例：

- `Cull [_CullPropertyName]`
- `ZTest [_ZTestPropertyName]`


プロパティーに `Enum` を設定してドロップダウンメニューから選べるようにしておくと、間違った値を設定をしてしまうことを防ぐ事が出来ます。

例：

```cpp
[Enum(UnityEngine.Rendering.CullMode)]
_Cull("Cull", Float) = 0
```

#### Enum がある場合はプロパティーに設定しておく

大抵のものは `UnityEngine.Rendering` に Enum が定義されているので、一覧から探して忘れずに設定しておきます。

- [Unity - Scripting API: CullMode](https://docs.unity3d.com/ScriptReference/Rendering.CullMode.html)



### プロパティーにシェーダーからアクセスする


シェーダーからプロパティーの値を参照するには、`CGINCLUDE` か `CGPROGRAM` 内で変数を定義します。詳細はこちらです。

- [Unity - Manual: Accessing shader properties in Cg/HLSL](https://docs.unity3d.com/Manual/SL-PropertiesInPrograms.html)


※ `uniform float4 _MyColor;` と書くことも出来ますが、`uniform` は無くても良いです。




### テクスチャ解像度や時間などの特殊なプロパティー


テクスチャとして `_MainTex` を設定すると、Inspector から UV オフセットとスケールが設定できるようになります。シェーダーからそれらの値にアクセスするには、`_ST`（Scale & Translation）を末尾に付けた `float4 _MainTex_ST` という特殊なプロパティーを使います。


同様に `float4 _MainTex_TexelSize` を定義した場合は、アサインされているテクスチャのサイズなどが取得できるようになります。


その他にも、末尾 `_HDR` や、VATシェーダーでも使っている `_Time` 等があります。詳細は以下のページが参考になります。

- [[Unity] シェーダで使える定義済値 - Qiita](https://qiita.com/edo_m18/items/591925d7fc960d843afa)

- [Unity - Manual: Accessing shader properties in Cg/HLSL](https://docs.unity3d.com/Manual/SL-PropertiesInPrograms.html)




## SubShader と Pass


SubShader はプロパティの並びに必要なブロックで、タグを通して Render Queue の設定などが可能です。


※ レンダーキューについての抜粋

> Background (1000)： 背景、スカイボックス（天球）の描画に使用。
> Geometry (2000)： 不透明オブジェクト用のキューで、描画順は手前→奥。
> AlphaTest (2450)： カットアウト（Cutout）用のキューで不透明オブジェクトとしてソートされる。
> ↑ ↑ ↑
> レンダーキューが 2,500 以下の場合は不透明オブジェクトとして手前→奥にソートされる。
> キューが 2,501 以上の場合は透明オブジェクトとして奥→手前にソートされるようになる。
> ↓ ↓ ↓
> Transparent (3000)： 透明オブジェクト用のキューで、描画順は奥→手前。
> Overlay (4000)： レンズフレアや GUI、テキストの描画などに使用。

※ `"Geometry+5"` のように相対的に指定することも可能です。
※ フラグメントシェーダーでカットアウト処理を行うには `clip()` 関数を使用します。


SubShader のその他の詳細はこちらから確認できます。


- [Unity - Manual: ShaderLab: SubShader Tags](https://docs.unity3d.com/Manual/SL-SubShaderTags.html)



### アルファチャンネルを使用したカットアウト（Cutout）処理


キューを AlphaTest にしただけではカットアウト処理は行われません。Unity 2018 LTS・フラグメントシェーダー対応のカットアウト処理については、こちらの記事が非常に参考になります。

- [Unityのアルファテストがレガシーなものになっていた件](https://www.shibuya24.info/entry/alphatest)





### パフォーマンスに応じてシェーダーを切り替える LOD 処理

シェーダーの LOD については、こちらから確認できます。

- [Unity - Manual: Shader Level of Detail](https://docs.unity3d.com/Manual/SL-ShaderLOD.html)




### SubShader の Tags

SubShader の `Tags` の詳細はこちらから確認できます。

- [Unity - Manual: ShaderLab: SubShader Tags](https://docs.unity3d.com/Manual/SL-SubShaderTags.html)




### SubShader の Pass

SubShader には複数の `Pass` ブロックを持つ事ができ、影描画やライトマップのベイク時など、場合によって処理を変える事が出来ます。（パスが無い SubShader のみの実装でも動作します）

Pass と SubShader は記述内容はほぼ同様ですが、Tags の内容が若干違います。

- [Unity - Manual: ShaderLab: Pass Tags](https://docs.unity3d.com/Manual/SL-PassTags.html)



タグの後には SubShader と同様に `Cull` 等の設定を必要に応じて記述します。

- [Unity - Manual: ShaderLab: Pass](https://docs.unity3d.com/Manual/SL-Pass.html)





## CGPROGRAM ~ ENDCG

SubShader や Pass ブロック内に記述する、シェーダー本体の実装部分です。頂点シェーダーやピクセルシェーダーは CGPROGRAM ~ ENDCG の間に記述します。

Unity エンジンから取得できるデータの一覧など、良く参照する公式ドキュメントはこちらです。


チュートリアル

- [Unity - Manual: Shaders: vertex and fragment programs](https://docs.unity3d.com/Manual/ShaderTut2.html)




リファレンス

- [Unity - Manual: Built-in shader variables](https://docs.unity3d.com/Manual/SL-UnityShaderVariables.html)

- [Unity - Manual: Built-in shader helper functions](https://docs.unity3d.com/Manual/SL-BuiltinFunctions.html)

- [Unity - Manual: Predefined Shader preprocessor macros](https://docs.unity3d.com/Manual/SL-BuiltinMacros.html)


- [Unity - Manual: Built-in shader include files](https://docs.unity3d.com/Manual/SL-BuiltinIncludes.html)

- [Unity - Manual: Providing vertex data to vertex programs](https://docs.unity3d.com/Manual/SL-VertexProgramInputs.html)

- [Unity - Manual: Shader semantics](https://docs.unity3d.com/Manual/SL-ShaderSemantics.html)

- [Unity - Manual: Writing vertex and fragment shaders](https://docs.unity3d.com/Manual/SL-ShaderPrograms.html)



サンプル集

- [Unity - Manual: Vertex and fragment shader examples](https://docs.unity3d.com/Manual/SL-VertexFragmentShaderExamples.html)





### CGINCLUDE ~ ENDCG

上記の `CGPROGRAM` とほぼ同様ですが、SubShader 内などで、各 Pass 共通の処理を書くときに使用します。




### シェーダーモデルのターゲットレベル

ターゲットレベルは、基本的に Shader Model 3.0 にしておくと良いと思います。

- `#pragma target 3.0`

各モデルでサポートされている機能の一覧はこちらで確認できます。

- [Unity - Manual: Shader Compilation Target Levels](https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html)




### multi_compile vs. shader_feature


設定によって処理内容を変える方法として、`#pragma multi_compile...` と `#pragma shader_feature...` の２つがあります。


- `multi_compile`
    - 使用しているかどうかに関わらず、すべての組み合わせがコンパイルされる。ランタイム中に `Material.EnableKeyword` で切り替えが可能。
- `shader_feature`
    - プロジェクト内で使用している組み合わせだけがコンパイルされる。ランタイム中に切り替えは不可能。


とりあえず `multi_compile` を使っておこう、と思えますが、`multi_compile` は組み合わせのすべてを shader variants としてコンパイルするので、数によっては非常に時間のかかる処理になってしまいます。
（例： オン・オフが4つ、2 ^ 4 = 16 種類のバリアント）


シェーダーが初めて表示されるときにメインスレッドでコンパイルが走るわけですが、シェーダーバリアントが多い場合は、特にモバイル環境でコンパイルに時間がかかり、終わるまでアニメーション・パーティクル・UIの入力ほか全てが停まることになります。

そのため、`shader_feature` との使い分けが必要になります。


参考：

- [Unity - Manual: Making multiple shader program variants](https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html)





### if/else の使用は避ける

シェーダーの条件分岐で if/else を使用することは可能ですが、処理が重いので代わりに `step` 関数を使用します。詳細は以下の記事が非常に参考になります。


- [条件分岐のためにstep関数を使う時の考え方をまとめてみた - Qiita](https://qiita.com/yuichiroharai/items/6e378cd128279ac9a2f0)
    - ※ `mix()` は `GLSL` の関数で、`ShaderLab (HLSL)` では `lerp()` を使用します。



GLSL と HLSL の差異はこちらにまとまっています。

- [GLSLをHLSLに書き換える - Qiita](https://qiita.com/kitasenjudesign/items/89297f239059662cd38e)





### TEXCOORD が不足した場合

UV は基本的には最大 4 枚（TEXCOORD0~3）で、`TEXCOORD1` はライトマップ用に使われる想定になっています。モデルのインポート設定で Generate Lightmap UVs をオンにした場合、`TEXCOORD1` が更新されるという事ですね。


この `TEXCOORD` は UV 以外にも Unity 標準のフォグの実装などでも使われていたり、その他様々な用途で消費され、足りなくなってきます。

UVは2次元の座標ですが、`TEXCOORD` は `float4` or `half4` の4要素を持てるようになっています。なので、UV 1つ目を `TEXCOORD#.xy` に、UV 2つ目を `TEXCOORD#.zw` に、といったように２つを１つにまとめる事も可能です。
※ VAT シェーダーでも `TEXCOORD0` の `.zw` を使っています。


Unity 上で UV を設定する場合、`Mesh.uv*` を使用すると `.zw` の設定が出来ないので、代わりに `Mesh.GetUVs` & `Mesh.SetUVs` を使います。

- [Unity - Scripting API: Mesh.GetUVs](https://docs.unity3d.com/ScriptReference/Mesh.GetUVs.html
)
- [Unity - Scripting API: Mesh.SetUVs](https://docs.unity3d.com/ScriptReference/Mesh.SetUVs.html)


`v2f` など、頂点シェーダーからピクセルシェーダーにデータを送る際には、`TEXCOORD` を効率よく利用することを心がけておきたいです。









### 頂点シェーダーの注意点


テクスチャの色情報を取得する `tex2D()` がありますが、これはフラグメントシェーダーの中でしか使えません。

バーテックスシェーダーからテクスチャにアクセスしたい場合には、代わりに `tex2Dlod()` を使用します。




### フラグメントシェーダーの注意点


注意点、では無いですが、`fixed4 frag()` だと HDR レンダリングに対応できるのか、出来ないのか、調べ切れませんでした。

溢れた色が思った通りにならなければ、`half4 frag()` や `float4 frag()` にすると良いかもしれません。







## おわりに


冒頭で紹介した VAT シェーダーですが、仕事で使う必要が無くなった＆他の事で手一杯なので、今後の開発についてはまっっったく予定が立たない状態ではありますが、積んでしまったパーティクル（Shuriken）のメッシュインスタンスへの対応なども、シェーダーの開発に合わせてこちらの備忘録まとめに追加していければ良いなと思っています。


今回は１体のポリゴン数が 4,472、アニメーションが 202 frames @ 30fps のアセットを録画しながら大量に動かす、という結構な重みでのテストでしたが、モバイルでも 100 体程度なら十分動いたのは予想外でした。

ループモーションで動くキャラをつまんで移動することもできるので、リアルタイムストラテジーのように引いた絵に大量のキャラがいるようなゲームであれば、VAT でのアニメーションでも十分なのかもしれませんね。


--


最後までお読み頂きありがとうございます。これは Unity のアドベントカレンダーに向けて書いていたのですが、当初想定した以上に長く＆遅くなってしまいました。みなさまの今後のシェーダー開発の助けになれば幸いです。

それでは以上になります。お疲れさまでした。



### おまけ

モバイル端末（Android）でのテスト動画です。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">モバイルで VAT シェーダーのテスト<br><br>Vertex Animation Texture shader on mobile platform, <a href="https://twitter.com/hashtag/unity3d?src=hash&amp;ref_src=twsrc%5Etfw">#unity3d</a>.<br>202 frames@30fps animation, 4,472 tris each.<br><br>Xperia 5 (Snapdragon 855 / Adreno 640)<br><br>60fps＝180mesh<br>30fps＝460mesh<br>1,000mesh＝20fps <a href="https://t.co/k1GpXStzQW">pic.twitter.com/k1GpXStzQW</a></p>&mdash; サトー (@sator_imaging) <a href="https://twitter.com/sator_imaging/status/1209796110929149954?ref_src=twsrc%5Etfw">December 25, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>