12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

UnityAdvent Calendar 2017

Day 10

[Unity]CustomShaderGUIによるBlend Mode指定

Last updated at Posted at 2017-12-09

本文
この記事は、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等を代用したり自前で用意したりする必要がありますが
もうこの際シンプルなシェーダーはコレ一つで足りるようにします。

ShaderBasic.shader
// 標準スプライトシェーダー
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"
}
SpriteBasicShaderGUI.cs
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ひとつで対応できます。
SpriteMaterial.PNG

今回Sprite用のマテリアルなのでMainTexの表記はInspector上に表示しないようにしています。
Tintカラーもやや蛇足な処理なので今回は無視しています。

GPU Instancingを対応したい場合はビルトインシェーダーのUnitySprite.cgincを参考にすればよいでしょう。
※ビルトインシェーダーはMITライセンスが明記されていますのでその旨ご注意ください。


次の記事は @Kan_Kikuchi さんによる 日本語から変数や関数名を生成するエディタ拡張になります!

12
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?