1.自定义材质面板-ShaderGUI类
1.1 知识点
自定义材质面板指的是什么
在 Unity 中,我们可以在 Shader 中添加属性,以便在材质球的 Inspector 窗口中显示,并让外部进行设置。而 自定义材质面板 指的是,除了 Unity 默认的 Inspector 视图外,我们还可以定制 材质球的 Inspector 界面,让其具备更丰富的编辑功能。
ShaderGUI类是什么
ShaderGUI 是一个用于 自定义材质 Inspector 界面 的基类。通过继承 ShaderGUI,我们可以完全控制 材质编辑界面的布局和功能,而不仅仅局限于 Shader 属性 (Properties) 语句块 定义的默认行为。
ShaderGUI 可以实现:
- 自定义材质界面布局
- 基于某些属性值动态隐藏或禁用其他属性(通过脚本逻辑)
- 添加高级功能(如按钮控件,触发某些操作,如实时更新材质预览、显示调试信息等)
- 增强材质编辑体验,使美术人员或其他开发者无需关心底层 Shader 实现,而是通过友好的界面快速调整材质
ShaderGUI 类的基本使用
主要步骤
- 自定义 C# 脚本 继承
ShaderGUI
- 重写
OnGUI
方法 - 在
OnGUI
方法中编写自定义布局逻辑 - 在 Shader 语句块最后加入
CustomEditor "自定义 C# 脚本名"
这样便可以让使用该 Shader 的材质面板使用自定义布局规则。
自定义C#脚本继承ShaderGUI,重写OnGUI方法,在OnGUI方法中书写自定义布局逻辑
using UnityEditor;
using UnityEngine;
public class Lesson01_CustomShaderInspector : ShaderGUI
{
// 重写 OnGUI 方法
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
{
// 开启父类的 GUI 显示
// base.OnGUI(materialEditor, properties);
// ...自定义逻辑
}
}
在Shader语句块最后加入 CustomEditor “自定义C#脚本名” ,这样便可以让使用该Shader的材质面板使用自定义布局规则,赋值给一个材质,可以看到就算定义了属性不使用父类Gui方法话Inspect窗口什么也看不到
在 Shader 文件的 最后 需要添加:
Shader "Unlit/Lesson01_TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("TestFloat", Float) = 1
}
SubShader
{
//...
}
CustomEditor "Lesson01_CustomShaderInspector"
}
这样就可以让 使用该 Shader 的材质面板 采用 自定义的 Inspector 布局规则。如果不调用 base.OnGUI(materialEditor, properties);
,即使 定义了属性,Inspector 窗口也不会显示任何内容。
自定义材质面板
核心方法
使用 ShaderGUI 自定义材质面板的 核心方法 是 OnGUI
方法:
void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties)
其中 两个参数 非常重要:
materialEditor
- 提供 与材质交互 的接口,比如获取和修改材质属性值。
- 关键属性
target
:可以获取到材质球对象。
- 关键方法
ShaderProperty(MaterialProperty对象, "名字")
让 Unity 以默认 Inspector UI 方式显示属性。
properties
- 包含所有 Shader
Properties
语句块 定义的属性。 - 关键属性
displayName
:获取属性的名称。
- 包含所有 Shader
其他方法
MaterialProperty 属性对象 = FindProperty("属性名", properties)
- 可以利用FindProperty方法,获取到对应属性对象
自定义 Inspector 面板完整代码示例
// 自定义Shader检查器类,继承自ShaderGUI
public class Lesson01_CustomShaderInspector : ShaderGUI
{
private bool isShow; // 控制属性面板的显示状态
// private float value; // _TestFloat属性的值 其实没必要 直接用materialProperty.floatValue就行
// 重写ShaderGUI的OnGUI方法,绘制自定义界面
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] materialProperties)
{
// base.OnGUI(materialEditor, properties); // 注释掉默认的界面绘制
// 显示/隐藏属性设置的切换按钮
if (GUILayout.Button(isShow ? "隐藏所有属性设置" : "显示所有属性设置"))
isShow = !isShow;
// 获取当前编辑的材质球
Material material = materialEditor.target as Material;
// 添加重置材质属性的按钮
if (GUILayout.Button("重置材质球属性"))
{
material.SetTexture("_MainTex", null);
material.SetFloat("_TestFloat", 0);
}
// 直接设置材质的渲染队列值
material.renderQueue = EditorGUILayout.IntField("渲染队列", material.renderQueue);
// 当显示属性时绘制所有属性
if (isShow)
{
//单独获取某一个属性的方式绘制属性编辑器
//找到_TestFloat属性
MaterialProperty testFloatMaterialProperty = FindProperty("_TestFloat", materialProperties);
//自定义一个拖动条去设置TestFloat属性
//这种单独获取某一个属性的方式 就不需要使用中间变量 以及 对应的材质球设置了
testFloatMaterialProperty.floatValue =
EditorGUILayout.Slider("单独获取某一个属性的自定义float属性", testFloatMaterialProperty.floatValue, -1, 1);
// 找到_MainTex属性
MaterialProperty mainTexMaterialProperty = FindProperty("_MainTex", materialProperties);
materialEditor.ShaderProperty(mainTexMaterialProperty, mainTexMaterialProperty.displayName);
//----------------------------------------------------------------------------
// 遍历所有材质属性的方式绘制属性编辑器
foreach (var materialProperty in materialProperties)
{
if (materialProperty.displayName == "TestFloat")
{
//自定义一个拖动条去设置TestFloat属性
materialProperty.floatValue =
EditorGUILayout.Slider("遍历所有材质属性的自定义float属性", materialProperty.floatValue, -1, 1);
// _TestFloat属性的值 其实没必要 直接用materialProperty.floatValue就行
// material.SetFloat("_TestFloat", value);
}
else
//利用获取到的一个个的材质属性 利用Unity自带的Inspector窗口UI显示方式去显示这些属性
//等于和默认绘制没区别
materialEditor.ShaderProperty(materialProperty, materialProperty.displayName);
}
}
}
}
查看效果
由于使用定义了单独获取和遍历获取两种方式,面板上会显示两个纹理和float。
总结
- ShaderGUI 让我们能够完全自定义材质 Inspector 面板
- 可以动态控制 Inspector 界面布局
- 可以为特定属性提供自定义编辑器
- 可以增强 Shader 编辑体验,让材质参数更直观可控
通过这些方法,我们可以创建更加直观、易用的材质编辑界面,让 Shader 开发更加高效。
1.2 知识点代码
Lesson01_TestShader.shader
Shader "Unlit/Lesson01_TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("TestFloat", Float) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
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;
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);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
CustomEditor "Lesson01_CustomShaderInspector"
}
Lesson01_自定义材质面板_ShaderGUI类.cs
using UnityEngine;
public class Lesson01_自定义材质面板_ShaderGUI类 : MonoBehaviour
{
void Start()
{
#region 知识点一 自定义材质面板指的是什么
//我们目前可以通过在Shader中添加属性的形式
//把一些我们希望从外部设置的内容在材质球的Inspector窗口中显示
//自定义材质面板指的就是
//Unity除了这些默认的显示内容外,还可以让我们自定义材质球的Inspector窗口显示
#endregion
#region 知识点二 ShaderGUI类是什么
//它是一个用于自定义材质 Inspector 界面 的基类
//通过继承 ShaderGUI,你可以完全控制材质编辑界面的布局和功能
//而不仅仅局限于 Shader 的 属性(Properties) 语句块定义的默认行为
//ShaderGUI可以让我们:
//1.自定义材质界面布局
//2.通过脚本逻辑,可以基于某些属性值动态隐藏或禁用其他属性
//3.添加高级功能,比如通过添加按钮控件,触发某些操作,如实时更新材质预览,显示调试信息等
//4.增强材质编辑体验,使美术人员或其他开发者无需关心底层Shader实现,而是通过友好的界面快速调整材质
//等等
#endregion
#region 知识点三 ShaderGUI类的基本使用
//1.自定义C#脚本继承ShaderGUI
//2.重写OnGUI方法
//3.在OnGUI方法中书写自定义布局逻辑
//4.在Shader语句块最后加入 CustomEditor "自定义C#脚本名"
//这样便可以让使用该Shader的材质面板使用自定义布局规则
#endregion
#region 知识点四 自定义材质面板
//使用ShaderGUI自定义材质面板的核心方法就是我们重写的
//void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) 方法
//其中两个参数非常重要:
//1.materialEditor:
// 提供与材质交互的接口,比如材质属性值等
// 关键属性:
// target
// 可以利用它获取到材质球
// 关键方法:
// ShaderProperty(MaterialProperty对象, "名字")
// 可以将属性设置为对应名字,并且用默认GUI显示
//2.properties:
// 包含所有在Shader的属性语句块中定义的属性
// 关键属性:
// displayName
// 获取属性名
//其他:
//MaterialProperty 属性对象 = FindProperty("属性名", properties)
//可以利用FindProperty方法,获取到对应属性对象
#endregion
}
}
Lesson01_CustomShaderInspectorc.cs
using UnityEditor;
using UnityEngine;
// 自定义Shader检查器类,继承自ShaderGUI
public class Lesson01_CustomShaderInspector : ShaderGUI
{
private bool isShow; // 控制属性面板的显示状态
// private float value; // _TestFloat属性的值 其实没必要 直接用materialProperty.floatValue就行
// 重写ShaderGUI的OnGUI方法,绘制自定义界面
public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] materialProperties)
{
// base.OnGUI(materialEditor, properties); // 注释掉默认的界面绘制
// 显示/隐藏属性设置的切换按钮
if (GUILayout.Button(isShow ? "隐藏所有属性设置" : "显示所有属性设置"))
isShow = !isShow;
// 获取当前编辑的材质球
Material material = materialEditor.target as Material;
// 添加重置材质属性的按钮
if (GUILayout.Button("重置材质球属性"))
{
material.SetTexture("_MainTex", null);
material.SetFloat("_TestFloat", 0);
}
// 直接设置材质的渲染队列值
material.renderQueue = EditorGUILayout.IntField("渲染队列", material.renderQueue);
// 当显示属性时绘制所有属性
if (isShow)
{
//单独获取某一个属性的方式绘制属性编辑器
//找到_TestFloat属性
MaterialProperty testFloatMaterialProperty = FindProperty("_TestFloat", materialProperties);
//自定义一个拖动条去设置TestFloat属性
//这种单独获取某一个属性的方式 就不需要使用中间变量 以及 对应的材质球设置了
testFloatMaterialProperty.floatValue =
EditorGUILayout.Slider("单独获取某一个属性的自定义float属性", testFloatMaterialProperty.floatValue, -1, 1);
// 找到_MainTex属性
MaterialProperty mainTexMaterialProperty = FindProperty("_MainTex", materialProperties);
materialEditor.ShaderProperty(mainTexMaterialProperty, mainTexMaterialProperty.displayName);
//----------------------------------------------------------------------------
// 遍历所有材质属性的方式绘制属性编辑器
foreach (var materialProperty in materialProperties)
{
if (materialProperty.displayName == "TestFloat")
{
//自定义一个拖动条去设置TestFloat属性
materialProperty.floatValue =
EditorGUILayout.Slider("遍历所有材质属性的自定义float属性", materialProperty.floatValue, -1, 1);
// _TestFloat属性的值 其实没必要 直接用materialProperty.floatValue就行
// material.SetFloat("_TestFloat", value);
}
else
//利用获取到的一个个的材质属性 利用Unity自带的Inspector窗口UI显示方式去显示这些属性
//等于和默认绘制没区别
materialEditor.ShaderProperty(materialProperty, materialProperty.displayName);
}
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com