在Unity开发中,因为渲染管线问题,经常会遇到需要大量更改shader的问题,本篇文章讲述批量更换Shader的工具
一、原理:
- 拿到场景内、指定文件夹内所有模型或指定模型
- 编辑获取其Renderer上的所有材质球
- 记录原材质的颜色及贴图
- 更换新的shader
- 将颜色与贴图赋值给新的shader
二、代码
- 代码功能:(其他地方可自行更改)
- 更换场景内所有物体的shader
- 目标Shader为URP管线
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
using Object = UnityEngine.Object;
public class BathShaderTool : EditorWindow
{
private Shader _targetShader;
private int _selectedShaderIndex = 0;
private static string[] _shaders = new string[]
{
// 可自定指定Shader名称
//"Universal Render Pipeline/Lit",
};
private string _folderPath = "Assets/YourFolderPath"; // 指定你的文件夹路径
[MenuItem("Tools/BathShaderTool")]
public static void ShowWindow()
{
if (_shaders.Length == 0)
{
LoadAllShaders();
}
GetWindow<BathShaderTool>();
}
private void OnGUI()
{
GUILayout.Label("BathShader", EditorStyles.boldLabel);
_folderPath = GUILayout.TextField(_folderPath);
_selectedShaderIndex = EditorGUILayout.Popup("Target Shader", _selectedShaderIndex, _shaders);
_targetShader = Shader.Find(_shaders[_selectedShaderIndex]);
if (GUILayout.Button("设置场景中所有对象的shader"))
{
if (_targetShader != null)
{
SetShaderForAllObjectsInScene(_targetShader);
}
else
{
Debug.LogWarning("Please select a shader.");
}
}
if (GUILayout.Button("设置文件夹中所有对象的shader"))
{
if (_targetShader != null)
{
SetShader<GameObject>("t:GameObject", _targetShader,
(mat, shader) => TraverseAndSetShader((GameObject)mat, shader));
}
else
{
Debug.LogWarning("Please select a shader.");
}
}
if (GUILayout.Button("设置文件夹中所有材质球的shader"))
{
if (_targetShader != null)
{
SetShader<Material>("t:Material", _targetShader,
(mat, shader) => SetShaderForMat((Material)mat, shader));
}
else
{
Debug.LogWarning("Please select a shader.");
}
}
}
/// <summary>
/// 设置场景中所有对象的Shader
/// </summary>
/// <param name="shader"></param>
private void SetShaderForAllObjectsInScene(Shader shader)
{
GameObject[] allObjects = SceneManager.GetActiveScene().GetRootGameObjects();
foreach (GameObject obj in allObjects)
{
TraverseAndSetShader(obj, shader);
}
Debug.Log("Shader has been set for all objects in the scene.");
}
/// <summary>
/// 遍历对象
/// </summary>
/// <param name="obj"></param>
/// <param name="shader"></param>
private void TraverseAndSetShader(GameObject obj, Shader shader)
{
SetShaderForAllRenderers(obj, shader);
foreach (Transform child in obj.transform)
{
TraverseAndSetShader(child.gameObject, shader);
}
}
/// <summary>
/// 遍历Renderer并设置Shader
/// </summary>
/// <param name="obj"></param>
/// <param name="shader"></param>
private void SetShaderForAllRenderers(GameObject obj, Shader shader)
{
Renderer[] renderers = obj.GetComponents<Renderer>();
foreach (Renderer renderer in renderers)
{
if (renderer != null)
{
foreach (Material material in renderer.sharedMaterials)
{
SetShaderForMat(material, shader);
}
}
}
}
/// <summary>
/// 设置材质球的Shader
/// </summary>
/// <param name="material"></param>
/// <param name="shader"></param>
private void SetShaderForMat(Material material, Shader shader)
{
if (material != null)
{
Color albedoColor = material.color;
Texture albedoTexture = material.mainTexture;
material.shader = shader;
material.color = albedoColor;
material.mainTexture = albedoTexture;
}
}
/// <summary>
/// 设置文件夹中所有指定类型的资源的Shader
/// </summary>
/// <param name="type"></param>
/// <param name="shader"></param>
/// <param name="action"></param>
/// <typeparam name="T"></typeparam>
private void SetShader<T>(string type,Shader shader,Action<object,Shader> action) where T : Object
{
string[] guids = AssetDatabase.FindAssets(type, new[] { _folderPath });
foreach (string guid in guids)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
T assetAt = AssetDatabase.LoadAssetAtPath<T>(assetPath);
if (assetAt != null)
{
action?.Invoke(assetAt,shader);
EditorUtility.SetDirty(assetAt);
}
}
AssetDatabase.SaveAssets();
}
/// <summary>
/// 加载所有Shader名称
/// </summary>
private static void LoadAllShaders()
{
var shaders = Resources.FindObjectsOfTypeAll<Shader>();
_shaders = new string[shaders.Length];
for (int i = 0; i < _shaders.Length; i++)
{
Debug.Log(shaders[i].name);
_shaders[i] = shaders[i].name;
}
}
}