unity自定義材質面板的一個小心得

unity自定義材質面板的一個小心得

來自專欄喵喵Mya的shader雜貨店16 人贊了文章

最近在給shader寫材質面板的時候,遇到了一個小坑,在這裡記錄一下。

關於unity的自定義材質面板,網上的資料並不太多,主要還是參考的unity給standard shader寫的材質面板腳本(在built-in shader包里可以找到這些文件)。

要自定義某個shader的材質面板,我們只需要在shader最下面添加一行:

CustomEditor "XXX"

雙引號裡面的就是我們的自己寫的材質面板腳本的名字(腳本需要放在名為Editor的文件夾內)。

參考standard的寫法,我寫了一個非常簡單的面板:

using System;using UnityEngine;using UnityEditor;public class CustomShaderGUI : ShaderGUI{ MaterialProperty mainTex; public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) { Material targetMat = materialEditor.target as Material; mainTex = FindProperty("_MainTex", props); GUIContent mainTexText = new GUIContent("Textrue", "Main Textrue"); materialEditor.ShaderProperty(mainTex, mainTexText); }}

代碼很簡單,就是在面板上把一個shader屬性畫出來,我們看看效果。

一個帶 Tiling和Offset的貼圖屬性已經畫出來了,不過為什麼貼圖槽變的那麼小了??我門看看默認的面板:

默認的貼圖屬性面板

最初我以為是用的方法不對,但是看了下unity的源碼,發現默認的面板其實也是用同樣的方法畫出來的。

中間的曲折不細說,反正我是把整個MaterialEditor都看了一遍,最後定位到問題是因為scaleOffsetRect的寬度是根據EditorGUIUtility.fieldWidth這個值來計算的,於是,我自己設了一下這個值:

EditorGUIUtility.fieldWidth = 65;

這樣效果就和默認的效果一致了,然後我在回頭看了一下PropertiesDefaultGUI這個函數,它其實是這樣寫的:

public void PropertiesDefaultGUI(MaterialProperty[] props) { SetDefaultGUIWidths(); if (m_InfoMessage != null) EditorGUILayout.HelpBox(m_InfoMessage, MessageType.Info); else // Hack to make sure that control IDs stay the same when the help box is there or is not there. // Otherwise, open color picker windows will not keep synched to the same properties when the help box // shows up or disappears (case 566958) GUIUtility.GetControlID(s_ControlHash, FocusType.Passive, new Rect(0, 0, 0, 0)); for (var i = 0; i < props.Length; i++) { if ((props[i].flags & (MaterialProperty.PropFlags.HideInInspector | MaterialProperty.PropFlags.PerRendererData)) != 0) continue; float h = GetPropertyHeight(props[i], props[i].displayName); Rect r = EditorGUILayout.GetControlRect(true, h, EditorStyles.layerMaskField); ShaderProperty(r, props[i], props[i].displayName); } EditorGUILayout.Space(); EditorGUILayout.Space(); RenderQueueField(); EnableInstancingField(); DoubleSidedGIField(); }

裡面第一行就用到了一個函數:

public void SetDefaultGUIWidths() { EditorGUIUtility.fieldWidth = EditorGUI.kObjectFieldThumbnailHeight; EditorGUIUtility.labelWidth = GUIClip.visibleRect.width - EditorGUIUtility.fieldWidth - 17; }

這個函數的其實就是把fieldWidth 的值設為和高度一樣,而這個函數在官方文檔裡面其實是有說的,為什麼我繞了一大圈最後才發現人家第一行就已經寫了這個方法呢?答案是......我看漏了......

雖然花了一下午的時間來解決這個非常基礎的小問題(其實好好看一下api文檔就能解決了),但能把默認材質面板的相關源碼都理解一遍也算是一個不小的收穫吧。

最後是完整代碼:

using System;using UnityEngine;using UnityEditor;public class CustomShaderGUI : ShaderGUI{ MaterialProperty mainTex; public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) { materialEditor.SetDefaultGUIWidths(); Material targetMat = materialEditor.target as Material; mainTex = FindProperty("_MainTex", props); GUIContent mainTexText = new GUIContent("Textrue", "Main Textrue"); materialEditor.ShaderProperty(mainTex, mainTexText ); }}

shader:

Shader "Mya/TestShader"{ Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); return col; } ENDCG } } CustomEditor "CustomShaderGUI"}

推薦閱讀:

室友每晚都LOL五黑到一點左右,已經影響到自己的休息了,我該怎麼辦?
如何評價蝙蝠俠大戰超人:正義黎明?
「JJ比賽杯」公開賽分站賽怎樣收官?
旋轉輪胎怎麼玩?
海洋世界探險的遊戲有哪些?

TAG:遊戲 | shader | unity |