Unity URP渲染管线动态修改材质球状态

最近工作过程中,有遇到需要动态修改模型材质球的情况,然后发现这点内置管线和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);
        }
    }

我这里基本上只是选择了材质球里比较常用的属性举例,如果有别的需要修改的属性,可以修改一下结构体,找到那个属性对应的关键字,再根据需求添加就可以。