using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using System.IO; using Object = UnityEngine.Object; public abstract class PostProcessor { public const string SCENE_POST_PROCESS_NAME = "PostProcess"; private const string SCENE_SURFACE_FOLDER_NAME = "SurfaceData"; private const string SCENE_COMBINED_NAME = "Combined"; private const string GROUND_SPLIT_NAME = "GroundSplit"; private const string LIGHT_BAKE_NAME = "LightMaps"; protected const string SCENE_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessScenes"; protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessPrefabs"; protected const string SURFACE_SHADER_PATH = "Assets/Code/Shaders/Scene/NLD_Scene_Surface.shader"; protected const string SCENE_ADDRESSABLE_GROUP = "PostProcessScenes"; protected const string PREFAB_ADDRESSABLE_GROUP = "PostProcessPrefabs"; // 区分剧情场景前缀 protected const string STORY_SCENE_PREFIX = "story_"; private const int GROUND_SPLIT_WIDTH = 1024; private const int GROUND_SPLIT_HEIGHT = 1024; private const int UPP = 100; // 存储渲染器和它们的原始材质映射 private Dictionary originalMaterials = new Dictionary(); // 存储需要进行材质恢复的渲染器列表 private List renderersToRestore = new List(); private readonly List sceneRoots = new() { "Main Camera", "TerrainGridSystem", "Art", "SceneManager", "MapEditor", "LevelEditor", "TGSPreview", "InputManager (Singleton)" }; /// /// 需要烘焙的节点 /// private readonly List bakeRoot = new() { "Trees", "Buildings", "CombinedRoot" }; /// /// 需要烘焙和关闭阴影的地面节点 /// private readonly List groundRoot = new() { "Ground_2" }; protected string ProcessedResourceDir => $"{ResourcesBaseDir}/{PostProcessFolderName}"; protected abstract string ResourcesBaseDir { get; } private string LightBakeDir => $"{ProcessedResourceDir}/{LIGHT_BAKE_NAME}"; private string SurfaceDir => $"{ProcessedResourceDir}/{SCENE_SURFACE_FOLDER_NAME}"; private string CombinedDir => $"{ProcessedResourceDir}/{SCENE_COMBINED_NAME}"; private string GroundSplitDir => $"{ProcessedResourceDir}/{GROUND_SPLIT_NAME}"; protected abstract string PostProcessFolderName { get; } protected PostProcessInstructor _processInstructor; private PostProcessInstructor _clonedProcessInstructor; //private NLDSceneManager NLDSceneManager { get; set; } //private NLDSceneManager _postProcessSceneManager; private bool HasClone { get; set; } protected abstract Object AssetToPostProcess { get; } protected string OriginAssetPath => AssetDatabase.GetAssetPath(AssetToPostProcess); protected abstract string OutputAssetPath { get; } private PostProcessInstructor PostProcessInstructor => HasClone ? _clonedProcessInstructor : _processInstructor; private Transform CombinedObjectsRoot { get { if (PostProcessInstructor == null || PostProcessInstructor.combinedObjectsRoot == null) { Debug.LogError("找不到Art节点!"); return PostProcessInstructor.transform.parent; } return PostProcessInstructor.combinedObjectsRoot; } } private bool CheckPostProcessInstructor(PostProcessInstructor postProcessInstructor) { if (postProcessInstructor == null) { Debug.LogError("postProcessInstructor is null"); return false; } if (postProcessInstructor.processPrefab && (!PrefabUtility.IsPartOfAnyPrefab(postProcessInstructor) || EditorUtility.IsPersistent(postProcessInstructor))) { Debug.LogError("postProcessInstructor不在任何预制体内或者其不是预制体实例"); return false; } var currentScene = postProcessInstructor.gameObject.scene; var objs = currentScene.GetRootGameObjects(); foreach (var obj in objs) { if (!sceneRoots.Contains(obj.name)) { DebugUtil.LogWarning("{1} 场景有其他节点:{0},请检查节点规范", obj.name, currentScene.name); } } return true; } protected virtual void Init(PostProcessInstructor postProcessInstructor) { _processInstructor = postProcessInstructor; } public void PostProcess(PostProcessInstructor postProcessInstructor, bool destroyAfterPostProcess = false, bool defaultCameraParam = true) { if (!CheckPostProcessInstructor(postProcessInstructor)) return; Init(postProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); PrepareFolders(); //NLDSceneManager = CommonUtils.GetSceneRootComponent(Scene); //暂时先不渲染成面片(地面阴影还没法解决,以及一些与笔刷工具的耦合) //BakeSurfaceData(); _clonedProcessInstructor = Clone(); HasClone = true; if (_clonedProcessInstructor == null) { Debug.LogError("_clonedProcessInstructor is null!"); return; } BakeCombinedGameObject(); BakeLightmaps(); // SplitGroundTexture(); SetFogPosition(); if (!defaultCameraParam) { SetCameraParam(); } RemoveRedundant(); DestroyUselessObject(); AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } protected abstract void AfterAll(bool destroyAfterPostProcess); public static PostProcessInstructor FindInstructorInScene(Scene scene) { var instructors = Object.FindObjectsOfType(); return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene); } // private void DeleteSurfaceData(SceneSurfaceData surfaceData) // { // var mat = AssetDatabase.LoadAssetAtPath(surfaceData.materialPath); // if (mat == null) // { // return; // } // // AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(mat.mainTexture)); // AssetDatabase.DeleteAsset(surfaceData.materialPath); // } private void PrepareFolders() { AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); AssetDatabase.CreateFolder(ProcessedResourceDir, SCENE_SURFACE_FOLDER_NAME); AssetDatabase.CreateFolder(ProcessedResourceDir, SCENE_COMBINED_NAME); AssetDatabase.CreateFolder(ProcessedResourceDir, GROUND_SPLIT_NAME); } protected abstract PostProcessInstructor Clone(); //烘焙面片数据 // private void BakeSurfaceData() // { // if (NLDSceneManager == null) // return; // NLDSceneManager.allSceneSurfaceData.Clear(); // List renderInfos = new(); // var camera = Camera.main; // if (camera == null) // { // Debug.LogError("找不到主相机!请设置主相机的tag"); // return; // } // // foreach (var sObj in NLDSceneManager.sceneObjects) // { // if (sObj && sObj.renderInfo && !renderInfos.Contains(sObj.renderInfo)) // { // var brush = SceneBrushHelper.GetSceneBrushByRenderInfo(sObj.renderInfo); // if (!brush.SupportSurface) // continue; // renderInfos.Add(sObj.renderInfo); // var gameObject = brush.prefab; // var texPath = // EditorUtil.GetAssetUniquePath($"{SurfaceDir}/{gameObject.name}_surface.jpg", out var name); // Vector3 offset = Vector3.zero; // Vector3 scale = Vector3.one; // Quaternion rotation = Quaternion.identity; // var texture = gameObject.RenderToTexture(camera, texPath, ref offset, ref scale, ref rotation); // if (texture != null) // { // var material = new Material(AssetDatabase.LoadAssetAtPath(SURFACE_SHADER_PATH)) // { // mainTexture = texture // }; // material.enableInstancing = true; // var matPath = $"{SurfaceDir}/{gameObject.name}_surface.mat".Replace("\\", "/"); // EditorUtil.CreateAnUniqueAsset(material, matPath); // SceneSurfaceData surfaceData = new() // { // renderInfo = sObj.renderInfo, // materialPath = matPath, // cachedOffset = offset, // eulerAngleID = gameObject.transform.eulerAngles, // cachedRotation = rotation.eulerAngles, // cachedScale = scale, // valid = true, // }; // NLDSceneManager.allSceneSurfaceData.Add(surfaceData); // } // } // } // // //MarkSceneDirty(); // } //合并网格,贴图 private void BakeCombinedGameObject() { var art = CombinedObjectsRoot; // 不再自动执行模型切割,由外部单独调用 // 收集渲染器 var renderers = CollectRenderers(); Debug.Log($"合批前收集到 {renderers.Count} 个渲染器"); var prefix = GetScenePrefix(); var combinedRoot = new GameObject("CombinedRoot"); combinedRoot.transform.parent = art.transform; try { if (PostProcessInstructor.enableBatchGrouping) { // 按照距离对渲染器进行分组 Dictionary> rendererGroups = GroupRenderersByCircularDistance(renderers, PostProcessInstructor.batchGroupDistance); SceneCombineUtil.Instance.CombineGameObjectsByGroups(rendererGroups, combinedRoot.transform, CombinedDir, (int)PostProcessInstructor.postProcessTextureUnitSize, PostProcessInstructor.sampleMaterials, prefix); } else { // 使用原有的合批方式 SceneCombineUtil.Instance.CombineGameObjects(renderers, combinedRoot.transform, CombinedDir, (int)PostProcessInstructor.postProcessTextureUnitSize, PostProcessInstructor.sampleMaterials, prefix); } // 如果是剧情场景,重命名生成的所有物体添加前缀 if (!string.IsNullOrEmpty(prefix)) { var combined = combinedRoot.GetComponentsInChildren(); foreach (var trans in combined) { if (trans != combinedRoot.transform && !trans.name.StartsWith(prefix)) { trans.name = prefix + trans.name; } } } foreach (var renderer in renderers) { if (renderer != null && renderer.gameObject != null) { Object.DestroyImmediate(renderer.gameObject); } } } catch (System.Exception ex) { Debug.LogError($"合批过程中发生错误: {ex.Message}\n{ex.StackTrace}"); // 如果合批失败,保留原始物体 if (combinedRoot != null) { Object.DestroyImmediate(combinedRoot); } } } // 根据距离对渲染器进行分组 private Dictionary> GroupRenderersByCircularDistance(List renderers, float maxDistance) { Dictionary> groups = new Dictionary>(); int groupIndex = 0; // 已分组的渲染器 HashSet processedRenderers = new HashSet(); // 对所有渲染器进行处理 while (processedRenderers.Count < renderers.Count) { // 找到第一个未处理的渲染器作为新组的起点 Renderer startRenderer = null; foreach (var renderer in renderers) { if (!processedRenderers.Contains(renderer)) { startRenderer = renderer; break; } } if (startRenderer == null) break; // 创建新组 List currentGroup = new List(); currentGroup.Add(startRenderer); processedRenderers.Add(startRenderer); bool addedNew = true; while (addedNew) { addedNew = false; // 计算当前组的中心点 Vector3 groupCenter = CalculateGroupCenter(currentGroup); // 查找距离中心点在阈值内的未处理渲染器 foreach (var renderer in renderers) { if (processedRenderers.Contains(renderer)) continue; float distance = Vector3.Distance(groupCenter, renderer.transform.position); if (distance <= maxDistance) { currentGroup.Add(renderer); processedRenderers.Add(renderer); addedNew = true; } } } // 保存当前组 groups.Add($"Group_{groupIndex}", currentGroup); groupIndex++; } Debug.Log($"基于圆形区域分组:将{renderers.Count}个渲染器分成{groups.Count}个组,半径为{maxDistance}"); return groups; } private Vector3 CalculateGroupCenter(List group) { if (group.Count == 0) return Vector3.zero; Vector3 sum = Vector3.zero; foreach (var renderer in group) { sum += renderer.transform.position; } return sum / group.Count; } // 原先基于顶点数的分组方法(保留作为备用) private Dictionary> GroupRenderers(List renderers, int groupSize) { Dictionary> groups = new Dictionary>(); int totalVertexCount = 0; int groupIndex = 0; List currentGroup = new List(); foreach (var renderer in renderers) { var meshFilter = renderer.GetComponent(); if (meshFilter == null || meshFilter.sharedMesh == null) continue; // 获取当前渲染器的顶点数 int vertexCount = meshFilter.sharedMesh.vertexCount; // 如果当前组顶点数已经超过限制或这个模型本身太大,创建新组 if ((totalVertexCount + vertexCount > groupSize && currentGroup.Count > 0) || vertexCount > groupSize) { // 保存当前组 if (currentGroup.Count > 0) { groups.Add($"Group_{groupIndex}", new List(currentGroup)); currentGroup.Clear(); totalVertexCount = 0; groupIndex++; } // 如果单个模型超过大小限制,单独放一组 if (vertexCount > groupSize) { groups.Add($"Group_{groupIndex}_LargeModel_{renderer.name}", new List { renderer }); groupIndex++; continue; } } // 添加到当前组 currentGroup.Add(renderer); totalVertexCount += vertexCount; } // 确保最后一组也被添加 if (currentGroup.Count > 0) { groups.Add($"Group_{groupIndex}", new List(currentGroup)); } Debug.Log($"分组合批:将{renderers.Count}个渲染器分成{groups.Count}个组,按每组约{groupSize}个顶点"); return groups; } private List CollectRenderers() { List allRenderers = new(); foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } var render = root.GetComponentsInChildren(); if (render is not { Length: > 0 }) { continue; } allRenderers.AddRange(render); } return allRenderers; } #region 地面切图 private void SplitGroundTexture() { var ground = PostProcessInstructor.ground; if (ground == null) return; var renderer = ground.GetComponent(); if (renderer == null) return; var mat = renderer.sharedMaterial; if (mat == null) return; var texture = mat.mainTexture as Texture2D; if (texture == null) return; var prefix = GetScenePrefix(); var groundParent = Object.Instantiate(ground, ground.transform.parent, true); groundParent.name = "Ground"; // 计算原始地面的大小 var originalScale = ground.transform.localScale; var originalWidth = originalScale.x; var originalHeight = originalScale.y; var originPos = -new Vector3(originalWidth / 2f, originalHeight / 2f, 0); groundParent.transform.localScale = Vector3.one; // 确保目录存在 if (!AssetDatabase.IsValidFolder(GroundSplitDir)) { var parentDir = Path.GetDirectoryName(GroundSplitDir); if (!AssetDatabase.IsValidFolder(parentDir)) { AssetDatabase.CreateFolder(Path.GetDirectoryName(parentDir), Path.GetFileName(parentDir)); } AssetDatabase.CreateFolder(parentDir, Path.GetFileName(GroundSplitDir)); } var textures = SplitTextureUtil.SplitTexture(texture, GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT, GroundSplitDir, prefix); // 计算每个分割块的大小(基于原始地面大小) var rowCount = textures.GetLength(0); var columnCount = textures.GetLength(1); var blockWidth = originalWidth / columnCount; var blockHeight = originalHeight / rowCount; for (int row = 0; row < rowCount; row++) { for (int column = 0; column < columnCount; column++) { var splitTexture = textures[row, column]; if (splitTexture == null) { Debug.LogError($"Failed to load split texture at row {row}, column {column}"); continue; } var splitGround = Object.Instantiate(ground, groundParent.transform, true); Object.DestroyImmediate(splitGround.GetComponent()); var splitName = $"{prefix}Ground_{row}_{column}"; splitGround.name = splitName; // 设置固定的quad大小,不随贴图尺寸变化 splitGround.transform.localScale = new Vector3(blockWidth, blockHeight, 1); // 创建并保存材质 var splitMat = Object.Instantiate(mat); splitMat.mainTexture = splitTexture; var matPath = $"{GroundSplitDir}/{splitName}.mat"; AssetDatabase.CreateAsset(splitMat, matPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // 重新加载保存后的材质 var savedMat = AssetDatabase.LoadAssetAtPath(matPath); if (savedMat == null) { Debug.LogError($"Failed to load saved material at {matPath}"); continue; } var meshRenderer = splitGround.GetComponent(); if (meshRenderer != null) { meshRenderer.sharedMaterial = savedMat; meshRenderer.shadowCastingMode = ShadowCastingMode.Off; } // 计算位置,使用固定的block大小 splitGround.transform.localPosition = originPos + new Vector3(column * blockWidth + blockWidth / 2f, row * blockHeight + blockHeight / 2f, 0); } } Object.DestroyImmediate(ground); Object.DestroyImmediate(groundParent.GetComponent()); Object.DestroyImmediate(groundParent.GetComponent()); var boxCollider = groundParent.gameObject.AddComponent(); boxCollider.size = new Vector3(200, 200, 1); boxCollider.center = new Vector3(0, 0, 0.5f); Object.DestroyImmediate(groundParent.GetComponent()); } #endregion private void SetFogPosition() { var fog = PostProcessInstructor.fog; if (fog == null) return; var transform = fog.transform; var position = transform.position; transform.position = new Vector3(position.x, position.y + 0.01f, position.z); } private void RemoveRedundant() { Scene postScene = PostProcessInstructor.gameObject.scene; foreach (GameObject rootObj in postScene.GetRootGameObjects()) { EventSystem[] eventSystems = rootObj.GetComponentsInChildren(); if (null == eventSystems) continue; for (int i = 0; i < eventSystems.Length; ++i) { Object.DestroyImmediate(eventSystems[i].gameObject); } } } private void DestroyUselessObject() { foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject, true); } Object.DestroyImmediate(PostProcessInstructor.gameObject, true); } private bool IsStoryScene() { string sceneName = PostProcessInstructor.gameObject.scene.name; return sceneName.Contains(STORY_SCENE_PREFIX); } private string GetScenePrefix() { return IsStoryScene() ? STORY_SCENE_PREFIX : ""; } #region 保存相机参数 private void SetCameraParam() { var postScene = PostProcessInstructor.gameObject.scene; var scene = _processInstructor.gameObject.scene; foreach (var rootObj in scene.GetRootGameObjects()) { if (rootObj.CompareTag("MainCamera")) { CloneCameraToScene(rootObj, postScene); break; } } } private void CloneCameraToScene(GameObject obj, Scene scene) { // 只更改transform var oldPosition = obj.transform.position; var oldRotation = obj.transform.rotation; GameObject oldCamera = null; foreach (var rootObj in scene.GetRootGameObjects()) { if (rootObj.CompareTag("MainCamera")) { oldCamera = rootObj; break; } } if (oldCamera == null) return; oldCamera.transform.position = oldPosition; oldCamera.transform.rotation = oldRotation; } #endregion #region 烘焙 /// /// 烘焙灯光贴图的方法 /// private void BakeLightmaps() { // 检查是否开启了烘焙选项 if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking) return; Debug.Log("开始灯光烘焙过程..."); // 清空旧的材质映射 originalMaterials.Clear(); renderersToRestore.Clear(); // 1. 从CombinedObjectsRoot下查找灯光,分为烘焙灯光和实时灯光 var bakeLights = new List(); var realtimeLights = new List(); var lights = CombinedObjectsRoot.GetComponentsInChildren(); foreach (var light in lights) { // 跳过相机上的灯光,避免重复 if (light.GetComponentInParent() != null) continue; // 复制灯光对象 var realtimeLightObj = Object.Instantiate(light.gameObject, light.transform.parent); var realtimeLight = realtimeLightObj.GetComponent(); // 设置为烘焙和实时模式 light.gameObject.name += "_Bake"; light.lightmapBakeType = LightmapBakeType.Baked; realtimeLight.lightmapBakeType = LightmapBakeType.Realtime; // 取消烘焙灯光的static GameObjectUtility.SetStaticEditorFlags(light.gameObject, 0); // 添加到列表 bakeLights.Add(light); break; } // 查找并关闭所有实时灯光 var allSceneLights = Object.FindObjectsOfType(); foreach (var light in allSceneLights) { // 只处理实时灯光 if (light.lightmapBakeType == LightmapBakeType.Realtime) { if (light.gameObject.activeSelf) { light.gameObject.SetActive(false); realtimeLights.Add(light); } } } // 3. 收集指定节点下需要烘焙的渲染器 var bakedRenderers = new List(); // 处理bakeRoot中的渲染器,从CombinedObjectsRoot下查找 foreach (var rootName in bakeRoot) { var rootObj = FindChildRecursive(CombinedObjectsRoot, rootName); if (rootObj != null) { var renderers = rootObj.GetComponentsInChildren(true); if (renderers != null && renderers.Length > 0) { Debug.Log($"从{rootName}收集到 {renderers.Length} 个渲染器用于烘焙"); foreach (var renderer in renderers) { if (renderer.receiveShadows) { // 保证物体阴影打开 renderer.shadowCastingMode = ShadowCastingMode.On; bakedRenderers.Add(renderer); // 设置静态标志 GameObjectUtility.SetStaticEditorFlags(renderer.gameObject, StaticEditorFlags.ContributeGI | StaticEditorFlags.ReflectionProbeStatic); } } } } } // 处理groundRoot中的渲染器,从CombinedObjectsRoot下查找 foreach (var rootName in groundRoot) { var rootObj = FindChildRecursive(CombinedObjectsRoot, rootName); if (rootObj != null) { var renderers = rootObj.GetComponentsInChildren(true); if (renderers != null && renderers.Length > 0) { Debug.Log($"从{rootName}收集到 {renderers.Length} 个地面渲染器用于烘焙"); foreach (var renderer in renderers) { renderer.shadowCastingMode = ShadowCastingMode.Off; GameObjectUtility.SetStaticEditorFlags(renderer.gameObject, StaticEditorFlags.ContributeGI | StaticEditorFlags.ReflectionProbeStatic); DebugUtil.Log("groundRoot渲染器已设置静态标志"); } } } } // 处理 PostProcessInstructor.ground 指定的渲染器 if (PostProcessInstructor.ground != null) { var renderer = PostProcessInstructor.ground.GetComponent(); if (renderer != null) { renderer.shadowCastingMode = ShadowCastingMode.Off; // 设置静态标志 GameObjectUtility.SetStaticEditorFlags(renderer.gameObject, StaticEditorFlags.ContributeGI | StaticEditorFlags.ReflectionProbeStatic); DebugUtil.Log("PostProcessInstructor.ground渲染器已设置静态标志"); // 将PostProcessInstructor.ground的RenderQueue改为3000 var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { if (materials[i] != null) { renderer.shadowCastingMode = ShadowCastingMode.Off; materials[i].renderQueue = 3000; Debug.Log($"将PostProcessInstructor.ground的材质 {materials[i].name} 的RenderQueue从3000改回2000"); } } } } Debug.Log($"总共收集到 {bakedRenderers.Count} 个渲染器用于烘焙"); // 替换Shader为支持光照贴图的Shader ReplaceShaderWithLightmapShader(bakedRenderers); // 4. 设置烘焙参数并开始烘焙 var lightingSettings = new LightingSettings(); // 获取当前场景的烘焙设置 var activeScene = SceneManager.GetActiveScene(); if (Lightmapping.TryGetLightingSettings(out var existingSettings)) { lightingSettings = existingSettings; } else { // 如果没有现有设置,创建新的并分配给场景 lightingSettings.name = $"{activeScene.name}_LightingSettings"; AssetDatabase.CreateAsset(lightingSettings, $"Assets/{lightingSettings.name}.asset"); Lightmapping.lightingSettings = lightingSettings; } // 设置烘焙参数 lightingSettings.lightmapper = LightingSettings.Lightmapper.ProgressiveGPU; lightingSettings.directSampleCount = 1; lightingSettings.indirectSampleCount = 1; lightingSettings.environmentSampleCount = 1; lightingSettings.lightProbeSampleCountMultiplier = 8; lightingSettings.maxBounces = 1; lightingSettings.lightmapResolution = 40; lightingSettings.lightmapPadding = 2; lightingSettings.lightmapMaxSize = 1024; lightingSettings.lightmapCompression = LightmapCompression.HighQuality; lightingSettings.filteringMode = LightingSettings.FilterMode.Auto; Lightmapping.lightingSettings = lightingSettings; // 强制场景保存以确保设置生效 EditorSceneManager.MarkSceneDirty(activeScene); EditorSceneManager.SaveOpenScenes(); Debug.Log("开始烘焙灯光..."); // 5. 开始烘焙 Lightmapping.Bake(); Debug.Log("灯光烘焙完成"); // 6. 关闭所有烘焙物体的阴影投射 foreach (var renderer in bakedRenderers) { if (renderer != null) renderer.shadowCastingMode = ShadowCastingMode.Off; } // 7. 删除烘焙灯光,打开实时灯光 foreach (var light in bakeLights) { if (light != null) Object.DestroyImmediate(light.gameObject); } foreach (var light in realtimeLights) { if (light != null) light.gameObject.SetActive(true); } // 8. 恢复非地面物体的原始材质 RestoreOriginalMaterials(); // 9. 只将PostProcessInstructor.ground的RenderQueue从3000改回2000 if (PostProcessInstructor.ground != null) { var renderer = PostProcessInstructor.ground.GetComponent(); if (renderer != null) { var materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { if (materials[i] != null) { materials[i].renderQueue = 2000; Debug.Log($"将PostProcessInstructor.ground的材质 {materials[i].name} 的RenderQueue从3000改回2000"); } } } } // 刷新资源以确保RenderQueue修改已保存 AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // 10. 复制光照烘焙文件到目标路径 CopyLightmapFiles(activeScene); // 11. 保存场景 EditorSceneManager.MarkSceneDirty(activeScene); EditorSceneManager.SaveOpenScenes(); Debug.Log("灯光烘焙处理完成"); } /// /// 移动光照烘焙相关文件到指定目录 /// private void CopyLightmapFiles(Scene scene) { string sceneName = scene.name; Debug.Log("开始移动光照烘焙文件..."); try { // 确保目标目录存在 string lightmapDirAssetPath = GetAssetPath(LightBakeDir); if (!AssetDatabase.IsValidFolder(lightmapDirAssetPath)) { // 创建父目录结构 string[] folders = lightmapDirAssetPath.Split('/'); string currentPath = folders[0]; // Assets for (int i = 1; i < folders.Length; i++) { string folderName = folders[i]; string parentPath = currentPath; currentPath = $"{currentPath}/{folderName}"; if (!AssetDatabase.IsValidFolder(currentPath)) { AssetDatabase.CreateFolder(parentPath, folderName); } } AssetDatabase.Refresh(); } // 1. 移动LightingSettings.asset文件 string lightingSettingsPath = $"Assets/{sceneName}_LightingSettings.asset"; if (AssetDatabase.LoadAssetAtPath(lightingSettingsPath) != null) { string destPath = $"{lightmapDirAssetPath}/{sceneName}_LightingSettings.asset"; // 如果目标已存在,先删除 if (AssetDatabase.LoadAssetAtPath(destPath) != null) { AssetDatabase.DeleteAsset(destPath); } AssetDatabase.MoveAsset(lightingSettingsPath, destPath); Debug.Log($"已移动: {lightingSettingsPath} -> {destPath}"); } // 2. 移动整个光照目录 string lightmapDirName = sceneName; string lightmapSourceDir = $"{Path.GetDirectoryName(scene.path)}/{lightmapDirName}"; string lightmapSourceDirAssetPath = GetAssetPath(lightmapSourceDir); if (AssetDatabase.IsValidFolder(lightmapSourceDirAssetPath)) { string destLightmapDir = $"{lightmapDirAssetPath}/{lightmapDirName}"; // 如果目标已存在,先删除 if (AssetDatabase.IsValidFolder(destLightmapDir)) { AssetDatabase.DeleteAsset(destLightmapDir); AssetDatabase.Refresh(); } // 直接移动整个目录 string result = AssetDatabase.MoveAsset(lightmapSourceDirAssetPath, destLightmapDir); if (string.IsNullOrEmpty(result)) { Debug.Log($"已成功移动光照数据目录: {lightmapSourceDirAssetPath} -> {destLightmapDir}"); } else { Debug.LogError($"移动光照数据目录失败: {result}"); } } else { Debug.LogWarning($"找不到光照数据目录: {lightmapSourceDirAssetPath}"); } } catch (System.Exception e) { Debug.LogError($"移动光照文件时出错: {e.Message}"); } AssetDatabase.Refresh(); Debug.Log("光照烘焙文件移动完成"); } // 获取Unity资源路径的简化方法 private string GetAssetPath(string path) { // 已经是Assets路径格式 if (path.StartsWith("Assets")) return path; // 绝对路径转相对Assets路径 string fullPath = Path.GetFullPath(path); string assetsFullPath = Path.GetFullPath(Application.dataPath); if (fullPath.StartsWith(assetsFullPath)) return "Assets" + fullPath.Substring(assetsFullPath.Length).Replace('\\', '/'); return path; } // 替换材质的Shader为光照贴图支持的Shader private void ReplaceShaderWithLightmapShader(List renderers) { // 清空旧的材质映射 originalMaterials.Clear(); renderersToRestore.Clear(); // 追踪已处理的材质和它们的新版本 Dictionary processedMaterialMap = new Dictionary(); // 尝试查找光照贴图Shader var lightmapShader = Shader.Find("NLD_URP/NLD_Scene_Lightmap"); if (lightmapShader == null) { Debug.LogError("找不到NLD_Scene_Lightmap着色器,请确保它存在于项目中"); return; } int replacedCount = 0; HashSet processedMaterials = new HashSet(); // 避免处理重复材质 Debug.Log($"处理 {renderers.Count} 个渲染器用于Shader替换"); foreach (var renderer in renderers) { if (renderer == null) continue; // 跳过地面渲染器(它们将使用地面专用的shader) bool isGroundRenderer = false; Transform parent = renderer.transform.parent; while (parent != null) { if (groundRoot.Contains(parent.name) || (PostProcessInstructor.ground != null && parent == PostProcessInstructor.ground.transform)) { isGroundRenderer = true; break; } parent = parent.parent; } if (isGroundRenderer) { Debug.Log($"跳过地面渲染器: {renderer.name}"); continue; } // 获取渲染器的所有材质 Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) continue; // 保存原始材质数组 Material[] originalMaterialsArray = new Material[sharedMaterials.Length]; Array.Copy(sharedMaterials, originalMaterialsArray, sharedMaterials.Length); originalMaterials[renderer] = originalMaterialsArray; // 添加到需要恢复的渲染器列表 renderersToRestore.Add(renderer); // 创建新的材质数组,用于替换 Material[] newMaterials = new Material[sharedMaterials.Length]; for (int i = 0; i < sharedMaterials.Length; i++) { Material originalMaterial = sharedMaterials[i]; if (originalMaterial == null) { newMaterials[i] = null; continue; } // 如果已经处理过这个材质,使用已经替换好的版本 if (processedMaterialMap.TryGetValue(originalMaterial, out Material replacedMaterial)) { newMaterials[i] = replacedMaterial; continue; } processedMaterials.Add(originalMaterial); // 创建材质实例,避免修改原始共享材质 Material instanceMaterial = new Material(originalMaterial); if (instanceMaterial.shader != lightmapShader) { // 记录原始shader string originalShaderName = instanceMaterial.shader != null ? instanceMaterial.shader.name : "未知"; // 保存原始主贴图及其Scale/Offset Texture mainTexture = null; Vector2 texScale = Vector2.one; Vector2 texOffset = Vector2.zero; // 检查不同的可能贴图属性名 if (instanceMaterial.HasProperty("_MainTex")) { mainTexture = instanceMaterial.GetTexture("_MainTex"); texScale = instanceMaterial.GetTextureScale("_MainTex"); texOffset = instanceMaterial.GetTextureOffset("_MainTex"); Debug.Log( $"从材质 {instanceMaterial.name} 的 _MainTex 找到主贴图, Scale:{texScale}, Offset:{texOffset}"); } else if (instanceMaterial.HasProperty("_BaseMap")) { mainTexture = instanceMaterial.GetTexture("_BaseMap"); texScale = instanceMaterial.GetTextureScale("_BaseMap"); texOffset = instanceMaterial.GetTextureOffset("_BaseMap"); Debug.Log( $"从材质 {instanceMaterial.name} 的 _BaseMap 找到主贴图, Scale:{texScale}, Offset:{texOffset}"); } else if (instanceMaterial.HasProperty("_AlbedoMap")) { mainTexture = instanceMaterial.GetTexture("_AlbedoMap"); texScale = instanceMaterial.GetTextureScale("_AlbedoMap"); texOffset = instanceMaterial.GetTextureOffset("_AlbedoMap"); Debug.Log( $"从材质 {instanceMaterial.name} 的 _AlbedoMap 找到主贴图, Scale:{texScale}, Offset:{texOffset}"); } else if (instanceMaterial.HasProperty("_DiffuseMap")) { mainTexture = instanceMaterial.GetTexture("_DiffuseMap"); texScale = instanceMaterial.GetTextureScale("_DiffuseMap"); texOffset = instanceMaterial.GetTextureOffset("_DiffuseMap"); Debug.Log( $"从材质 {instanceMaterial.name} 的 _DiffuseMap 找到主贴图, Scale:{texScale}, Offset:{texOffset}"); } // 记录原始颜色 Color baseColor = Color.white; if (instanceMaterial.HasProperty("_Color")) { baseColor = instanceMaterial.GetColor("_Color"); } else if (instanceMaterial.HasProperty("_BaseColor")) { baseColor = instanceMaterial.GetColor("_BaseColor"); } // 应用新Shader instanceMaterial.shader = lightmapShader; // 恢复主贴图到_BaseMap (这是Lightmap shader使用的属性名) if (mainTexture != null) { if (instanceMaterial.HasProperty("_BaseMap")) { instanceMaterial.SetTexture("_BaseMap", mainTexture); instanceMaterial.SetTextureScale("_BaseMap", texScale); instanceMaterial.SetTextureOffset("_BaseMap", texOffset); Debug.Log( $"将主贴图设置到 {instanceMaterial.name} 的 _BaseMap, 并设置Scale:{texScale}, Offset:{texOffset}"); } // 同时尝试设置旧属性以兼容 if (instanceMaterial.HasProperty("_MainTex")) { instanceMaterial.SetTexture("_MainTex", mainTexture); instanceMaterial.SetTextureScale("_MainTex", texScale); instanceMaterial.SetTextureOffset("_MainTex", texOffset); } } else { Debug.LogWarning($"材质 {instanceMaterial.name} 没有找到可用的主贴图"); } // 设置颜色 if (instanceMaterial.HasProperty("_BaseColor")) { instanceMaterial.SetColor("_BaseColor", baseColor); } if (instanceMaterial.HasProperty("_Color")) { instanceMaterial.SetColor("_Color", baseColor); } replacedCount++; Debug.Log($"已替换材质 {instanceMaterial.name} 的Shader: {originalShaderName} -> NLD_Scene_Lightmap"); } // 将新材质添加到映射表中 processedMaterialMap[originalMaterial] = instanceMaterial; newMaterials[i] = instanceMaterial; } // 应用新材质数组 renderer.sharedMaterials = newMaterials; } Debug.Log($"完成Shader替换,共处理了 {replacedCount} 个材质"); } // 恢复原始材质(地面材质除外) private void RestoreOriginalMaterials() { Debug.Log("开始恢复非地面原始材质..."); int restoredCount = 0; // 遍历所有需要恢复的渲染器 foreach (var renderer in renderersToRestore) { if (renderer == null || !originalMaterials.ContainsKey(renderer)) continue; // 检查是否是地面渲染器,地面渲染器的材质不需要恢复 bool isGroundRenderer = false; Transform parent = renderer.transform.parent; while (parent != null) { if (groundRoot.Contains(parent.name) || (PostProcessInstructor.ground != null && (parent == PostProcessInstructor.ground.transform || renderer.gameObject == PostProcessInstructor.ground))) { isGroundRenderer = true; Debug.Log($"跳过地面渲染器材质恢复: {renderer.name}"); break; } parent = parent.parent; } if (isGroundRenderer) continue; // 获取原始材质数组 Material[] originalMaterialsArray = originalMaterials[renderer]; // 恢复原始材质 renderer.sharedMaterials = originalMaterialsArray; restoredCount++; } Debug.Log($"已恢复 {restoredCount} 个非地面渲染器的原始材质"); // 清空材质映射和渲染器列表,释放内存 originalMaterials.Clear(); renderersToRestore.Clear(); } // 递归查找子节点的辅助方法 private Transform FindChildRecursive(Transform parent, string childName) { // 首先检查直接子节点 Transform child = parent.Find(childName); if (child != null) return child; // 如果直接子节点中没找到,递归查找每个子节点 for (int i = 0; i < parent.childCount; i++) { child = FindChildRecursive(parent.GetChild(i), childName); if (child != null) return child; } return null; } #endregion }