Optimizing forest framerate

Hello,

I’ve been working on generating a forest and at this stage I’m trying to optimize the framerate. The forest contains about 10K trees, each of them having around 1k triangles. I’m projecting shadows. The whole thing looks like this

  • First thing I did was generating LODs on the meshes (so far only tried with 2 lods), I did see a significant impact on the framerate
  • Then I tried using batch geometries. Here, reading:
    My experiences with batching objects and the resulting frame rate
    I made sure I split the whole forest into smaller nodes (100 nodes of about 100 trees, all trees use the same material) and this is what I see, at medium range, I feel the bulk geometry is actually a bit worse:

in comparison to the non-bulked

I don’t understand how come I still render 64 out of 100 nodes. With the layout of my forest, I would expect a much smaller fraction to show up from this camera angle (in the non-bulked, the fraction of objects showed is much smaller in comparison to the total)

At close range, I can see an improvement (like x2) from the bulking. Still I see that I’m rendering 44 nodes out of 100

vs the non-bulked

At long range, I see the bulk geometry doesn’t use LODs (even though I’m calling it through this
GeometryBatchFactory.optimize(subNode, true);
I don’t really care that much since I won’t be displaying the whole forest very often, but let me know if you see I am missing something)

Do you think there is a way to improve either the bulking, the z-culling or frustrum culling? Is there any other rendering optimization technique that would benefit the framerate?

Thanks!
Adrien

Yes, there are billboards/impostors. That is a 2d quad with a texture of a tree. You can generate eg. 4-8 different textures from different angles and swap between these. You normally apply these for the furthest trees. They are very fast since 1 quad = 4 vertexes. To render them efficiently you can use instancing. The rotation and selection of texture can be done in a shader also. The generation of the textures can be done by code too.

Also there is the instancing way to do it.
Instancing might work nice with this kind of use case.

Group them into a larger grid. Then as they get further away you could lower the lod until a certain point, then replace them with billboards, instanced billboards, or batched billboards, though you might have to have some shader knowledge first. The point is, when those trees are only a few pixels on screen, it is much better to just have a quad (two triangles) rendered per tree instead of a mesh. The trick is if you want to combine those quads into groups so the renderer can cull out large sections of the scene without having to check each tree individually. Most AAA games do this is some way or another, from skyrim to battlefield etc. You could combine all your tree billboard textures into one big texture atlas and render them all from that with a shader.

1 Like

I tried creating an instancednode for the whole forest but then framerate dropped considerably since all trees have to be rendered all the time

Then I tried to instance each of the 100 sub nodes but then something very strange happened onscreen (like the forest turned into a big black shape with pieces of tree everywhere and framerate dropped to around 1 frame per 10 s) maybe because the 100 nodes share the same instanced material ?

Regarding the billboard, why not and I don’t mind digging the shader, but you were saying there is a way to generate the different billboard textures from a single mesh ? I will take a look already

I don’t know why that happens, but make sure you are putting the trees under a node based on location, not just random location, so that the renderer can check the nodes and cull areas that are offscreen.

You can do offscreen rendering at runtime or prebuilt that will render your tree into a 2d texture, just like taking a screenshot of it. Then you can arrange whatever you want into a texture atlas.

1 Like

Yes, that’s the way the forest is generated (by different nodes, and then I move the nodes around) so each node groups trees belonging to the same area

One thing regarding the culling, does the actual node position matter for this? I have all my 100 nodes centered at 0,0,0 and I just place the trees at their real world coordinates

[Edit] I’m testing moving the location of the actual subnodes instead of just the trees, it doesn’t seem to help the culling

No, I believe the bounding box for a node is only based on the geometry max extents. You might want to try davidb’s viewer for debugging:

So you should:

  1. Have close range trees be Instanced in location grid groups, or close range trees be in a Batch node/geometry batch factory, or close range trees be regular spatials if it is necessary.
  2. Transition lods as the grids get further away
  3. Eventually switch to a 2d billboard of the tree. The billboards should still be Instanced / batched per grid group, so they can be culled out optimally.

