本文
この記事は、Unity Advent Calendar 2017 の10日目の記事になります。
前日の記事は @lycoris102 さんの [Unity] Editor拡張でInspectorの"戻る"を実現する です。gifを用いた非常に丁寧な投稿でした。
本内容ですが、以前にShaderのCullingやZTest等のMaterialPorpertyをEnumにて指定できるようにしましたが、
コメントにてMaterialのEditor拡張を行うCustomShaderGUIを教えて頂きました。
大分期間が空いてしまいましたが、今回それを用いてBlend Modeのプリセットを指定できるよう拡張しました。
サンプルとしてSpriteRenderer用のシンプルなシェーダーを作ります。
プリセットのSprite-Defaultシェーダーは加算ブレンドがない為、
Particle/Additive等を代用したり自前で用意したりする必要がありますが
もうこの際シンプルなシェーダーはコレ一つで足りるようにします。
// 標準スプライトシェーダー
Shader "Custom/SpriteBasic" {
Properties {
_MainTex ("Base (RGB), Alpha (A)", 2D) = "white" {}
[Enum(UnityEngine.Rendering.CullMode)]
_Cull("Cull", Float) = 0 // Off
[Enum(UnityEngine.Rendering.CompareFunction)]
_ZTest("Z Test", Float) = 4 // LEqual
[Enum(UnityEngine.Rendering.BlendMode)]
_SrcBlend("Src Factor", Float) = 5 // SrcAlpha
[Enum(UnityEngine.Rendering.BlendMode)]
_DstBlend("Dst Factor", Float) = 10 // OneMinusSrcAlpha
[HideInInspector] _Mode("__mode", Float) = 0.0 // ブレンドモードキャッシュ
}
SubShader {
Tags {
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
"PreviewType" = "Plane"
"CanUseSpriteAtlas"="True"
}
Cull [_Cull] ZTest [_ZTest] ZWrite Off
Blend[_SrcBlend][_DstBlend]
Pass {
CGPROGRAM
struct appdata_t {
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
fixed4 color : COLOR;
};
struct v2f {
float4 pos : SV_POSITION;
half2 uv : TEXCOORD0;
fixed4 color : COLOR;
};
sampler2D _MainTex;
v2f vert(appdata_t v) {
v2f o;
o.pos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1.0)));
o.uv = v.texcoord;
o.color = v.color;
#ifdef PIXELSNAP_ON
o.pos = UnityPixelSnap(o.pos);
#endif
return o;
}
fixed4 frag(v2f i) : SV_Target {
return tex2D(_MainTex, i.uv) * i.color;
}
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
// ShaderGUI指定
CustomEditor "SpriteBasicShaderGUI"
}
using UnityEngine;
using UnityEditor;
using System;
/// <summary>
/// 標準スプライトシェーダー
/// </summary>
public sealed class SpriteBasicShaderGUI : ShaderGUI {
/// <summary>
/// ブレンド方法
/// </summary>
public enum BlendMode {
Opaque, // 不透明
Transparent, // 半透明
Additive, // 加算
AdditiveTransparent, // 加算半透明
}
private MaterialProperty blendProp, cullProp, ztestProp, snapProp;
/// <summary>
/// Inspector表示
/// </summary>
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) {
this.blendProp = ShaderGUI.FindProperty("_BlendMode", props);
this.cullProp = ShaderGUI.FindProperty("_Cull", props);
this.ztestProp = ShaderGUI.FindProperty("_ZTest", props);
this.snapProp = ShaderGUI.FindProperty("_PixelSnap", props);
materialEditor.ShaderProperty(this.cullProp, "Culling");
materialEditor.ShaderProperty(this.ztestProp, "Z Test");
BlendMode mode = (BlendMode)this.blendProp.floatValue;
EditorGUI.BeginChangeCheck();
mode = (BlendMode)EditorGUILayout.Popup("Blend Mode", (int)mode, Enum.GetNames(typeof(BlendMode)));
if (EditorGUI.EndChangeCheck()) {
this.blendProp.floatValue = (float)mode;
foreach (UnityEngine.Object obj in this.blendProp.targets)
this.SetupBlendMode(obj as Material, mode);
}
materialEditor.ShaderProperty(this.snapProp, "Pixel Snap");
materialEditor.RenderQueueField();
//materialEditor.EnableInstancingField(); // GPU Instancing未対応
}
/// <summary>
/// Shader切り替えコールバック
/// </summary>
public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader) {
base.AssignNewShaderToMaterial(material, oldShader, newShader);
// MaterialのShader切り替え時にBlend指定が変更されてしまうので再設定
this.SetupBlendMode(material, (BlendMode)material.GetFloat("_BlendMode"));
}
/// <summary>
/// ブレンド種類設定
/// </summary>
private void SetupBlendMode(Material material, BlendMode blendMode) {
switch (blendMode) {
case BlendMode.Opaque:
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.One);
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.Zero);
break;
case BlendMode.Transparent:
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
break;
case BlendMode.Additive:
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.One);
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.One);
break;
case BlendMode.AdditiveTransparent:
material.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha);
material.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.One);
break;
}
}
}
これで下記画像のように半透明・加算のマテリアル等を用意すればこのShaderひとつで対応できます。
今回Sprite用のマテリアルなのでMainTexの表記はInspector上に表示しないようにしています。
Tintカラーもやや蛇足な処理なので今回は無視しています。
GPU Instancingを対応したい場合はビルトインシェーダーのUnitySprite.cgincを参考にすればよいでしょう。
※ビルトインシェーダーはMITライセンスが明記されていますのでその旨ご注意ください。
次の記事は @Kan_Kikuchi さんによる 日本語から変数や関数名を生成するエディタ拡張になります!