实际上是个老问题了,之前是使用一个场景来手动切换,现在工业仿真不允许这么做,或者说很麻烦,所以结合AI找到原因,并给出的解决方案:
cs
/// <summary>
/// 场景就位后补一次环境光。
/// <para>★ 为什么必须补:从 MainMenu 切进来时 <c>RenderSettings.ambientMode</c> 是 Skybox,
/// 而环境光实际来自「天空盒算出来的 SH 探针」。<c>SceneManager.LoadScene</c> 只把新场景的
/// RenderSettings 字段填进去,**不会重算这个探针** ------ 于是沿用上一个场景留下的值。
/// MainMenu 的 <c>m_SkyboxMaterial</c> 是空的(UI 场景用 Solid Color 当背景),
/// 算出来的探针就是纯黑,切到 Exam 后整个场景只剩方向光直射面是亮的,其余全糊成黑的。
/// 修复分两层:场景侧给 MainMenu 补上默认天空盒(断掉污染源),代码侧在这里强制重算
/// (兜住任何进入路径,包括以后再加的场景或打包后的真实切换)。</para>
/// </summary>
private void Start()
{
RefreshEnvironmentLighting();
StartCoroutine(RefreshEnvironmentNextFrame());
}
/// <summary>
/// 推迟一帧再刷一次。第二帧时相机、后处理 Volume、XR 模式切换都已就位,
/// 有些工程会在 LateUpdate 里改天空盒 ------ 那时再刷一次才拿得到最终值。
/// <c>DynamicGI.UpdateEnvironment</c> 是幂等的,多调一次只是多算一遍天空盒 SH。
/// </summary>
private System.Collections.IEnumerator RefreshEnvironmentNextFrame()
{
yield return null;
RefreshEnvironmentLighting();
}
/// <summary>重建环境光探针(天空盒 SH + 默认反射),并打一行光照自检。</summary>
private void RefreshEnvironmentLighting()
{
DynamicGI.UpdateEnvironment();
int directional = 0;
Light[] lights = FindObjectsOfType<Light>(true);
for (int i = 0; i < lights.Length; i++)
{
if (lights[i] != null && lights[i].type == LightType.Directional && lights[i].enabled)
{
directional++;
}
}
RobotAssemblyLog.Info("Light", string.Format(
"场景「{0}」光照自检:环境光={1} 天空盒={2} 环境光强度={3:0.##}|方向光 {4} 盏|已重建环境探针",
SceneManager.GetActiveScene().name,
RenderSettings.ambientMode,
RenderSettings.skybox != null ? RenderSettings.skybox.name : "无(Skybox 模式下无天空盒 = 纯黑)",
RenderSettings.ambientIntensity,
directional));
if (RenderSettings.ambientMode == UnityEngine.Rendering.AmbientMode.Skybox
&& RenderSettings.skybox == null)
{
RobotAssemblyLog.Warn("Light",
"环境光模式是 Skybox 但场景没有天空盒,物体背面会全黑 ------ 给场景补一个 Skybox 材质或改成 Trilight/Flat");
}
}
大致原因是 因为切换场景的时候不会重算环境光,需要刷新下。