最近工作过程中,有遇到需要动态修改模型材质球的情况,然后发现这点内置管线和URP还是有一些区别的,所以整理总结一下。
自定义结构体
我这里用到方法是创建一个自定义的结构体,结构体内写入对应的字段来存储材质球的各种属性,这里是我创建的一个结构体:
cs
[System.Serializable]
//初始材质状态
public struct MaterialState
{
// URP渲染模式
public int surfaceType; // 表面类型(0-不透明 1-透明)
public int blendMode; // 混合模式(1=Alpha混合,2=Premultiply等)
public int renderFace; //渲染面 (0-双面 1-背面 2-正面)
// 基础属性
public Color albedoColor; // 基础颜色
public Texture albedoMap; // 纹理贴图
public string renderTypeTag; // 渲染类型标签
public int renderQueue; // 渲染队列
public CompareFunction zTest; // 深度测试模式
public int zWrite; // 深度写入
public MaterialGlobalIlluminationFlags globalIlluminationFlags;
// URP PBR属性
public float metallic; // 金属度 [0-1]
public float smoothness; // 光滑度 [0-1]
public Texture metallicSmoothnessMap;// 金属光滑度贴图
//增强属性
public Texture normalMap; //法线贴图
public float normalStrength; //法线强度
// 自发光属性(URP增强版)
public bool isEmission; // 是否启用自发光
public Color emissionColor; // 自发光颜色
public float emissionIntensity; // 自发光强度
public Texture emissionMap; // 自发光贴图
public bool emissionUseGlobalIllumination; // 自发光是否影响全局光照
// URP关键字状态
public List<string> enabledKeywords; // 已启用的Shader关键字
}
保存材质球状态
Unity里动态保存或者修改材质球属性,都是通过调用Material类的API,通过关键字查找到材质球对应的属性,进行保存或修改。
初始化保存材质球方法:
cs
public static MaterialState SaveMaterialState(Material mat)
{
// 初始化材质状态结构体
MaterialState state = new MaterialState();
if (mat == null)
{
Debug.LogError("尝试保存空材质的状态");
return state;
}
if (mat.HasProperty("_Surface"))
{
state.surfaceType = mat.GetInt("_Surface");
}
if (mat.HasProperty("_Blend"))
{
state.blendMode = mat.GetInt("_Blend");
}
if (mat.HasProperty("_Cull"))
{
Debug.Log("当前渲染面:" + mat.GetInt("_Cull"));
state.renderFace = mat.GetInt("_Cull");
}
// 基础属性
state.renderTypeTag = mat.GetTag("RenderType", false, "");
state.renderQueue = mat.renderQueue;
// 基础颜色和纹理(URP标准属性)
state.albedoColor = mat.HasProperty("_BaseColor") ? mat.GetColor("_BaseColor") :
(mat.HasProperty("_Color") ? mat.GetColor("_Color") : Color.white);
state.albedoMap = mat.HasProperty("_BaseMap") ? mat.GetTexture("_BaseMap") :
(mat.HasProperty("_MainTex") ? mat.GetTexture("_MainTex") : null);
// 深度测试
if (mat.HasProperty("_ZTest"))
{
state.zTest = (CompareFunction)mat.GetInt("_ZTest");
}
if (mat.HasProperty("_ZWrite"))
{
state.zWrite = mat.GetInt("_ZWrite");
}
// URP PBR属性
state.metallic = mat.HasProperty("_Metallic") ? mat.GetFloat("_Metallic") : 0f;//金属度
state.smoothness = mat.HasProperty("_Smoothness") ? mat.GetFloat("_Smoothness") :
(mat.HasProperty("_Glossiness") ? mat.GetFloat("_Glossiness") : 0.5f);//光滑度
state.metallicSmoothnessMap = mat.HasProperty("_MetallicGlossMap") ?
mat.GetTexture("_MetallicGlossMap") : null;
//增强属性
state.normalMap = mat.HasProperty("_BumpMap") ? mat.GetTexture("_BumpMap") : null;
state.normalStrength = mat.HasProperty("_BumpScale") ? mat.GetFloat("_BumpScale"):0;
//自发光属性
state.isEmission = mat.IsKeywordEnabled("_EMISSION");
if (mat.HasProperty("_EmissionColor"))
{
state.emissionColor = mat.GetColor("_EmissionColor");
}
if (mat.HasProperty("_EmissionIntensity"))
{
state.emissionIntensity = mat.GetFloat("_EmissionIntensity");
}
else
{
// fallback: 使用颜色分量最大值
state.emissionIntensity = state.emissionColor.maxColorComponent;
}
state.emissionMap = mat.HasProperty("_EmissionMap") ? mat.GetTexture("_EmissionMap") : null;
state.emissionUseGlobalIllumination = (mat.globalIlluminationFlags &
MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
// 保存材质关键字状态(URP关键特性)
state.enabledKeywords = new List<string>(mat.shaderKeywords);
// 全局光照设置
state.globalIlluminationFlags = mat.globalIlluminationFlags;
return state;
}
恢复材质球初始状态(动态修改材质球)
cs
public static void RestoreMaterialState(Material mat, MaterialState state)
{
if (mat == null)
{
Debug.LogError("无法恢复空材质的状态");
return;
}
// 1. 恢复URP核心渲染模式(替代传统_Mode)
if (mat.HasProperty("_Surface"))
{
mat.SetInt("_Surface", state.surfaceType); // 表面类型(0=不透明,1=透明等)
}
if (mat.HasProperty("_Blend"))
{
mat.SetInt("_Blend", state.blendMode); // 混合模式
}
if (mat.HasProperty("_Cull"))
{
mat.SetInt("_Cull", state.renderFace);
}
// 2. 恢复渲染类型标签和队列
mat.SetOverrideTag("RenderType", state.renderTypeTag);
mat.renderQueue = state.renderQueue;
// 3. 恢复深度测试和写入设置(URP关键属性)
if (mat.HasProperty("_ZTest"))
{
mat.SetInt("_ZTest", (int)state.zTest); // 深度测试模式
}
if (mat.HasProperty("_ZWrite"))
{
mat.SetInt("_ZWrite", state.zWrite); // 深度写入
}
// 4. 恢复基础颜色和纹理 URP一般是_BaseColor
if (mat.HasProperty("_BaseColor"))
{
mat.SetColor("_BaseColor", state.albedoColor); // 基础颜色
}
else if (mat.HasProperty("_Color"))
{
mat.SetColor("_Color", state.albedoColor); // 兼容传统属性
}
if (mat.HasProperty("_BaseMap") && state.albedoMap != null)
{
mat.SetTexture("_BaseMap", state.albedoMap); // URP基础纹理
}
else if (mat.HasProperty("_MainTex") && state.albedoMap != null)
{
mat.SetTexture("_MainTex", state.albedoMap); // 兼容传统纹理
}
// 5. 恢复PBR属性(金属度/光滑度)
if (mat.HasProperty("_Metallic"))
{
mat.SetFloat("_Metallic", state.metallic);
}
if (mat.HasProperty("_Smoothness"))
{
mat.SetFloat("_Smoothness", state.smoothness);
}
else if (mat.HasProperty("_Glossiness"))
{
mat.SetFloat("_Glossiness", state.smoothness); // 兼容光泽度
}
if (mat.HasProperty("_MetallicGlossMap") && state.metallicSmoothnessMap != null)
{
mat.SetTexture("_MetallicGlossMap", state.metallicSmoothnessMap); // URP金属光滑度贴图
}
// 6.恢复增强属性
if (mat.HasProperty("_BumpMap") && state.normalMap != null)
{
mat.SetTexture("_BumpMap", state.normalMap);
}
if (mat.HasProperty("_BumpScale"))
{
mat.SetFloat("_BumpScale", state.normalStrength);
}
// 7. 恢复自发光属性
if (mat.HasProperty("_EmissionColor"))
{
mat.SetColor("_EmissionColor", state.emissionColor);
}
if (mat.HasProperty("_EmissionIntensity"))
{
mat.SetFloat("_EmissionIntensity", state.emissionIntensity); // URP单独强度控制
}
if (mat.HasProperty("_EmissionMap") && state.emissionMap != null)
{
mat.SetTexture("_EmissionMap", state.emissionMap);
}
// 恢复自发光关键字和全局光照影响
SetKeyword(mat, "_EMISSION", state.isEmission);
if (state.isEmission)
{
// 控制自发光是否影响全局光照
mat.globalIlluminationFlags = state.emissionUseGlobalIllumination
? state.globalIlluminationFlags & ~MaterialGlobalIlluminationFlags.EmissiveIsBlack
: state.globalIlluminationFlags | MaterialGlobalIlluminationFlags.EmissiveIsBlack;
}
// 8. 恢复全局光照设置
mat.globalIlluminationFlags = state.globalIlluminationFlags;
}
private static void SetKeyword(Material material, string keyword, bool state)
{
if (state)
{
material.EnableKeyword(keyword);
}
else
{
material.DisableKeyword(keyword);
}
}
我这里基本上只是选择了材质球里比较常用的属性举例,如果有别的需要修改的属性,可以修改一下结构体,找到那个属性对应的关键字,再根据需求添加就可以。