I have a Main scene to store some general gameobjects for all game levels and I load scene with LoadSceneMode.Additive so there will be two scenes at a time, one is Main and another is something like Level1. I only load one level scene at a time.
But it ask me to bake all levels’ occlusion data into the Main scene, I think it is unacceptable because each level has its own bake settings, they are different and if I want to re-bake one level, then I need re-bake all levels, the bake time will be every long compared to baking each level only if I changed it. Also I only want to load one level’s occulsion data to save memory and I/O.
I have searched the forum and found someone said if I set the additive level scene active and then Unity will use the bake data from the active scene instead of the first loaded scene. I tried but it still show no bake data in editor.
So is there any way to let me force Unity choose the correct occlusion bake data at runtime?
I’m not sure there is… I think it is analogous to the render settings in that only one can be active at a time.
The reason is that if you compute occlusion you do so in a context, and Unity only builds enough information to be accurate in that context. If you then add extra context (e.g., the second additive scene), the first scene’s occlusion would no longer be valid.
I agree with you, but the first scene has nothing to do with the occlusion, there are just some UI gameobjects or virtual cameras, they don’t need any occlusion. Just like I move the Don’tDestroyOnLoad to a single scene.
So in fact I only need use one occlusion at a time even I load more than one scene at a time. Is there any way to do this?
I’m sorry, I misread your post: I thought you wanted two scenes with culling to merge at runtime. My bad.
Now, for one occluded scene among many additively loaded…
For what you’re saying you tried (setting that scene to be active), that’s the correct way for setting Lighting / RenderSettings (set the scene active), so I’m surprised that didn’t do it.
When you mark that scene active, did you pause and verify that it really was in the Hierarchy window?
ALSO: you know you cannot load a scene AND mark it active in the same frame, right? You have to wait for the scene to load (at end of frame), then mark it active, after at least waiting until the next frame. I suppose if it was async streamed in it would take even more time before you can mark it active.
I have checked the Hierarchy and can confirm the additive scene is actived, I also tried to ative the first single scene and active the additive scene again, the lighting changed like what you said but editor always show there is no occlusion data.
I also found a interested thing is that if I select any gameobject with renderer in the additive scene then the occlusion data is actived and draw calls descreased a lot and editor also show the bake data size.
BTW, I use Unity2018.4.2, will there be an issue about active operation?
@Kurt-Dekker I finally found the reason why the active doesn’t work. It should be called on the same frame of scene loaded. Which means you need to set the additive scene active immediately after SceneManager.sceneLoaded is called. If you set active only delay one frame, then the occlusion data won’t be used by Unity. It was tested on Unity 2018.4, 2019.4, 2020.2.
I tend to be lazy and not hook that callback. That callback (.sceneLoaded) is indeed called on (at earliest) the frame after you issue the actual LoadScene() call, or at latest, once the scene is truly loaded in.
Seems that it should be part of the argument to LoadScene().
I can see conflicts of course, but at least it would be fire and forget, which is how I always wrap it up: I tend not to use straight LoadScene additives, no async, so I just wait a frame and mark the scene I want as active.
My scene helper, link inside the source to the CallAfterDelay gist as well:
I just want to confirm that this works and after a lot of googling, seems like the only fix.
In my case I have a very complex scene loading setup (multiple asyncs at once, waiting for deserialization before activation, etc, etc.), and the “catch-all” solution I found was to hook SceneManager.sceneLoaded right before my whole loading routine, then unhook it after.
So for example:
private static string occlusionScenePath;
private static void FixOcclusionCulling(Scene scene, LoadSceneMode mode)
{
if (scene.path == occlusionScenePath)
{
SceneManager.SetActiveScene(scene);
}
}
private static void MyLoading()
{
occlusionScenePath = // the path of the occlusion scene
SceneManager.sceneLoaded += FixOcclusionCulling;
// Your loading rountine here, which may include any number of scenes/async/etc., including the occlusion scene
SceneManager.sceneLoaded -= FixOcclusionCulling;
}
Do all the scenes need the same occlusion data then?
Wait what? I thought that async scene load only worked one scene at a time, each time i tried concurrent scene load it blocked the next one until the previous was active. How did you cast that magic spell?
Sorry, not “at once”, I just meant to load multiple async scenes in the same stack/setup. My level is layered (like one scene for art, one for audio, one for gameplay, etc.).
This seems to also work for me.
I’m in the same case as you’re: 1st scene is my loader and entry menu. My real scenes are loaded/unloaded by this menu.
I hardly struggled on this topic ! Mostly like if unity users ( and unity devs ) only use one scene for everything.
Now things look better:
one scene —> one occlusion
this will allow me to have multiple new scenes in the future without having to rebake everything in a nonsense way.
This should be documented somewhere. Setting an additive scene as active right with the sceneLoaded callback does indeed load the occlusion culling data. Thank you to the ones that found out
Wow, this is crazy that you have to do this, but it does work to set it active after the first frame. Here is my code that works. I have other scenes and then load this one last.
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(level, LoadSceneMode.Additive);
asyncOperation.allowSceneActivation = false;
while (!asyncOperation.isDone)
{
percentageDoneText = ((int)(100f * asyncOperation.progress / .9f)).ToString() + "%";
SetLoadingLabel($"LOADING Level {percentageDoneText}");
// scene has loaded as much as possible, the last 10% can't be multi-threaded
if (asyncOperation.progress >= 0.9f)
{
// we finally show the scene
asyncOperation.allowSceneActivation = true;
}
yield return null;
}
//IMPORTANT
//NOTE - Seems that you have to set the scene active on the first frame after loading or the Occlusion data will not load properly...
SceneManager.SetActiveScene(SceneManager.GetSceneByName(level));