2.自定义材质面板-MaterialPropertyDrawer类
2.1 知识点
MaterialPropertyDrawer类是用来做什么的
MaterialPropertyDrawer(材质属性绘制器)用于自定义材质属性在材质面板中的显示和交互方式。
材质属性通常在 Shader 中通过属性语句块定义。
默认情况下,Unity 提供了一些基础的控件(如滑块、颜色选择器等),通过继承 MaterialPropertyDrawer,可以为自定义 Shader 属性创建更加灵活和直观的控件。
MaterialPropertyDrawer和ShaderGUI的区别
ShaderGUI 用来自定义整个材质面板,可以理解为以 Shader 材质球为一个整体单位;
而 MaterialPropertyDrawer(材质属性绘制器)是用来自定义某一个属性的,以属性为一个整体单位,
相当于可以更加精细地进行属性自定义显示封装。
MaterialPropertyDrawer类的声明
主要步骤
- 新建一个 C# 脚本,继承自 MaterialPropertyDrawer 类
- 重写
void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
方法 - 在其中实现 UI 自定义布局
声明
using UnityEditor;
using UnityEngine;
public class Lesson02_MaterialPropertyDrawer : MaterialPropertyDrawer
{
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
base.OnGUI(position, prop, label, editor);
// 自定义布局逻辑
}
}
MaterialPropertyDrawer类的使用
配合ShaderGUI使用
材质编辑器绘制指定为当前ShaderGUI的代码
Shader "Unlit/Lesson02_TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("TestFloat", Float) = 1
}
SubShader
{
// ...
}
CustomEditor "Lesson02_CustomShaderInspector"
}
定义材质绘制器,指定绘制Float类型的属性
using UnityEditor;
using UnityEngine;
// 自定义材质属性绘制器,继承自 MaterialPropertyDrawer
// 用于在材质面板中创建带范围限制的滑动条控件
public class Lesson02_MaterialPropertyDrawer : MaterialPropertyDrawer
{
private float min; // 滑动条最小值
private float max; // 滑动条最大值
/// <summary>
/// 构造函数(用于初始化滑动条范围)
/// </summary>
/// <param name="min">滑动条最小值</param>
/// <param name="max">滑动条最大值</param>
public Lesson02_MaterialPropertyDrawer(float min, float max)
{
this.min = min;
this.max = max;
}
/// <summary>
/// 重写GUI绘制方法
/// </summary>
/// <param name="position">控件位置</param>
/// <param name="prop">材质属性</param>
/// <param name="label">属性显示名称</param>
/// <param name="editor">材质编辑器实例</param>
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
// base.OnGUI(position, prop, label, editor); // 注释掉默认绘制逻辑
// 类型校验:仅支持 Float 类型属性
if (prop.type != MaterialProperty.PropType.Float)
{
// 显示错误提示(当属性类型不匹配时)
EditorGUILayout.LabelField(label, "请使用float或者数值 不然无法使用该控件");
return;
}
// 创建带范围限制的滑动条控件
prop.floatValue = EditorGUILayout.Slider(
label, // 属性显示名称
prop.floatValue, // 当前属性值
min, // 滑动条最小值
max // 滑动条最大值
);
}
}
复制上节课的ShaderGUI代码,初始实例化材质绘制器floatMaterialPropertyDrawer且区间设置为-50到50,绘制属性名为TestFloat时调用绘制器的绘制方法进行绘制
using UnityEditor;
using UnityEngine;
// 自定义Shader检查器类,继承自 ShaderGUI
public class Lesson02_CustomShaderInspector : ShaderGUI
{
private bool isShow; // 控制属性面板的显示状态
// private float value; // _TestFloat属性的值 其实没必要 直接用 materialProperty.floatValue 就行
private Lesson02_MaterialPropertyDrawer floatMaterialPropertyDrawer = new Lesson02_MaterialPropertyDrawer(-50, 50);
// 重写 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)
{
//...
// 遍历所有材质属性的方式绘制属性编辑器
foreach (var materialProperty in materialProperties)
{
if (materialProperty.displayName == "TestFloat")
{
//...
// 调用 floatMaterialPropertyDrawer 的 OnGUI 方法,用于在 Unity 编辑器的 Inspector 面板中绘制自定义的 GUI 控件
// 此方法会处理特定材质属性(MaterialProperty)的显示和交互逻辑
// 参数说明:
// 1. EditorGUILayout.GetControlRect():获取一个用于绘制 GUI 控件的矩形区域,
// 这个矩形区域定义了在 Inspector 面板中显示该控件的位置和大小
// 2. materialProperty:要处理的材质属性对象,
// 它包含了材质属性的各种信息,如属性值、类型等
// 3. materialProperty.displayName:材质属性的显示名称,
// 这个名称会显示在 Inspector 面板中,用于标识该属性
// 4. materialEditor:当前的材质编辑器对象,
// 它提供了与材质编辑相关的上下文和功能
floatMaterialPropertyDrawer.OnGUI(EditorGUILayout.GetControlRect(), materialProperty,
materialProperty.displayName, materialEditor);
}
else
// 利用获取到的每个材质属性,采用 Unity 自带的 Inspector 窗口 UI 显示方式展示这些属性
// 效果与默认绘制相同
materialEditor.ShaderProperty(materialProperty, materialProperty.displayName);
}
}
}
}
可以看到效果,我们设置的 -50 到 50 生效了
独立使用
注释掉 CustomEditor,不指定 ShaderGUI
在 Shader 文件中添加一个 TestFloat2 属性,属性前使用 [] 传入绘制类名(不需要添加 Drawer 后缀,和特性类似)
Shader "Unlit/Lesson02_TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("TestFloat", Float) = 1
[Lesson02_MaterialProperty(-6.6,6.6)]_TestFloat2("TestFloat2", Float) = 1
}
SubShader
{
//...
}
// CustomEditor "Lesson02_CustomShaderInspector"
}
由于 Unity 的 bug 可能看不到效果
这无伤大雅,因为实际上很少直接继承 MaterialPropertyDrawer,Unity 已经提供了很多写好的基础 MaterialPropertyDrawer 类。
2.2 知识点代码
Lesson02_TestShader.shader
Shader "Unlit/Lesson02_TestShader"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("TestFloat", Float) = 1
// [Lesson02_MaterialProperty(-6.6,6.6)]_TestFloat2("TestFloat2", 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 "Lesson02_CustomShaderInspector"
}
Lesson02_CustomShaderInspector.cs
using UnityEditor;
using UnityEngine;
// 自定义Shader检查器类,继承自ShaderGUI
public class Lesson02_CustomShaderInspector : ShaderGUI
{
private bool isShow; // 控制属性面板的显示状态
// private float value; // _TestFloat属性的值 其实没必要 直接用materialProperty.floatValue就行
private Lesson02_MaterialPropertyDrawer floatMaterialPropertyDrawer = new Lesson02_MaterialPropertyDrawer(-50, 50);
// 重写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);
// 调用 floatMaterialPropertyDrawer 的 OnGUI 方法,用于在 Unity 编辑器的 Inspector 面板中绘制自定义的 GUI 控件
// 此方法会处理特定材质属性(MaterialProperty)的显示和交互逻辑
// 参数说明:
// 1. EditorGUILayout.GetControlRect():获取一个用于绘制 GUI 控件的矩形区域
// 这个矩形区域定义了在 Inspector 面板中显示该控件的位置和大小
// 2. materialProperty:要处理的材质属性对象
// 它包含了材质属性的各种信息,如属性值、类型等
// 3. materialProperty.displayName:材质属性的显示名称
// 这个名称会显示在 Inspector 面板中,用于标识该属性
// 4. materialEditor:当前的材质编辑器对象
// 它提供了与材质编辑相关的上下文和功能
floatMaterialPropertyDrawer.OnGUI(EditorGUILayout.GetControlRect(), materialProperty,
materialProperty.displayName, materialEditor);
}
else
//利用获取到的一个个的材质属性 利用Unity自带的Inspector窗口UI显示方式去显示这些属性
//等于和默认绘制没区别
materialEditor.ShaderProperty(materialProperty, materialProperty.displayName);
}
}
}
}
Lesson02_MaterialPropertyDrawer.cs
using UnityEditor;
using UnityEngine;
// 自定义材质属性绘制器,继承自MaterialPropertyDrawer
// 用于在材质面板中创建带范围限制的滑动条控件
public class Lesson02_MaterialPropertyDrawer : MaterialPropertyDrawer
{
private float min; // 滑动条最小值
private float max; // 滑动条最大值
/// <summary>
/// 构造函数(用于初始化滑动条范围)
/// </summary>
/// <param name="min">滑动条最小值</param>
/// <param name="max">滑动条最大值</param>
public Lesson02_MaterialPropertyDrawer(float min, float max)
{
this.min = min;
this.max = max;
}
/// <summary>
/// 重写GUI绘制方法
/// </summary>
/// <param name="position">控件位置</param>
/// <param name="prop">材质属性</param>
/// <param name="label">属性显示名称</param>
/// <param name="editor">材质编辑器实例</param>
public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor)
{
// base.OnGUI(position, prop, label, editor); // 注释掉默认绘制逻辑
// 类型校验:仅支持Float类型属性
if (prop.type != MaterialProperty.PropType.Float)
{
// 显示错误提示(当属性类型不匹配时)
EditorGUILayout.LabelField(label, "请使用float或者数值 不然无法使用该控件");
return;
}
// 创建带范围限制的滑动条控件
prop.floatValue = EditorGUILayout.Slider(
label, // 属性显示名称
prop.floatValue, // 当前属性值
min, // 滑动条最小值
max // 滑动条最大值
);
}
}
Lesson02_自定义材质面板_MaterialPropertyDrawer类.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson02_自定义材质面板_MaterialPropertyDrawer类 : MonoBehaviour
{
void Start()
{
#region 知识点一 MaterialPropertyDrawer类是用来做什么的
//MaterialPropertyDrawer(材质属性绘制器) 用于自定义材质 属性 在 材质面板中的显示和交互方式的
//材质属性通常在Shader中通过 属性语句块 定义。
//默认情况下,Unity提供了一些基础的控件(如滑块、颜色选择器等)
//通过继承 MaterialPropertyDrawer(材质属性绘制器)
//你可以为自定义Shader属性创建更加灵活和直观的控件
#endregion
#region 知识点二 MaterialPropertyDrawer和ShaderGUI的区别
//ShaderGUI是用来自定义整个材质面板的 可以理解为以Shader材质球为一个整体单位
//而MaterialPropertyDrawer(材质属性绘制器) 是用来自定义某一个属性的 以属性为一个整体单位
//相当于可以更加精细的来进行属性自定义显示封装
#endregion
#region 知识点三 MaterialPropertyDrawer类的声明
//1.新建一个C#脚本,继承自MaterialPropertyDrawer类
//2.重写void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor) 方法
//3.在其中实现UI自定义布局
#endregion
#region 知识点四 MaterialPropertyDrawer类的使用
//1.配合ShaderGUI使用
//2.独立使用
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com