When I was testing out character shadows on Hale's Riverhurst level, the framerate was not as smooth as I would like it to be. To remedy this, I'm taking a few days to make the engine handle complex scenes more efficiently.
My first step was to create a system for fast object instancing (copy-drawing). Because of our modular plants and buildings, most Overgrowth levels consist of many instances (copies) of relatively few unique models, so this is an important feature to optimize. As an example, Riverhurst contains about 4000 instances of the same wooden plank model.
Trees are also very good candidates for fast instancing. Here is one of the trees in Overgrowth:
As you can see in the bottom-left of the screenshot, the framerate is very smooth: 90 frames per second. However, if I create a few dozen more trees, the framerate drops down to 14 -- unacceptably low.
This is because the renderer is not taking advantage of the fact that the trees are using the same model. The rendering algorithm looks much like this:
setup rendering state
setup shader
setup texture
setup vertex arrays
setup transform
draw
}
This is a problem, because for simple models like this tree, the setup code takes much more time than the actual drawing. This is compounded by the fact that the tree is actually a collection of even simpler objects!
I decided to restructure the rendering loop to look more like this:
setup rendering state
setup shader
setup texture
setup vertex arrays
for each object in object-type {
setup transform
draw
}
}
The drawing itself is already as fast as possible, since it's just drawing a vertex buffer object with one line of code, but I further optimized the "setup transform" step by encoding the transformation matrix in the texture coordinates (aka GLSL pseudo-instancing).
Now we can draw that same group of trees at 90 frames per second!
It's still possible to slow the framerate down, but it takes many more trees than before:
Shadows are disabled in these pictures because I'm currently working on efficient texture atlases for shadow maps.
This kind of optimization may seem premature, but I think it's necessary so that we can see what level designs are technologically possible. When planning out the story and gameplay, it's important to know whether or not dense forest scenes and complex town scenes can be rendered smoothly.
Do you have any ideas about further optimizations I could do, or any questions about how the object instancing works?(permalink)
Track us on ModDB (visit our page)
Doesn't this mean that all the instances will have identical lighting?
Not with efficient texture atlases for shadow maps
awesome, but the lighting on objects is same?
no, but I don't know why/how to explain
Wiki: En.wikipedia.org
So is it possible with this technique to give every model a different scale and rotation? I would think so, but I still want to know for sure :)
I don't really like the tree model btw...
Yes you can have a different scale, rotation and lighting. Think of it like inheritance, you can have the same method called once but all the objects are scattered in an array list and individually being instanced with certain data such as position
Haha, I like this approach. Instead of telling your mapper to make a better optimized map, you just optimize the entire engine. Nice. :P
Nice!
I follow your posts with great interest and I look forward to playing the completed game.
Your tree doesn't seem too healthy though.
I understand you may want this tundra look with only a few branches, so a good way to make it look WAY better without lowering the frame rate too much would be to complexify the model for the trunk.
Otherwise it just looks like a big branch/stick planted in the ground.
Cool.
Isn't it possible to mirror all the models (not including shadows, which is applied separately) so that all the trees are actually redirected from 1 source and is like a "illusion" of a tree?
In reality, if you take a tree and put a mirror next to it, you get to see two trees that's mirrored and reflected.
Then to apply that to the game, you'd have to somehow grab the model and put it in that area, and have it not eat away the fps.
Since it's a "mirrored" object, it should only take up resources for the mirror itself...
And here I am going on into things I don't understand, as the more sense I try to make of what I say the more complex it gets...
The idea is good. There is something along that way. Not a mirror, but it's called impostors and is used to render clouds and volumetrics for example [1].
How it works is, you basically render a complex object onto a texture and put that texture onto a sprite. As you get closer, you only update the sprite every now and then, saving render time. Getting closer still, you'll have to substitute it for a real model, though. This way rendering objects (particularly trees) which are far way can be sped up a lot.
[1] Markmark.net
Could you make Level of detail model for trees farther away consisting of one plane that is always facing you? The old trick that they used back in the days.
Nice.