First of all, thanks to the folks who built this library.
I'm building a 3D game engine based on osg::CompositeViewer featuring rather larger worlds built using cubes. I've attached an image of what I'm trying to create.
I was using ShapeDrawable for the blocks until I read I would get faster rendering by building the geometry myself using vertex arrays.
I've implemented something like an octree to create a balanced tree upon level load. The nodes of the tree are Group objects and the leaves are Geode objects with multiple Geometry objects attached (the faces of each block.) Empty "cells" are culled. The minimum cell size for the octree division is 2x2x2 (so a leaf node can contain up to 8 blocks).
The octree doesn't seem to have given me much of a performance boost in the culling department. I am guessing this is because the leaves of the tree contain so many geometry objects?
I've had a read of the forum and google and have come to the conclusion I should do something like the following to improve performance:
1. Group up my geometry objects to perform "batch rendering" - i.e. render more than a single face of a single block in one go.
How can I do this? Do I need to create a single geometry object containing vertex arrays etc of a whole chunk of block faces?
2. Re-use a single geometry object for block faces. Apparently there is code in osgParticle::PrecipitationEffect which does this - although I haven't checked it out yet.
3. "Instanced rendering" I'm not sure quite what this is about, but is what osg::Impostor does? I just had a look at osgimpostor.exe for the first time while writing this, and it seems pretty close to what I want to achieve.
Anyway I've kinda answered some of my own questions in writing this, but I would be thankful for some clarification on the finer points.
I'll be digging into osgimpostor now anyway. :D
Cheers,
David W
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=30514#30514
Attachments:
http://forum.openscenegraph.org//files/deditor_156.png
_______________________________________________
osg-users mailing list
osg-...@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
David Wilson wrote:
> Hi all,
>
> First of all, thanks to the folks who built this library.
>
> I'm building a 3D game engine based on osg::CompositeViewer featuring rather larger worlds built using cubes. I've attached an image of what I'm trying to create.
>
> I was using ShapeDrawable for the blocks until I read I would get faster rendering by building the geometry myself using vertex arrays.
>
> I've implemented something like an octree to create a balanced tree upon level load. The nodes of the tree are Group objects and the leaves are Geode objects with multiple Geometry objects attached (the faces of each block.) Empty "cells" are culled. The minimum cell size for the octree division is 2x2x2 (so a leaf node can contain up to 8 blocks).
>
> The octree doesn't seem to have given me much of a performance boost in the culling department. I am guessing this is because the leaves of the tree contain so many geometry objects?
>
Possibly. An octree design generally works well with a scene graph
architecture, so the idea itself is a good one.
> I've had a read of the forum and google and have come to the conclusion I should do something like the following to improve performance:
>
> 1. Group up my geometry objects to perform "batch rendering" - i.e. render more than a single face of a single block in one go.
>
> How can I do this? Do I need to create a single geometry object containing vertex arrays etc of a whole chunk of block faces?
>
Yeah, that's a start. You definitely don't want to have one Geometry
per face. Ideally, you want to do as much as you can in one primitive
set (each primitive set is a draw call, and you want to cut down on draw
calls as much as possible).
Obviously, you'll have to balance this goal with your octree design as well.
> 2. Re-use a single geometry object for block faces. Apparently there is code in osgParticle::PrecipitationEffect which does this - although I haven't checked it out yet.
>
> 3. "Instanced rendering" I'm not sure quite what this is about, but is what osg::Impostor does? I just had a look at osgimpostor.exe for the first time while writing this, and it seems pretty close to what I want to achieve.
>
osg::Impostor is an older technique for representing relatively complex
objects with a single billboard face. I don't think this will help you.
Instanced rendering is different. It lets you draw multiple instances
of the same object very efficiently by using a single copy of the vertex
data and a vertex shader to do the positioning for each instance of the
object (often, an additional vertex attribute is used to hold the
position of each instance). Look at the osgdrawinstanced example for
details.
Hope this helps.
--"J"
> The octree doesn't seem to have given me much of a performance boost in the culling department. I am guessing this is because the leaves of the tree contain so many geometry objects?
The octree will only help significantly when the cull traversal is a
performance bottleneck and you want to cull as much as the subgraph as
quickly as possible and the scene is distributed right across the 3d
volume. More usual 3d worlds sit on terrain which limits the height
range that things are distributed so the useful of octree is far less,
and quad tree is the prefered structure of the scene graph.
> I've had a read of the forum and google and have come to the conclusion I should do something like the following to improve performance:
>
> 1. Group up my geometry objects to perform "batch rendering" - i.e. render more than a single face of a single block in one go.
I'd go much further than just this, batch rendering for me is pulling
together as much vertex and primitive data into a single osg::Geometry
as is efficient. The "as is efficient" part vary from app to app.
Generally principles to follow is the all the data you are grouping
together sit in the local spatial area so the bounding volume remains
quick tight for the amount of vertex data in it - think about
maximizing vertex density in the bounding volumes.
Also consider that the OpenGL driver and graphics hardware will like
batches of geometry of a thousands of vertices to tens or even
hundreds of thousands, small batches such as 100 or less not efficient
at all as the call overheads are very high, and very large batches
such as a million vertices or more can lead to OpenGL driver and
graphics hardware struggling to managing moving and storing the data
around concurrently.
Using bigger batches is win from several perspectives - less scene
graph nodes to traverse in cull and draw dispatch, less OpenGL
commands, better utilization of the graphics hardware. Put too much
data together into single really large batches then you can have an
impact of culling geometry, as well as potentially stall the graphics
pipeline.
The sweet spot size will depend upon your apps needs and the type of
hardware, but you'll probably be surprised at how big the batches will
be for max efficiency.
> How can I do this? Do I need to create a single geometry object containing vertex arrays etc of a whole chunk of block faces?
Yep. You even better can still loads of cubes in one osg::Geometry
too all with the same local area to keep the density up. If you are
putting a thousand to then thousand vertices in a single osg::Geometry
then you will probably be doing things pretty efficiently.
> 2. Re-use a single geometry object for block faces. Apparently there is code in osgParticle::PrecipitationEffect which does this - although I haven't checked it out yet.
You could do this, but now the geometry instancing is available in
hardware you might be best to just use it instead...
> 3. "Instanced rendering" I'm not sure quite what this is about, but is what osg::Impostor does? I just had a look at osgimpostor.exe for the first time while writing this, and it seems pretty close to what I want to achieve.
Imposters are no longer something I would recommend except for some
very specific applications, and even those would be best served by a
custom imposter implementation.
Geometry instancing is something you should look at though. Also if
you don't have to use cubes then perhaps point sprites would be a
useful.
> I'll be digging into osgimpostor now anyway. :D
A decade ago it might have been useful, but not with modern graphics
cards. Have a look at osgdrawinstanced and osgpointsprite.
Robert.
Thanks very much for the helpful replies!
Turns out my framerate was suffering because I was using a PositionAttitudeTransform object for every block in the scene. I was using it to test integration of Bullet physics and I got a huge boost when I removed it for terrain blocks.
The octree turns out to have given me a large performance boost, and the advice about grouping up vertex data will no doubt give me the kind of framerates I'm looking for in an open-world environment.
One last question though - I'm implementing the cube faces as triangles. Would I be able to scrounge a little more performance by implementing them as quads instead? I'm guessing it would probably be marginal but might decrease memory use a little.
Anyway, thanks again!
David Wilson
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=30925#30925
I doubt you'll notice any difference. The quads will be tessellated to
triangles (by the OpenGL driver, I think) before they're transformed and
shaded anyway.
--"J"