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