1
\$\begingroup\$

This question is deliberately written in a "High-Level" manor to avoid screeds and screeds of code snippets, hopefully I can get my point across, I am using C++ and OpenGL.

I have a game engine, and in the engine I have my "game loop" that is called every frame, one of the things in this game loop is my call to Render(), before a call to SwapBuffers(), nothing new there.

Essentially my engine draws the scene using a scene graph, that is I have GameObject's and each object has a GameComponent. So when the Render() is called in the "game loop" I get the root GameObject and render its GameComponent and then move onto the next GameObject and render its GameComponent, etc. until all GameObject's and each of their GameComponent's are drawn using glDrawElements(...).

One of the GameComponent's a GameObject can have is a MeshRenderer, that is a Mesh paired with a Texture. In my scene I want to add loads of tree models, therefore I create for example 50 GameObject's that each have a MeshRenderer component (a tree) that will eventually in the game loop described above be drawn.

The issue with this is that the time it takes to render a frame is directly proportional to the number of tree models I have (O(N) I think is how you word it). For example with 100 trees the render time is 23ms, with 200 trees it is 46ms, with 400 trees it is 90ms so on and so forth.

This is terrible, I need at least 1000 trees in my "game world" and even at around 400 my game is "laggy". Because the time to draw a frame is proportional to the number of tees I know that each frame it is re-drawing each mesh essentially (every frame drawing 100 trees, and then 200 and so on, and the time to draw scales with it)

My question is this - How can I store the mesh data so that I don't need to draw them from scratch every frame, I can just draw them once, and then re-use that data for the next frame, but don't re-draw it all? is there a way to firstly use glDrawElements(...), then store that mesh data(all the vertices etc.) and then every frame after call something like for example glDrawExcistingElements() instead of glDrawElements(...). Ultimately meaning that for consecutive frame draws the time is the exact same wether I have 1 tree or 1000 trees because the system is simply saying "here is the scene I already drew", instead of "lets draw it all again from scratch".

\$\endgroup\$
  • 1
    \$\begingroup\$ It sounds like you want to use batching or instancing to combine these 1000 trees into fewer draw calls. \$\endgroup\$ – DMGregory Feb 29 at 20:07
  • \$\begingroup\$ @DMGregory I have never heard of "batching or instancing", how would I go about doing this? perhaps you know of a good source that could help me? \$\endgroup\$ – Kyle_Pearce Feb 29 at 20:35
  • 1
    \$\begingroup\$ Search engines remain free, and are a great way to get background on a term you haven't heard of before. Start there, or there. \$\endgroup\$ – DMGregory Feb 29 at 20:40
  • 1
    \$\begingroup\$ What DMGregory has written about batching and instancing are very good ways to reduce draw calls. One thing I would also like to recommend is checking up on something called frustum culling, which basically means that everything outside of the camera's frustum (viewport/view area) is skipped during the render phase, which means that only primitives that can be seen by the camera is rendered, the rest is not. \$\endgroup\$ – Daniel_1985 Feb 29 at 23:12
1
\$\begingroup\$

As said in comments, what you are looking for is ptobably instancing: instead of gldrawElements you use glDrawElementsInstanced. The first thing you want too look for is probably this tutorial. They give an example of drawing 100.000 asterodis(!) with complexity of ~500 vertices with one draw call and still having excelent FPS. Without it the program chokes on ~1k asteroids with something on <10FPS level.

For further optimizations you would have too look more into LODding (reducing mesh complexity the farther the object is - for example farthest trees could be just 2d sprites). Some kind of occlusion culling (frustum, occlusion) could be a next step (reducing the amount of overdraw for pixels).

Also remember that in an opengl program changing any state should be done as rarely as possible. It means that you should group your rendering calls by the state they depend upon, including mesh data, textures, element arrays etc. The state remains bound as long as you need it and explicitely need it. If you want to use the same mesh for many objects (for example trees with different textures, it's called static mesh in some engines) you don't have to bind/unbind mesh data everytime you render a tree, but just store for example a uniform indexed by instance_id into the texture atlas (like minecraft does for example).

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.