StandardShaderのMode選択機能をカスタムシェーダーに入れたい！

#カスタムシェーダーのInspectorもこだわりたい。
`StandardShader`の一番上についている`Renderting Mode`のプルダウンメニュー
自作シェーダーの`BlendMode`をいじるたびにコレつけられたらかっこいいのに！
と思ってました。

↓コレ
![StandardMode.png](https://qiita-image-store.s3.amazonaws.com/0/212169/49b91407-3cae-2ded-15a3-9b9afae6637a.png)

というわけで、ちょっとチャレンジしてみることにしました。
`Unity2017.2.0f3`で動作確認しています。

#参考
・builtin_shadersの中に含まれるeditor/StandardShaderGUI.cs
・安藤圭吾さんのエディター拡張入門
[第6章　EditorGUI (EdirotGUILayout)](![image.png](https://qiita-image-store.s3.amazonaws.com/0/212169/2f062928-456e-4a79-05bf-37eea788df82.png))

#まずは準備
![shadersample.png](https://qiita-image-store.s3.amazonaws.com/0/212169/7c62d166-6fe4-90d0-fd75-9b13dcbb9347.png)

`Create > Shader>Unlit Shader`　名前をGUITest
`Create  >Material` できたMaterialに先ほどのShaderをアタッチ
Editorフォルダを作って中に`Create > C# Script`
名前は適当に付けました。　一応画像の通りにしてあります。

Shaderの最後に`CustomEditor　"BlendTestGUI"`　（名前は任意）
こうすることでこのShaderはMaterial経由でInspectorに表示される時は指定されたScriptを
参照するようになるみたいです。
ちなみに、Standard.Shaderは末尾に`CustomEditor "StandardShaderGUI"`と書いてありますね。

#調べたこと
StandardShaderGUI.csの中にModeの設定について記載がありました。
`public enum BlendMode`
`public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode)`

BlendModeの定義を使ってSetupMaterialWithBlendModeで値をセットしているみたいです。
StandardShaderGUIは長いので後はEditorGUIを調べつつシンプルに実装していきます。

※ソースは下記参照してください

Inspectorの表示物はこの中↓に記載します
`OnInspectorGUI()`

流れとしては。
`Material targetMat = target as Material;`
ここで扱うMaterialを確保しておいて

`EditorGUI.BeginChangeCheck();`
これでGUI内の変更を監視しつつ、表示部本体を表示

`if (EditorGUI.EndChangeCheck())`
ここで変更があった場合にパラメータをMaterialにセットしていきます。
セット忘れると、GUIはいじれても値がセットされません。

Materialの設定項目そのものは`StandardShader`からコピペになっています。

#結果
さあどうでしょうか。無事モードセレクトが表示されました。
このあとは設定項目に`Additive`を追加してみたりとか夢がひろがります。
Materialを設定する人が使いやすいGUI設計ができそうな気がしてきました。


![kekka1.png](https://qiita-image-store.s3.amazonaws.com/0/212169/3707ee4b-d640-a5e6-2e13-6a6ff727bc1f.png)

![kekka2.png](https://qiita-image-store.s3.amazonaws.com/0/212169/b095793e-b3ad-3209-8322-7ff9711d768d.png)


#それぞれのScriptの中身

Shaderの中はこんな感じにしています。

    Shader "Custom/GuiTest"
    {
	Properties
	{
		_Color("Color",color) = (1,1,1,1)
		_MainTex ("Texture", 2D) = "white" {}

		[HideInInspector] _Mode("Mode", Float) = 0.0
		[HideInInspector]_SrcBlend("Blend Src", Float) = 0
		[HideInInspector]_DstBlend("Blend Dst", Float) = 0
		[HideInInspector]_ZWrite("ZWrite", Float) = 0
	}
	SubShader
	{
		LOD 100

		Pass
		{
			Blend[_SrcBlend][_DstBlend]
			ZWrite[_ZWrite]

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			// make fog work
			#pragma multi_compile_fog
			
			#include "UnityCG.cginc"

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

			struct v2f
			{
				float2 uv : TEXCOORD0;
				UNITY_FOG_COORDS(1)
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float4 _Color;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				UNITY_TRANSFER_FOG(o,o.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = tex2D(_MainTex, i.uv) * _Color;
				// apply fog
				UNITY_APPLY_FOG(i.fogCoord, col);
				return col;
			}
			ENDCG
		}
	}
		CustomEditor "BlendTestGUI"
    }

Edirorの中に作成したBlendTestGUI.csはこんな感じです。

    public class BlendTestGUI : MaterialEditor
    {
        public override void OnInspectorGUI()
        {
            if (!isVisible)
                return;

            //Materialを所得
            Material targetMat = target as Material;

            //GUIの変更をチェック開始
            EditorGUI.BeginChangeCheck();

            //表示物本体
            //Mode選択
            BlendMode　SelectedMode = (BlendMode)EditorGUILayout.EnumPopup("ブレンドモード", (BlendMode)targetMat.GetFloat("_Mode"));
            Color color = EditorGUILayout.ColorField("MainColor",targetMat.color);
            Texture Texture =  EditorGUILayout.ObjectField(targetMat.GetTexture("_MainTex"), 
                typeof(Texture),false, GUILayout.Width(64), GUILayout.Height(64)) as Texture;
            Vector2 Tiling = EditorGUILayout.Vector2Field("Tiling", targetMat.GetTextureScale("_MainTex"),
                GUILayout.Width(200), GUILayout.Height(32));
            Vector2 Offset = EditorGUILayout.Vector2Field("Offset", targetMat.GetTextureOffset("_MainTex"),
                GUILayout.Width(200), GUILayout.Height(32));

            //GUIの変更点をMaterialに反映
            if (EditorGUI.EndChangeCheck())
            {
                targetMat.SetFloat("_Mode", (float)SelectedMode);
                SetupMaterialWithBlendMode(targetMat, SelectedMode);//BlendModeも保存
                targetMat.SetColor("_Color", color);
                targetMat.SetTexture("_MainTex", Texture);
                targetMat.SetTextureScale("_MainTex", Tiling);
                targetMat.SetTextureOffset("_MainTex", Offset);
            }
        }

        //StandardShaderGUI.csから引っこ抜き
        public enum BlendMode
        {
            Opaque,
            Cutout,
            Fade,       // Old school alpha-blending mode, fresnel does not affect amount of transparency
            Transparent // Physically plausible transparency mode, implemented as alpha pre-multiply
        }

        public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode)
        {
            switch (blendMode)
            {
                case BlendMode.Opaque:
                    material.SetOverrideTag("RenderType", "");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = -1;
                    break;
                case BlendMode.Cutout:
                    material.SetOverrideTag("RenderType", "TransparentCutout");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
                    material.SetInt("_ZWrite", 1);
                    material.EnableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
                    break;
                case BlendMode.Fade:
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.EnableKeyword("_ALPHABLEND_ON");
                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                    break;
                case BlendMode.Transparent:
                    material.SetOverrideTag("RenderType", "Transparent");
                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                    material.SetInt("_ZWrite", 0);
                    material.DisableKeyword("_ALPHATEST_ON");
                    material.DisableKeyword("_ALPHABLEND_ON");
                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
                    break;
            }
        }

    }