Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Max number of lights per object limit is applied globally for all instances of a MultiMesh, rather than per instance #44912

Closed
alib86 opened this issue Jan 4, 2021 · 7 comments · Fixed by #65491

Comments

@alib86
Copy link

alib86 commented Jan 4, 2021

Godot version:

3.2.3 Stable and 3.2.4 Beta 3 (Same result)

OS/device including version:

Windows 10, Nvidia RTX2060, Latest official drivers, GLES3

Issue description:

-Max number of lights per object limit is applied globally to all instances of a given MultiMesh, rather than per instance. Effects of a visible light on one instance does not get drawn on other instances (unless in range) so expected each instance to have it's own limit.

-In other words, 8 lights (limit) on one instance disables lights for all other instances of the same MultiMesh. MeshInstances with the same Mesh resource as the MultiMesh do not act this way.

-Increasing the max number of lights via project config (as you can do in 3.2.4 Beta 3, see #43606 ) can improve the situation but at a high cost of performance.

Steps to reproduce:
(Given max number of lights per object is 8 - limit at rendering/limits/rendering/max_lights_per_object for 3.2.4 B3)
-Add a MeshInstance with CubeMesh,
-Populate MultiMesh using MeshInstance (so same Mesh resource) with a number of instances,
-Set 9 lights over one of the MultiMesh instances (Choose OmniLight or SpotLight and keep all lights thesame type. Limit is per light type)
-One of the lights will not be visible on that instance as you are over the limit.
-Move that light over another MultiMesh instance and it still won't be visible. (Expected
MultiMeshTest.zip
to be visible)
-Move the same light over the MeshInstance (that uses the same Mesh resource), it will be visible there.

Images of the issue:
Godot1
Godot2
Godot3

Minimal reproduction project:

MultiMeshTest.zip

Note: If anyone is able to point me in the right direction, I am willing to dig through the source code and take a look, haven't been able to find where to start on my own.

@clayjohn
Copy link
Member

clayjohn commented Jan 4, 2021

A multimesh is a single object, no matter how many instances. All instances are drawn at once as if they are one large object. This is part of the tradeoff you make when you use a multimesh. It is much faster to draw, but the GPU treats it as one instances rather than a collection of meshes.

@alib86
Copy link
Author

alib86 commented Jan 4, 2021

@clayjohn Figured as much, though had hope for a workaround since the lights are able to differentiate between instances and draw on specific ones but are limited per the whole MultiMesh instead of the specific ones it can draw to.

@Calinou
Copy link
Member

Calinou commented Jan 4, 2021

Remember not to use a single MultiMesh to represent lots of instances over a large area. Otherwise, you won't be able to benefit from view frustum culling.

Instead, it's recommended to use a "grid" of MultiMeshes so that they can be culled individually. You need to find a compromise between the number of draw calls and culling opportunities.

@Zireael07
Copy link
Contributor

IMHO this lights per object thing should be called out in MultiMesh documentation.

@alib86
Copy link
Author

alib86 commented Jan 4, 2021

@Calinou Thank you for the suggestion. My use case is a bit different, I am using MultiMeshes like a tilemap (or GridMap - which was very slow for large terrain) where each MultiMesh is a different type of tile, so they are arranged in an interlocked fashion. Tried dividing each tile type between a couple MultiMeshes, but the implementation with 1 tile per 1 MultiMesh has the best performance.

It's a medium sized city scene where there are multiple lamp posts per street, capping visible number of lights (32 of closest ones) gets me to 200-300 units of good view then the popping in starts as I move and turn on newer lights. Thought about ray casting to lights to do occlusion culling but that looks like it will get expensive pretty fast.

@Calinou
Copy link
Member

Calinou commented Jan 4, 2021

(or GridMap - which was very slow for large terrain)

Try decreasing the GridMap quadrant size to 1 to get better performance. GridMap uses MultiMesh under the hood, but it doesn't seem to be always working correctly for some reason.

Thought about ray casting to lights to do occlusion culling but that looks like it will get expensive pretty fast.

Look into using LOD-enabled lights from godot-lod 🙂

There is a pull request that reworks godot-lod to make it more efficient, you could test that too. I didn't merge it yet as I haven't been able to test it locally due to lack of time.

@alib86
Copy link
Author

alib86 commented Jan 4, 2021

@Calinou Thanks again for the suggestions, will look into godot-lod. Fiddled around with GridMaps for quite a while at the beginning but could not get anywhere, render performance aside the initialization from code (most likely the collisions) took ages. Now combining all multimeshes into one collision shape, lightning fast.

Anyway slightly deviating from the purpose here but if anyone is interested, in the past couple of hours just tried actually ray casting (4 times per sec) camera to lights using building AABBs as occlusion areas (scaled to 80% so turning the corner does not cause pop in), with 200+ lights it does actually seem to be working pretty good, keeping around 15-30 visible at once without much resource usage. Thought could further improve with an extra camera.IsPositionBehind(light) check but that does jump CPU usage from 5% to 15%.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants