using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using Object = UnityEngine.Object; public abstract class PostProcessor { /// /// 后处理命名后缀 /// public const string SCENE_POST_PROCESS_NAME = "PostProcess"; /// /// 合并网格后的节点 /// private const string SCENE_COMBINED_NAME = "Combined"; /// /// 后处理后存放地面贴图材质的文件夹命名 /// private const string GROUND_TEXTURE_MAT_PATH = "Ground"; /// /// 后处理后存放光照贴图和烘焙数据的文件夹命名 /// private const string LIGHT_BAKE_NAME = "LightMaps"; /// /// 后处理预制体根路径 /// protected const string PREFAB_POSTPROCESS_DIR = "Assets/Art_Out/AutoGen/PostProcessPrefabs"; /// /// 场景资源分组路径 暂时舍弃没有使用Addressable /// protected const string SCENE_ADDRESSABLE_GROUP = "PostProcessScenes"; /// /// 预制体资源分组路径 暂时舍弃没有使用Addressable /// protected const string PREFAB_ADDRESSABLE_GROUP = "PostProcessPrefabs"; /// /// 区分剧情场景的文件夹前缀 /// private 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; /// /// 烘焙后需要替换shader的渲染器列表 /// private readonly List needReplaceRenderers = new(); /// /// 烘焙后需要关闭产生阴影的渲染器列表 /// private readonly List allBakeRenderers = new(); /// /// 实时灯光 /// private readonly List realTimeLights = new(); /// /// 烘焙灯光 /// private readonly List bakedLights = new(); /// /// 检查规范节点 /// private readonly List sceneRoots = new() { "Main Camera", "TerrainGridSystem", "Art", "SceneManager", "MapEditor", "LevelEditor", "TGSPreview", "InputManager (Singleton)" }; /// /// 需要替换shader烘焙的节点 /// private readonly List bakeReplaceShaderRoot = new() { "CombinedRoot" }; /// /// 需要烘焙的其他地面节点 /// private readonly List groundRoot = new() { "Ground_2" }; /// /// 后处理文件夹路径 /// protected string ProcessedResourceDir => $"{ResourcesBaseDir}/{PostProcessFolderName}"; protected abstract string PostProcessFolderName { get; } protected abstract string ResourcesBaseDir { get; } protected abstract Object AssetToPostProcess { get; } protected abstract string OutputAssetPath { get; } private string LightBakeDir => $"{ProcessedResourceDir}/{LIGHT_BAKE_NAME}"; private string GroundSplitDir => $"{ProcessedResourceDir}/{GROUND_TEXTURE_MAT_PATH}"; /// /// 是否完成复制场景 /// private bool HasClone { get; set; } /// /// 原始场景路径 /// private string OriginAssetPath => AssetDatabase.GetAssetPath(AssetToPostProcess); protected PostProcessInstructor _processInstructor; private PostProcessInstructor _clonedProcessInstructor; private PostProcessInstructor PostProcessInstructor => HasClone ? _clonedProcessInstructor : _processInstructor; /// /// 用于处理后处理场景中的预制体 /// private Transform _unpackedCombinedObjectsRoot; private Transform CombinedObjectsRoot { get { if (_unpackedCombinedObjectsRoot != null && HasClone) return _unpackedCombinedObjectsRoot; if (PostProcessInstructor == null || PostProcessInstructor.combinedObjectsRoot == null) { Debug.LogError("找不到Art节点!"); return PostProcessInstructor.transform.parent; } return PostProcessInstructor.combinedObjectsRoot; } } protected abstract PostProcessInstructor Clone(); protected abstract void AfterAll(bool destroyAfterPostProcess); protected virtual void Init(PostProcessInstructor postProcessInstructor) { _processInstructor = postProcessInstructor; } public static PostProcessInstructor FindInstructorInScene(Scene scene) { var instructors = Object.FindObjectsOfType(); return instructors.FirstOrDefault(pi => pi.gameObject.scene == scene); } #region 后处理多节点 /// /// 后处理多节点场景 不烘焙 预制体不解包 /// /// /// public void PostProcessMore(PostProcessInstructor[] postProcessInstructors, bool destroyAfterPostProcess = false) { if (postProcessInstructors.Length <= 0) return; var firstPostProcessInstructor = postProcessInstructors[0]; if (!CheckPostProcessInstructor(firstPostProcessInstructor)) return; Init(firstPostProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); // 创建文件夹 AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); AssetDatabase.CreateFolder(ProcessedResourceDir, GROUND_TEXTURE_MAT_PATH); // 复制场景为后处理场景 Clone(); // 在后处理场景中重新获取 PostProcessInstructor 组 var newPostProcessInstructors = Object.FindObjectsOfType(); var index = 0; foreach (var postProcessInstructor in newPostProcessInstructors) { // DebugUtil.LogError($"重新获取的postProcessInstructor属于场景:{postProcessInstructor.gameObject.scene.name}"); _processInstructor = postProcessInstructor; if (PostProcessInstructor == null) { // DebugUtil.LogError($"PostProcessInstructor为空,克隆参数:{HasClone}"); continue; } var outputPath = SCENE_COMBINED_NAME + index++; BakeCombinedGameObject(outputPath); if (!PostProcessInstructor.enableSplitGround) { // 关闭切割地面 给地面添加BoxCollider GroundAddBoxCollider(); ChangeMapTextureAndMatPath(); } else { SplitGroundTexture(); } SetFogPosition(); RemoveRedundant(); HideCamera(); DestroyUselessObject(); } AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } #endregion #region 普通后处理 /// /// 后处理 /// /// /// /// 是否为默认相机 public void PostProcess(PostProcessInstructor postProcessInstructor, bool destroyAfterPostProcess = false, bool defaultCameraParam = true) { if (!CheckPostProcessInstructor(postProcessInstructor)) return; Init(postProcessInstructor); HasClone = false; EditorSceneManager.SaveOpenScenes(); PrepareFolders(); // 复制 ProcessInstructor 组件 _clonedProcessInstructor = Clone(); HasClone = true; if (_clonedProcessInstructor == null) { Debug.LogError("_clonedProcessInstructor is null!"); return; } var combineRootObj = BakeCombinedGameObject(); _PrepareForBake(combineRootObj.transform); // 如果Art为预制体先进行解包 UnpackPostSceneArt(); if (!PostProcessInstructor.enableSplitGround) { // 关闭切割地面 给地面添加BoxCollider GroundAddBoxCollider(); ChangeMapTextureAndMatPath(); } else { SplitGroundTexture(); } BakeLightmaps(); SetFogPosition(); if (!defaultCameraParam) { SetCameraParam(); } UnpackPostSceneArt(); RemoveRedundant(); HideCamera(); DestroyUselessObject(); AfterAll(destroyAfterPostProcess); PostProcessData.EditorInstance.AddPostProcessData(AssetToPostProcess, OutputAssetPath); Debug.Log($"执行后处理成功,后处理资源源路径:{OriginAssetPath} 后处理后路径:{OutputAssetPath}"); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); } #endregion private void _PrepareForBake(Transform combineRootParent) { if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking) return; for (var i = 0; i < combineRootParent.childCount; ++i) { var child = combineRootParent.GetChild(i); if (child == null) continue; var meshRenderer = child.GetComponent(); if (meshRenderer == null) { continue; } if (meshRenderer.shadowCastingMode == ShadowCastingMode.On) { meshRenderer.shadowCastingMode = ShadowCastingMode.On; meshRenderer.staticShadowCaster = true; meshRenderer.material.doubleSidedGI = true; } } } 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)) { Debug.LogWarning($"{currentScene.name} 场景有其他节点:{obj.name},请检查节点规范"); } } return true; } private void PrepareFolders() { AssetDatabase.DeleteAsset(ProcessedResourceDir); AssetDatabase.CreateFolder(ResourcesBaseDir, PostProcessFolderName); AssetDatabase.CreateFolder(ProcessedResourceDir, SCENE_COMBINED_NAME); AssetDatabase.CreateFolder(ProcessedResourceDir, GROUND_TEXTURE_MAT_PATH); } /// /// 合并网格、贴图 /// private GameObject BakeCombinedGameObject(string combinedName = "") { var art = CombinedObjectsRoot; var combinedRootName = string.IsNullOrEmpty(combinedName) ? SCENE_COMBINED_NAME : combinedName; var outputPath = $"{ProcessedResourceDir}/{combinedRootName}"; // 收集渲染器 var renderers = MeshCombineUtils.CollectRenderers(PostProcessInstructor.rootsToCombine); Debug.Log($"合批前收集到 {renderers.Count} 个渲染器"); var prefix = GetScenePrefix(); var combinedRoot = new GameObject("CombinedRoot"); combinedRoot.transform.parent = art.transform; DebugUtil.Log($"合并节点是:{GameObjectUtils.GetFullPath(combinedRoot)},输出路径:{outputPath}," + $"单位贴图尺寸:{(int)PostProcessInstructor.postProcessTextureUnitSize}"); try { if (PostProcessInstructor.enableBatchGrouping) { // 按照距离对渲染器进行分组 Dictionary> rendererGroups = MeshCombineUtils.GroupRenderersByProximity(renderers, PostProcessInstructor.batchGroupDistance); SceneCombineUtil.Instance.CombineGameObjectsByGroups(rendererGroups, combinedRoot.transform, outputPath, (int)PostProcessInstructor.postProcessTextureUnitSize, PostProcessInstructor.sampleMaterials, prefix); } else { // 使用原有的合批方式 SceneCombineUtil.Instance.CombineGameObjects(renderers, combinedRoot.transform, outputPath, (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 root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject); } } catch (System.Exception ex) { Debug.LogError($"合批过程中发生错误: {ex.Message}\n{ex.StackTrace}"); // 如果合批失败,保留原始物体 if (combinedRoot != null) { Object.DestroyImmediate(combinedRoot); } } return combinedRoot; } #region 地面处理 /// /// 修改地面贴图及材质的路径 用于打包 /// private void ChangeMapTextureAndMatPath() { var ground = PostProcessInstructor.ground; if (_unpackedCombinedObjectsRoot != null && HasClone) { ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } GroundUtils.ChangeMapTextureAndMatPath(ground, GroundSplitDir, GetScenePrefix(), groundRoot, CombinedObjectsRoot, _unpackedCombinedObjectsRoot); } /// /// 给地面添加BoxCollider /// private void GroundAddBoxCollider() { var ground = PostProcessInstructor.ground; if (_unpackedCombinedObjectsRoot != null && HasClone) { ground = _unpackedCombinedObjectsRoot.Find("Ground")?.gameObject; } if (ground != null) { // DebugUtil.LogError("获取的地面节点全路径:" + GameObjectUtils.GetFullPath(ground)); SceneUtils.AddBoxColliderToGround(ground); } else { Debug.LogError("场景地面节点不存在,无法添加BoxCollider"); } // 移除下层地面的碰撞体 var ground_2 = GameObject.Find("Ground_2"); if (ground_2 != null) { var collider = ground_2.GetComponent(); if (collider != null) { Object.DestroyImmediate(collider); } } } /// /// 切割地面 /// private void SplitGroundTexture() { var ground = PostProcessInstructor.ground; if (ground == null) return; GroundUtils.SplitGroundTexture(ground, GroundSplitDir, GetScenePrefix(), GROUND_SPLIT_WIDTH, GROUND_SPLIT_HEIGHT); } #endregion /// /// 设置雾的位置 /// private void SetFogPosition() { SceneUtils.SetFogPosition(PostProcessInstructor.fog); } /// /// 销毁多余eventSystems /// private void RemoveRedundant() { SceneUtils.RemoveRedundantEventSystems(PostProcessInstructor.gameObject.scene); } private void DestroyUselessObject() { foreach (var root in PostProcessInstructor.rootsToCombine) { if (!root) { continue; } Object.DestroyImmediate(root.gameObject, true); } Object.DestroyImmediate(PostProcessInstructor.gameObject, true); var designerObj = GameObject.Find("Designer"); if (designerObj != null) { Object.DestroyImmediate(designerObj, true); } } /// /// 检测是否为剧情场景 /// /// private bool IsStoryScene() { var 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; } private void HideCamera() { var scene = PostProcessInstructor.gameObject.scene; var cameras = Object.FindObjectsOfType(); foreach (var camera in cameras) { if (camera.gameObject.scene == scene) { camera.gameObject.SetActive(false); // Debug.Log($"已隐藏相机: {camera.gameObject.name}"); } } EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); EditorSceneManager.SaveOpenScenes(); } #endregion #region 烘焙 /// /// 烘焙灯光贴图的方法 /// private void BakeLightmaps() { // 检查是否开启了烘焙选项 if (PostProcessInstructor == null || !PostProcessInstructor.enableLightmapBaking) return; Debug.Log("开始灯光烘焙过程..."); // 1. 从CombinedObjectsRoot下查找灯光 改为烘焙灯光 LightmapUtils.PrepareLight(CombinedObjectsRoot, realTimeLights, bakedLights); // 2. 处理参与烘焙的节点 LightmapUtils.PreProcessBake(bakeReplaceShaderRoot, CombinedObjectsRoot, true, true, allBakeRenderers, needReplaceRenderers); LightmapUtils.PreProcessBake(groundRoot, CombinedObjectsRoot, false, false, allBakeRenderers, needReplaceRenderers); ProcessGround(3000); Debug.Log($"总共收集到 {needReplaceRenderers.Count} 个渲染器需要替换shader"); Debug.Log($"总共收集到 {allBakeRenderers.Count} 个渲染器参与烘焙"); // 3.替换Shader为支持光照贴图的Shader MaterialUtils.ReplaceShaderWithLightmapShader(needReplaceRenderers); // 4. 开始烘焙 LightmapUtils.Bake(SceneManager.GetActiveScene()); // 5. 关闭所有烘焙物体的阴影投射 foreach (var renderer in allBakeRenderers) { if (renderer != null) renderer.shadowCastingMode = ShadowCastingMode.Off; } // 6. 打开实时灯光 LightmapUtils.RevertLight(realTimeLights, bakedLights); // 7.还原地面RenderQueue ProcessGround(2000); // 8. 复制光照烘焙文件到目标路径 LightmapUtils.CopyLightmapFiles(SceneManager.GetActiveScene(), LightBakeDir); // 9. 保存场景 EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); EditorSceneManager.SaveOpenScenes(); Debug.Log("灯光烘焙处理完成"); } private void ProcessGround(int renderQueue) { if (PostProcessInstructor.ground != null) { SceneUtils.SetGroundRenderQueue(PostProcessInstructor.ground, renderQueue); } } private void UnpackPostSceneArt() { if (CombinedObjectsRoot != null && PrefabUtility.IsPartOfPrefabInstance(CombinedObjectsRoot.gameObject)) { // 保存对象的引用 _unpackedCombinedObjectsRoot = CombinedObjectsRoot; SceneUtils.UnpackPrefab(CombinedObjectsRoot.gameObject); } } #endregion }