We are currently developing a 3D-Survival game where the player spawns on a randomly created island and has to survive as long as possible. The game uses the Low-Poly style, for that we wrote our own terrain generator in order to achieve the low-poly look.
Unfortunately, we ran into a performance problem:
The snap was taken from our game, while running in full-screen mode (1920 x 1080), on my Ultrabook, Asus-Zenbook Ux303lb with a Nvidia Geforce 940M, 8GB RAM, Intel i7-5500U 2.4GHz.
We have way too many vertices (maximum number we measured was about 1 Mio ! ).
Consequently, we started to optimize our terrain, by splitting it into tiles and only loading tiles the player can see. We hope to get some performance boost from that, but there´s another problem.
Our island is populated with lots of objects like trees, rocks, animals, etc., therefore there are many objects in the scene graph (which is very bad, isn´t it ? ). So we thought about a way to optimize our scene-graph and only attach objects which are near the player.
So the question rises: Is this the way to go, or are there other optimizations we can make on our scene-graph or terrain, in order to enhance perfomance? Especially for low-poly terrain, which contains more vertices.
Thanks in advance
First, did you check the optimization page?
Second, you could try using batching or instancing, this will reduce the object count for you. The other thing which should be happening is culling. Only things in the camera view frustum should be rendered on screen. However if you have lots of objects behind each other you might have to resort to either using Level Of Detail or manually culling objects in the distance.
You can find out about Level Of Detail at http://wiki.jmonkeyengine.org/doku.php/jme3:advanced:level_of_detail
Was going to say the same: read about batching in jME.
The number of draw calls (i.e. number of objects or “meshes” or Geometry) is what kills performance.
The batching can be done when things don’t move and use the same textures.
About the move thing you can do some tricks, but texture / material must be same for all batched.
If you want things to have the same texture, you can also use the texture atlas.
If you use the texture atlas, then you can batch things together that did not have same texture yet.
Splitting the terrain into sub meshes can work - but too many sub meshes is always bad.
1 Mio vertices with 100 draw calls (objects) is better than 1 Mio vertices with 10000 draw calls.
This is why you do batching.
Number of vertices is not that important anymore anyway.
First try using a texture atlas for the single objects, even without actually batching the objects the jME render loop will try to optimize rendering so that state switches are kept to a minimum. That only works when each object has the same material though (including textures).
Vertices are not that much of a problem, objects and the resulting state changes are the main issue.
Thx for the fast replies
I´ve alredy read about Jme optimizations and as mentioned above we´ve already batched the grass. But the problem is that the player can interact with all kinds of objects: he can destroy trees with an axe, hit animals, mine rocks and so on. So I can´t simply batch those objects in order to be able to interact with those objects, am I right? Additionaly, we don´t use textures but vertex colors for our terrain and objects, simply a design decision. So all objects have the same material, but the problem is, as mentioned earlier, that the player should be able to interact with single objects.
I thought about batching objects that are far away from the players view and de-batch them if the player gets near to them. But I´ve read that batching is a costy operation, so I thought this wouldn´t be an option either.
Any ideas on how to decrese draw calls? I think, one of our main problems is really that all objects of the whole island are in the scenegraph, even if they are very far away.
OK, well you can create a simple control which culls these objects if the distance to the camera/player is above x. Should be fairly straight forward.
BatchNode allows you to batch objects and still move them separately, it effectively changes the coords in the mesh. This might of course stress the PCI bus more because the whole mesh has to be updated each frame - it might still (depending on the batch size) perform better.
Also think about the amount of lights, each light causes the scene to be rendered once. Furthermore as you talk about objects that are “very far away” - you’ll have to deal with this anyway. Some kind of pre-rendered or batched far plane will surely be necessary. It sounds like you have the frustum far plane set very far as you say “all objects are in the scenegraph” - I guess you mean are rendered. Normally all objects outside the camera frustum are not rendered.
So i could use BatchNodes for trees and still check against the collision shape of a single tree ( collision shapes are independent from batched objects aren´t they? ).
Regarding lights, we use one direction light for the sun and an ambient light. Additionaly, we planed to implement objects like a campfire or torch which should also use lights, but this just as a sidenote.
The frustum far is set to 1000 if I remember right. With “all objects are in the scenegraph” I mean that every tree, rock, apple, animal and so on is attached to the scenegraph, even if it is out of view or far away from the player. So I have for example a TREE_NODE to which all trees on the island are attached to. What I meant earlier was, if only attaching objects the player can see or that are near to the player to the scenegraph would enhance performance. Besides, I read that Jme has to traverse the SceneGraph 3-times every frame, so I thought this would definitely yield better performance.
What do you mean with a pre-rendered scene or batched far-plane will be necessary? Does that mean that objects which are far away from the players view should be batched? If yes, wouldn´t that be a costy operation to do so, and later debatch those objects? Or would I batch far away objects and attach the batched node instead of the single not-batched objects to the scenegraph and if the player gets near, detach the batched node and attach the single-object nodes again?
Sorry if I am asking so much, but this is my first bigger 3D game and I want to do things right.
I don’t know what you use for collision detection - if you use bullet then yes, the collision objects are completely separate from the scenegraph. Afaik “normal” jME collision also works as expected in BatchNodes. Again, if the object is not in the frustum (and the cull hint hasn’t been changed from its default) then the object won’t be rendered. So if the object is not in the camera view theres no render overhead. The update of the scenegraph itself is negligible compared to the actual rendering and certainly not your problem unless you get into millions of objects (and probably not even then).
But then again you should only have actual objects for the vicinity of your player and have some kind of far plane as described before if your world is very large. That can be done in many ways from rendering the objects onto a sky box to using a lowpoly version of your landscape generation algorithm to create a smaller version of the far away objects that you blend with the surrounding objects (keyword multiple viewports). The objects being smaller allows you to have them closer (inside the frustum range).
So even if I have tons of objects in the Jme SceneGraph it wont affect performance, because Jme internally clipps objects out of view before continuing with costy operations, did I get this right?
Rendering the objects on a skybox seems too diffiucult for me at the moment, the other approach seems pretty difficult too, but I definitly like the idea, so that would be an option. So I could decrease the frustum range, while faking a higher frustum range to the player? And would I batch those smaller versions of the objects together? If yes, wouldn´t that take a while?
There´s something I wanted to ask about concerning the low-poly terrain. I´ve seen other high-detail games which have way less vertices than we have. I mean, 1 Mio. vertices for a low poly world seems quite a lot. Is that normal with low-poly terrain which uses more vertices in order to achieve the low-poly look or are there ways to dcrease the vertex count of our terrain. We don´t have LOD on our terrain, because it is already low-poly, at least we thought that wouldn´t be useful. Or would that make a difference?
Btw is there something like occlusion culling in Jme, because I´ve read something about it and that this is really increasing performance?
Objects that are not in view are not rendered. So the GPU doesn’t have to work for them. Thats called “culling”. I can’t say anything about your terrain system as you wrote it, I didn’t. I do hope you don’t have one huuuge mesh but several chunks though - so that culling will actually work for it. As for vertex count you should know better - I hope you don’t use boxes that you stack though, that would be a huge amount of vertices that are never seen. So if you just have the amount of vertices you need to display the surface of the terrain then I guess thats as good as it gets - but again I am guessing about software that you wrote…
Occlusion culling means you cull objects that are behind other objects and thus unseen. Determining if objects are not seen in a generic way is so computation intense that it probably won’t yield a net FPS increase but depending on how your game looks / works you might have better ways to find out if an object is occluded. E.g. if your player is in a cave he certainly won’t see the trees above the ground so you can just set their cullhint to always (or detach them).
Currently our terrain is indeed one huge mesh, but we have already a test version where the terrain mesh is split into multiple tiles ( splited into several geometries ) and currently we are writting an algorithm to attach only the tiles the player can see ( Jme Terrain does something similar too or? ).
As a sidenote, the terrain is generated from a randomly generated heightmap.
So there might be an additional problem: we have a low-poly plane for the water which oscillates with some sine and cos functions we apply in the vertex shader. And here comes the point: the water plane is as huge as the island and also attached to the scene.
So would the vertices of the water mesh that can´t be seen because they are below the island mesh be sent to the graphics card if the player looks at the ground of the island? If YES, that would explain some lost FPS.
Ahh so occlusion culling wont be needed by us, as there are no such situations where the player can be in a cave or a certain room.
Culling works based on objects, not vertices.
Wow that made everything clear
So that´s definitly one of our main performance killers, if the whole island + water mesh were constantly sent to the graphics card. So we´ll fix that. Thanks normen
But i still have one question: Will objects still cast shadows if i batch them? Because the last time i tried to batch some objects they didn´t cast any shadows anymore. Moreover, is it valuable to batch objects far away, yes or no?
Afaik shadows should work normally with batched objects. As for the second question: It depends on your application. Having many objects in general is a performance killer because of the state changes to render each of them (not so much because of the rendering itself or the triangles). Because of the shape of the camera frustum you usually see more objects when they are far away, thus raising your object count.
Ok, but our island has really a lot of objects on it and therefore in the SceneGraph. But what matters are really just the objects in the viewing frustum? That means that we can keep all objects in the scene graph?
And for the rest, I think we should try to implement some methods to optimize our game instead of asking even more here. After all, one learns most by trying something out.
Thanks for the nice help
I can’t say what works for your application. In the SDK you can install and use the profiler to find out where your app loses most of its time.
Well, I´m going to try some things out.
One final question thought: Is the object count of the scenegraph really not important, after all every object must be tested against he frustum or?
Yeah but thats negligible overhead compared to everything else, especially rendering itself.
Ok if you say so
Well thanks for the help, topic closed I´d say