子关卡中的 static light 在特定情况下会变成 Dynamic

Hi, 我们发现,在子关卡 LoadedVisible->LoadedNotVisible->LoadedVisible 后,子关卡中的 static light 会变成 dynamic。

我们做了初步的排查,定位到原因是:

  1. ​static LightComponent 可以作为静态光渲染的条件是,能够从 MapBuildDataRegistry 获取 LightComponentMapBuildData,如果不能获取,就会动态渲染光照。
  2. 获取 LightComponentMapBuildData 依赖正确的 ULightComponentBase::LightGuid
  3. 每次 LightComponent AddToWorld,Register 的时候,​会重新计算 ULightComponentBase::LightGuid,计算过程依赖了所属 Actor 的 ActorInstanceGuid
  4. ActorInstanceGuid 是在加载 Actor 序列化的时候被添加到 GActorGuids 里的,当这个 Actor 的所有 Component 完成了 AddToWorld 以后,就会从 GActorGuids 中释放掉。
  5. 当子关卡 LoadedVisible->LoadedNotVisible->LoadedVisible 时,第二次 LoadedVisible,会重新将 LightComponent AddToWorld,重新计算 LighGuid,但是此时 ActorGuid 已经被释放掉了,导致计算得到的 LightGuid 错误,因此无法获取 LightComponentMapBuildData,因此这个 light 被动态地渲染了,当子关卡中 static light 很多时(几百个),性能会下降。

我们有一个临时的补丁解决:

在第二次将 LightComponent AddToWorld,重新计算 LightGuid 时,如果发现此时 ActorGuid 已经被释放掉了,就不更新 LightGuid,还使用上次的。

​ 但是这只能作为临时的解决方案,不知道与 ActorGuid 系统的设计是否完全吻合,是否会导致其他问题。

[Image Removed]

[Attachment Removed]

Steps to Reproduce

  1. 创建一个 Level
  2. 添加一个子关卡
  3. 在子关卡里添加一个 static PointLight
  4. 在关卡蓝图中添加逻辑,可以使用键盘或者按钮控制这个子关卡的 visibility
  5. build lighting,然后打包
  6. 运行打包好的游戏,让这个子关卡 Unloaded->LoadedVisible->LoadedNotVisible->LoadedVisible

此时可以观察到,第二次 LoadedVisible 后,这个点光源的光照出现了两次,不仅有原来烘焙的 lightmap,还有动态计算的。并且stat lightrendering 也能看到直接光源数量多了。

[Attachment Removed]

Hi,

感谢反馈,的确有这个问题,能否在FActorInstanceGuid::GetActorGuids,把两处Guids.ActorGuid改成 Guids.ActorGuid.IsValid() ? Guids.ActorGuid : InActor.GetActorGuid(),这样是否也能解决问题?

[Attachment Removed]

Hi, 非常感谢回复!我们试了一下这个方案,但是 InActor.GetActorGuid() 看起来是 Editor Only 的函数,而我们主要出问题是在打包后的游戏中,所以无法调用它。

[Attachment Removed]

的确,抱歉,没注意到。

那是否可以这样,先判断 if (!LightGuid.IsValid()),如果LightGuid无效,再用OriginalLightGuid和ActorInstanceGuid赋值?(本质上跟你的改法一样)我觉得应该没有什么大的问题。

[Attachment Removed]

我们这样尝试了,测试看起来可以,在编辑器和 Runtime 都暂时没有出现什么问题​,感谢帮助!

[Attachment Removed]