using System.Collections.Generic; using UnityEngine; using UnityEditor; public class ShaderReplacer : MonoBehaviour { [Header("目标对象")] public GameObject targetObject; [Header("Shader设置")] [SerializeField] private Shader opaqueShader = null; // 不透明shader [SerializeField] private Shader transparentShader = null; // 透明shader [Header("默认Shader名称")] [SerializeField] private string defaultOpaqueShaderName = "Universal Render Pipeline/Lit"; [SerializeField] private string defaultTransparentShaderName = "Universal Render Pipeline/Lit"; // 存储贴图对应的材质球,确保相同贴图只创建一个材质球 private Dictionary textureMaterialMap = new Dictionary(); [ContextMenu("替换所有子对象Shader")] public void ReplaceAllChildrenShaders() { if (targetObject == null) { Debug.LogError("目标对象未设置!"); return; } // 清空之前的映射 textureMaterialMap.Clear(); // 初始化默认shader InitializeDefaultShaders(); // 获取所有包含MeshRenderer的子对象 MeshRenderer[] meshRenderers = targetObject.GetComponentsInChildren(); Debug.Log($"找到 {meshRenderers.Length} 个MeshRenderer组件"); foreach (MeshRenderer meshRenderer in meshRenderers) { ProcessMeshRenderer(meshRenderer); } Debug.Log($"Shader替换完成!共创建了 {textureMaterialMap.Count} 个材质球"); #if UNITY_EDITOR // 在编辑模式下标记场景为已修改 if (!Application.isPlaying) { UnityEditor.EditorUtility.SetDirty(targetObject); UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty( UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()); } #endif } private void InitializeDefaultShaders() { // 如果没有手动设置shader,则使用默认的 if (opaqueShader == null) { opaqueShader = Shader.Find(defaultOpaqueShaderName); } if (transparentShader == null) { transparentShader = Shader.Find(defaultTransparentShaderName); } if (opaqueShader == null) { Debug.LogError($"找不到不透明Shader: {defaultOpaqueShaderName}"); } if (transparentShader == null) { Debug.LogError($"找不到透明Shader: {defaultTransparentShaderName}"); } } private void ProcessMeshRenderer(MeshRenderer meshRenderer) { // 使用 sharedMaterials 避免在编辑模式下创建材质实例 Material[] originalMaterials = meshRenderer.sharedMaterials; if (originalMaterials == null || originalMaterials.Length == 0) { Debug.LogWarning($"对象 {meshRenderer.gameObject.name} 没有材质球"); return; } Material[] newMaterials = new Material[originalMaterials.Length]; bool materialsChanged = false; for (int i = 0; i < originalMaterials.Length; i++) { Material originalMaterial = originalMaterials[i]; if (originalMaterial == null) { Debug.LogWarning($"对象 {meshRenderer.gameObject.name} 的材质球 {i} 为空"); newMaterials[i] = null; continue; } // 获取主贴图 Texture2D mainTexture = originalMaterial.mainTexture as Texture2D; if (mainTexture == null) { Debug.LogWarning($"对象 {meshRenderer.gameObject.name} 的材质球没有主贴图"); newMaterials[i] = originalMaterial; continue; } // 检查是否已经为这个贴图创建了材质球 if (textureMaterialMap.ContainsKey(mainTexture)) { newMaterials[i] = textureMaterialMap[mainTexture]; materialsChanged = true; Debug.Log($"复用材质球: {mainTexture.name}"); } else { // 创建新材质球 Material newMaterial = CreateMaterialForTexture(mainTexture, originalMaterial); if (newMaterial != null) { textureMaterialMap[mainTexture] = newMaterial; newMaterials[i] = newMaterial; materialsChanged = true; Debug.Log($"创建新材质球: {mainTexture.name}, 透明通道: {HasAlphaChannel(mainTexture)}"); } else { newMaterials[i] = originalMaterial; } } } // 只有在材质发生变化时才应用新材质球数组 if (materialsChanged) { meshRenderer.sharedMaterials = newMaterials; #if UNITY_EDITOR // 在编辑模式下标记对象为已修改 if (!Application.isPlaying) { UnityEditor.EditorUtility.SetDirty(meshRenderer); } #endif } } private Material CreateMaterialForTexture(Texture2D texture, Material originalMaterial) { bool hasAlpha = HasAlphaChannel(texture); Shader targetShader = hasAlpha ? transparentShader : opaqueShader; if (targetShader == null) { Debug.LogError($"目标Shader为空!透明: {hasAlpha}"); return null; } // 创建新材质球 Material newMaterial = new Material(targetShader); newMaterial.name = $"{texture.name}_{(hasAlpha ? "Transparent" : "Opaque")}"; // 复制原材质的属性 CopyMaterialProperties(originalMaterial, newMaterial); // 设置主贴图 newMaterial.mainTexture = texture; // 如果是透明材质,设置渲染模式 if (hasAlpha) { SetTransparentRenderingMode(newMaterial); } #if UNITY_EDITOR // 在编辑模式下,将材质保存为Asset if (!Application.isPlaying) { SaveMaterialAsAsset(newMaterial); } #endif return newMaterial; } #if UNITY_EDITOR private void SaveMaterialAsAsset(Material material) { // 创建Materials文件夹(如果不存在) string folderPath = "Assets/Materials/Generated"; if (!AssetDatabase.IsValidFolder(folderPath)) { if (!AssetDatabase.IsValidFolder("Assets/Materials")) { AssetDatabase.CreateFolder("Assets", "Materials"); } AssetDatabase.CreateFolder("Assets/Materials", "Generated"); } // 生成唯一的文件名 string materialPath = $"{folderPath}/{material.name}.mat"; materialPath = AssetDatabase.GenerateUniqueAssetPath(materialPath); // 保存材质为Asset AssetDatabase.CreateAsset(material, materialPath); AssetDatabase.SaveAssets(); Debug.Log($"材质已保存: {materialPath}"); } #endif private bool HasAlphaChannel(Texture2D texture) { if (texture == null) return false; // 检查贴图格式是否支持透明通道 TextureFormat format = texture.format; switch (format) { case TextureFormat.ARGB32: case TextureFormat.RGBA32: case TextureFormat.ARGB4444: case TextureFormat.RGBA4444: case TextureFormat.DXT5: case TextureFormat.BC7: case TextureFormat.ETC2_RGBA8: case TextureFormat.ASTC_4x4: case TextureFormat.ASTC_5x5: case TextureFormat.ASTC_6x6: case TextureFormat.ASTC_8x8: case TextureFormat.ASTC_10x10: case TextureFormat.ASTC_12x12: return true; default: return false; } } private void CopyMaterialProperties(Material source, Material target) { // 复制通用属性 if (source.HasProperty("_Color") && target.HasProperty("_Color")) { target.color = source.color; } if (source.HasProperty("_BaseColor") && target.HasProperty("_BaseColor")) { target.SetColor("_BaseColor", source.GetColor("_BaseColor")); } // 复制其他常用属性 string[] commonFloatProperties = { "_Metallic", "_Smoothness", "_BumpScale", "_OcclusionStrength", "_Cutoff", "_Surface", "_Blend", "_AlphaClip" }; foreach (string property in commonFloatProperties) { if (source.HasProperty(property) && target.HasProperty(property)) { target.SetFloat(property, source.GetFloat(property)); } } // 复制颜色属性 string[] colorProperties = { "_EmissionColor" }; foreach (string property in colorProperties) { if (source.HasProperty(property) && target.HasProperty(property)) { target.SetColor(property, source.GetColor(property)); } } // 复制贴图 string[] textureProperties = { "_BumpMap", "_MetallicGlossMap", "_OcclusionMap", "_EmissionMap" }; foreach (string texProperty in textureProperties) { if (source.HasProperty(texProperty) && target.HasProperty(texProperty)) { Texture tex = source.GetTexture(texProperty); if (tex != null) { target.SetTexture(texProperty, tex); } } } } private void SetTransparentRenderingMode(Material material) { // 设置URP透明渲染模式 if (material.HasProperty("_Surface")) { material.SetFloat("_Surface", 1); // Transparent } if (material.HasProperty("_Blend")) { material.SetFloat("_Blend", 0); // Alpha } // 设置渲染队列 material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; // 启用关键字 material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); material.EnableKeyword("_ALPHAPREMULTIPLY_ON"); } [ContextMenu("清理材质球映射")] public void ClearMaterialMapping() { textureMaterialMap.Clear(); Debug.Log("材质球映射已清理"); } // 在Inspector中显示当前映射信息 void OnValidate() { if (targetObject == null) { targetObject = gameObject; } } } #if UNITY_EDITOR [CustomEditor(typeof(ShaderReplacer))] public class ShaderReplacerEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); ShaderReplacer replacer = (ShaderReplacer)target; GUILayout.Space(10); if (GUILayout.Button("替换所有子对象Shader", GUILayout.Height(30))) { replacer.ReplaceAllChildrenShaders(); } if (GUILayout.Button("清理材质球映射")) { replacer.ClearMaterialMapping(); } GUILayout.Space(10); EditorGUILayout.HelpBox( "使用说明:\n" + "1. 设置目标对象(默认为当前对象)\n" + "2. 可选:设置自定义的不透明和透明Shader\n" + "3. 点击'替换所有子对象Shader'按钮执行替换\n" + "4. 脚本会自动检测贴图透明通道并选择合适的Shader\n" + "5. 相同贴图的对象会共享同一个材质球\n" + "6. 生成的材质球会保存到Assets/Materials/Generated文件夹", MessageType.Info ); GUILayout.Space(5); EditorGUILayout.HelpBox( "注意:在编辑模式下运行时,新创建的材质球会自动保存为Asset文件,避免材质泄漏。", MessageType.Warning ); } } #endif