The hard part (that many games still haven’t solved perfectly) is transitioning between these without too much popping or noticeable change to the player. There are many techniques such as fading the object out/ fading in billboard that you can try out. There’s also dithering and rendering different rotations of the trees to fade between based on what direction the player is looking at the tree.

Just putting these here in case you want to dig around in other solutions.

SimArboreal will generate trees that have LOD, etc. built in, including simpler trees as well as full atlases. It supports wind, etc. even for instanced trees. It also supports an in-between LOD where each branch is rendered as a single camera-aligned quad (hard to describe in words).

http://simsilica.github.io/SimArboreal-Editor/

On its own, it doesn’t have paging, etc. built in but you can look at how that was done in the IsoSurface Demos if you are curious:

Video of SimArboreal regular mesh-based LOD (simplified tree geometry):

Video of the aligned-billboard branches thing I was mentioning:

Trees in the wind: (wind effect can be tuned up/down as needed… even at runtime)

Putting it all together in the IsoSurface Demo:

1 Like

it looks pretty neat! I downloaded the zip file but when importing it says I’m missing a bunch of com.simsilica.lemur.* which i can’t find on the simsilica home page, by any chance do you know where to get these?

Also, I have integrated the library to SS Editor :wink:
https://bitbucket.org/JavaSabr/ss-editor-tree-generator

1 Like

Also note: the IsoSurfaceDemo has some prebuilt binaries if you just want to try it out:

I don’t remember what state they were in at that time.

SimArboreal Editor also has some prebuilt binaries:

Really sorry about that but I’m still seeing missing references such as below, even from simarboreal itself, for example the package (and I double checked the content from github to make sure it wasn’t just on my side) doesn’t contain the following references:

The class
Simarboreal\src\main\java\com\simsilica\arboreal\AtlasGeneratorState.java
references the following (dead?) links like
import com.simsilica.arboreal.mesh.BillboardedLeavesMeshGenerator;
import com.simsilica.arboreal.mesh.SkinnedTreeMeshGenerator;
import com.simsilica.arboreal.mesh.Vertex;
import com.simsilica.builder.Builder;
import com.simsilica.builder.BuilderReference;

import com.simsilica.builder.BuilderState;

Then in fileActionState I got an unknown class on TreeParameters at line 251 and 259

then ForestGridState references elements from the lemur node
com.simsilica.lemur.props
which doesn’t exist

and there are a few more like this

Adrien

Continuing on the idea of the billboarding, I’ve played a bit with the concept and created a control to which you provide a node containing the regular mesh and a simple quad. There is a control attached to each node (each node represents a tree) and that control will swap the two geometries (removing one and adding the other) depending on the distance and rotate the quad accordingly to the camera (based on the demo from the jme3 library)

I am very surprised to see that my map with billboarding is slower than the one that only has meshes. You can see below that the object count is about the same, and as expected the triangle count of the billboard scene is 4M triangles vs 11M for all meshes

I will investigate more why the fps is dropping, but happy to hear if you can think of anything

Well, if you are checking the distance for every tree once per frame, that’ll do it. I was thinking more along the lines of only checking the distance per small chunk of trees, and maybe do some kind of time slicing so you’re only checking one chunk of trees a frame or so. Maybe check every chunk of trees once a second, though not all at once. In addition, computing the billboard rotation for every billboard every frame may be having an impact. Might be better to do it in a vertex shader.

I downloaded the IsoSurfaceDemo for Windows and tried to run it, but I got an error dialog: “The registry refers to a nonexistent Java Runtime Environment installation of the runtime is corrupted. The system cannot find the file specified.”

1 Like

I’m not sure about billboard trees, but flatpoly trees computing only on GPU :slight_smile:

These are all up on the simsilica github… or you can just download the premade binary.

Or just checkout SimArboreal and run:
gradle run

…and let it build itself. You have to install gradle first but that’s pretty trivial.