There’s several parts to unpack so I will try to address these:
Here’s a link to the default flow of opening the app: https://developer.playcanvas.com/en/user-manual/scripting/application-lifecyle/
The default loading bar goes from 0 to 100% based on the download progress of the assets set to preload.
When all the preload assets are loaded, the bar is at 100% and it starts the app. This includes the initialisation of the components, scripts, first update and rendering the first frame.
If you are finding that there is a freeze/pause between the preload finishing and seeing the first render frame, this is likely to be one of these reasons:
-
You are using mesh collision and have multiple mesh collisions, some of which have a high poly count. This is because the collision mesh has to be built at runtime and can take a while depending on the number of different mesh collisions and poly count.
-
There’s a lot of logic happening in the initialisation/post initialisation/first update of the scripts. This all runs before the first frame render.
-
Shader compilation. When a material is first rendered to the scene, the engine has to compile the shader based on features used in the Standard Material. eg use of the diffuse material channels etc. The more complex and more unique materials you have, the longer it takes on devices.
3 is usually the issue (use the browser devtools profiler to check) and not an easy thing to optimise.
Reducing complexity of the shaders from lighting
If you aren’t using multiple omni or spotlights in the scene, then I would disable Clustered Lighting in the project settings to reduce the shader size to be compiled.
In either case, I would disable shadow casting from lights where possible.
Reducing shader/material variants
You can reduce the number of shaders needed to compile by reducing the different combinations of properties used in the materials.
As a simple example: PlayCanvas 3D HTML5 Game Engine
Scene 1 uses two separate materials
‘Scene 1 Material A’ is using a texture for the diffuse channel
‘Scene 1 Material B’ is just using the color
When we run the scene, we see how many shaders are being compiled in the PlayCanvas profiler:
In Scene 2, we have same visual render with ‘Scene 2 Material B’ using a texture instead for color
Now both materials in the scene are using the same materials properties as each other and therefore can use the same generated shader code which is seen in the PlayCanvas profiler
You can also see that my compile time is reduced from 2.7 ms to 1.2 ms
However, the downside in this case is that using a texture for color requires a lot more shader code which we can see in Spector.js:
Compared to Scene 1’s material of just using the color
This means that Scene 2 is doing more work per pixel and would be more expensive to render at runtime.
It’s also not easy to know which properties would add/remove code from the shader too which makes this process difficult if they wanted to go down this route.
Lag when moving the camera
Similar to the above. The lag is likely because it is rendering new materials in view and having to compile the shaders and upload textures needed for them. To work around this would be to have these materials in camera view somewhere (on a cube for example in the corner behind some UI) before they are needed over time.
Hiding the lag on preload complete
As mentioned before, the loading bar only shows download progress. Once it is complete, it will do all the initialisation for the scene, entities, materials etc needed to update and render the first frame.
To hide this, some developers have changed the preloader so the loading bar only goes from 0%-90% showing to the user that it is still ‘loading’.
Or if you want something more granular. You can disable all entities in the scene in the Editor and have the loading bar go from 0 to 50% (or some other number) for the preload assets download progress.
Then when the app is started, enable groups of Entities frame by frame and fire an event to update the progress bar. eg. first frame, enable all the wheels, wait for render, fire event. Second frame, the car exterior, wait for render, fire event, etc
This way, the materials are rendered and compiled in groups on a frame by frame basis so you can update the progress bar on progress.
On top of this, they have also added a transparent iframe on top that has a loading animation (eg spinning disc) because even though the parent page is thread locked, the iframe is still updated. This means that the user is not looking at a frozen screen.
Another possible method is to have the first scene / frame be as simple as possible. (eg disable the car entities) and the engine won’t compile the shaders until the materials are used to render in the scene. Once the user is in the experience, the app can enable car parts over time (this can be hidden in some way, Polaris do this quite well) so that it doesn’t completely lock up the page.