2025-04-25 15:07:22 +08:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
2026-01-13 13:20:29 +08:00
|
|
|
using PhxhSDK;
|
2025-04-25 15:07:22 +08:00
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEditor;
|
|
|
|
|
using UnityEngine.SceneManagement;
|
|
|
|
|
using UnityEditor.SceneManagement;
|
|
|
|
|
using UnityEngine.Rendering;
|
2026-01-13 13:20:29 +08:00
|
|
|
using UnityEditorGameObjectUtility = UnityEditor.GameObjectUtility;
|
2025-04-25 15:07:22 +08:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 灯光烘焙工具类
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static class LightmapUtils
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 处理参与烘焙的节点
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void PreProcessBake(List<string> rootNames, Transform rootTransform, bool castShadow, bool needReplaceShader,
|
|
|
|
|
List<Renderer> allBakeRenderers, List<Renderer> needReplaceRenderers)
|
|
|
|
|
{
|
|
|
|
|
foreach (var rootName in rootNames)
|
|
|
|
|
{
|
2026-01-13 13:20:29 +08:00
|
|
|
var rootObj = rootTransform.FindChildRecursive(rootName);
|
2025-04-25 15:07:22 +08:00
|
|
|
if (rootObj != null)
|
|
|
|
|
{
|
|
|
|
|
SceneUtils.SetStaticFlagsRecursive(rootObj.gameObject, StaticEditorFlags.ContributeGI);
|
|
|
|
|
var renderers = rootObj.GetComponentsInChildren<Renderer>(true);
|
|
|
|
|
if (renderers != null && renderers.Length > 0)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log($"从{rootName}收集到 {renderers.Length} 个渲染器用于烘焙");
|
|
|
|
|
foreach (var renderer in renderers)
|
|
|
|
|
{
|
|
|
|
|
Debug.Log($"处理从{rootName}收集到物体{renderer.gameObject.name}的渲染器");
|
|
|
|
|
if (renderer.receiveShadows)
|
|
|
|
|
{
|
|
|
|
|
renderer.shadowCastingMode = castShadow ? ShadowCastingMode.On : ShadowCastingMode.Off;
|
|
|
|
|
allBakeRenderers.Add(renderer);
|
|
|
|
|
if (needReplaceShader)
|
|
|
|
|
needReplaceRenderers.Add(renderer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.Log($"从{rootName}收集不到渲染器");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 准备灯光:将实时灯光改为烘焙灯光
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void PrepareLight(Transform root, List<Light> realTimeLights, List<Light> bakedLights)
|
|
|
|
|
{
|
|
|
|
|
var lights = root.GetComponentsInChildren<Light>();
|
|
|
|
|
foreach (var light in lights)
|
|
|
|
|
{
|
|
|
|
|
light.transform.position = new Vector3(0, 0, 0);
|
|
|
|
|
// 复制灯光对象
|
|
|
|
|
var bakeLightObj = Object.Instantiate(light.gameObject, light.transform.parent);
|
|
|
|
|
var bakeLight = bakeLightObj.GetComponent<Light>();
|
|
|
|
|
|
|
|
|
|
// 设置为烘焙模式
|
|
|
|
|
bakeLightObj.name = light.name + "_Bake";
|
|
|
|
|
bakeLight.lightmapBakeType = LightmapBakeType.Baked;
|
|
|
|
|
|
|
|
|
|
// 取消实时灯光的static
|
2026-01-13 13:20:29 +08:00
|
|
|
UnityEditorGameObjectUtility.SetStaticEditorFlags(light.gameObject, 0);
|
2025-04-25 15:07:22 +08:00
|
|
|
// 设置烘焙灯光的static
|
2026-01-13 13:20:29 +08:00
|
|
|
UnityEditorGameObjectUtility.SetStaticEditorFlags(bakeLightObj, StaticEditorFlags.ContributeGI);
|
2025-04-25 15:07:22 +08:00
|
|
|
|
|
|
|
|
// 添加到列表
|
|
|
|
|
bakedLights.Add(bakeLight);
|
|
|
|
|
realTimeLights.Add(light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 关闭实时灯光
|
|
|
|
|
foreach (var realTimeLight in realTimeLights)
|
|
|
|
|
{
|
|
|
|
|
if (realTimeLight != null)
|
|
|
|
|
realTimeLight.gameObject.SetActive(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 开启烘焙灯光
|
|
|
|
|
foreach (var bakeLight in bakedLights)
|
|
|
|
|
{
|
|
|
|
|
if (bakeLight != null)
|
|
|
|
|
bakeLight.gameObject.SetActive(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 恢复实时灯光
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void RevertLight(List<Light> realTimeLights, List<Light> bakedLights)
|
|
|
|
|
{
|
|
|
|
|
// 开启实时灯光
|
|
|
|
|
foreach (var realTimeLight in realTimeLights)
|
|
|
|
|
{
|
|
|
|
|
if (realTimeLight != null)
|
|
|
|
|
realTimeLight.gameObject.SetActive(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Debug.Log($"销毁{bakedLights.Count}个烘焙灯光");
|
|
|
|
|
// 销毁烘焙灯光
|
|
|
|
|
foreach (var bakeLight in bakedLights)
|
|
|
|
|
{
|
|
|
|
|
if (bakeLight != null)
|
|
|
|
|
Object.DestroyImmediate(bakeLight.gameObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 执行烘焙
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void Bake(Scene activeScene)
|
|
|
|
|
{
|
|
|
|
|
// 设置烘焙参数并开始烘焙
|
|
|
|
|
var lightingSettings = new LightingSettings();
|
|
|
|
|
|
|
|
|
|
Debug.Log($"烘焙时 场景是{activeScene.name}");
|
|
|
|
|
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.mixedBakeMode = MixedLightingMode.Subtractive;
|
|
|
|
|
lightingSettings.lightmapper = LightingSettings.Lightmapper.ProgressiveGPU;
|
|
|
|
|
lightingSettings.directSampleCount = 1;
|
|
|
|
|
lightingSettings.indirectSampleCount = 1;
|
|
|
|
|
lightingSettings.environmentSampleCount = 1;
|
|
|
|
|
lightingSettings.lightProbeSampleCountMultiplier = 8;
|
|
|
|
|
lightingSettings.maxBounces = 1;
|
|
|
|
|
lightingSettings.filteringMode = LightingSettings.FilterMode.Auto;
|
|
|
|
|
lightingSettings.lightmapResolution = 40;
|
|
|
|
|
lightingSettings.lightmapPadding = 2;
|
|
|
|
|
lightingSettings.lightmapMaxSize = 1024;
|
|
|
|
|
lightingSettings.lightmapCompression = LightmapCompression.HighQuality;
|
2025-05-15 19:27:10 +08:00
|
|
|
lightingSettings.indirectScale = 1;
|
|
|
|
|
lightingSettings.directionalityMode = LightmapsMode.NonDirectional;
|
2025-04-25 15:07:22 +08:00
|
|
|
|
|
|
|
|
Lightmapping.lightingSettings = lightingSettings;
|
|
|
|
|
|
|
|
|
|
// 强制场景保存以确保设置生效
|
|
|
|
|
EditorSceneManager.MarkSceneDirty(activeScene);
|
|
|
|
|
EditorSceneManager.SaveOpenScenes();
|
|
|
|
|
|
|
|
|
|
Debug.Log("开始烘焙灯光...");
|
|
|
|
|
Lightmapping.Bake();
|
|
|
|
|
Debug.Log("灯光烘焙完成");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 移动光照烘焙相关文件到指定目录
|
|
|
|
|
/// </summary>
|
|
|
|
|
public static void CopyLightmapFiles(Scene scene, string lightmapDirPath)
|
|
|
|
|
{
|
|
|
|
|
string sceneName = scene.name;
|
|
|
|
|
|
|
|
|
|
Debug.Log("开始移动光照烘焙文件...");
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// 确保目标目录存在
|
|
|
|
|
string lightmapDirAssetPath = SceneUtils.GetAssetPath(lightmapDirPath);
|
|
|
|
|
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";
|
2025-07-15 17:02:45 +08:00
|
|
|
if (AssetDatabase.LoadAssetAtPath<Object>(lightingSettingsPath) != null)
|
2025-04-25 15:07:22 +08:00
|
|
|
{
|
|
|
|
|
string destPath = $"{lightmapDirAssetPath}/{sceneName}_LightingSettings.asset";
|
|
|
|
|
|
|
|
|
|
// 如果目标已存在,先删除
|
2025-07-15 17:02:45 +08:00
|
|
|
if (AssetDatabase.LoadAssetAtPath<Object>(destPath) != null)
|
2025-04-25 15:07:22 +08:00
|
|
|
{
|
|
|
|
|
AssetDatabase.DeleteAsset(destPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AssetDatabase.MoveAsset(lightingSettingsPath, destPath);
|
|
|
|
|
Debug.Log($"已移动: {lightingSettingsPath} -> {destPath}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 移动整个光照目录
|
|
|
|
|
string lightmapDirName = sceneName;
|
|
|
|
|
string lightmapSourceDir = $"{Path.GetDirectoryName(scene.path)}/{lightmapDirName}";
|
|
|
|
|
string lightmapSourceDirAssetPath = SceneUtils.GetAssetPath(lightmapSourceDir);
|
|
|
|
|
|
|
|
|
|
if (AssetDatabase.IsValidFolder(lightmapSourceDirAssetPath))
|
|
|
|
|
{
|
2025-07-15 17:02:45 +08:00
|
|
|
string[] files = Directory.GetFiles(lightmapSourceDirAssetPath);
|
|
|
|
|
foreach (string file in files)
|
|
|
|
|
{
|
|
|
|
|
if (!file.EndsWith(".meta"))
|
|
|
|
|
{
|
|
|
|
|
string fileName = Path.GetFileName(file);
|
|
|
|
|
AssetDatabase.MoveAsset(file, lightmapDirAssetPath + "/" + fileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Directory.Delete(lightmapSourceDirAssetPath);
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
|
|
|
|
|
/*string destLightmapDir = $"{lightmapDirAssetPath}";
|
2025-04-25 15:07:22 +08:00
|
|
|
|
|
|
|
|
// 如果目标已存在,先删除
|
|
|
|
|
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}");
|
2025-07-15 17:02:45 +08:00
|
|
|
}*/
|
2025-04-25 15:07:22 +08:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.LogWarning($"找不到光照数据目录: {lightmapSourceDirAssetPath}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (System.Exception e)
|
|
|
|
|
{
|
|
|
|
|
Debug.LogError($"移动光照文件时出错: {e.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AssetDatabase.Refresh();
|
|
|
|
|
Debug.Log("光照烘焙文件移动完成");
|
|
|
|
|
}
|
|
|
|
|
}
|