I have a scene that contains a very large number of osg::Drawables.
I am currently using osg::SmoothingVisitor to compute the normals for each geometry. This creates a normal per vertex. If I turn normals off I see a sigificant reduction in the amount of memory in use, hence I am currently looking at implementing a visitor to compute 1 normal per triangle which would still give the appearance I need and reduce the memory load.
However, I am not sure of the correct way to binding normals to the geometry:
In osg::SmoothingVisitor this is done using:
geom.setNormalArray( normals );
geom.setNormalIndices( geom.getVertexIndices() );
geom.setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
My geometries are mostly defined using quads and triangle strips. If I use osg::Geometry::BIND_PER_PRIMITVE I am guessing that this will try to bind to a quad or triangle strip not a triangle as I would like. Is this correct?
Using a single normal for a triangle strip is not going to give me a suitable view of the geometry.
Is anyone able to point me in the right direction?
Many Thanks for your help.
James
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=19269#19269
_______________________________________________
osg-users mailing list
osg-...@lists.openscenegraph.org
http://lists.openscenegraph.org/listinfo.cgi/osg-users-openscenegraph.org
Yes.
> Using a single normal for a triangle strip is not going to give me a suitable view of the geometry.
>
You've already worked it all out, you don't need anything else.
Though beware that using PER_PRIMITVE drops you onto OSG's slow render pass,
so you may take a big performance hit.
> If I turn normals off I see a sigificant reduction in the amount of memory in use, hence I am currently looking at implementing a visitor to compute 1 normal per triangle which would still give the appearance I need and reduce the memory load.
As Simon mentioned, using per primitive bindings drop you down to OpenGL
slow paths, so if you have a large number of drawables you will surely
see a performance hit. Additionally, one normal per primitive will mean
that you will have a faceted look which may not be what you want.
You may find that in your case, you just have to bite the bullet and
accept the higher memory usage.
You could of course reduce the number of vertices your geometry has.
Otherwise, a possible alternative would be using PagedLOD with objects
split up into various LODs so that when objects are further away, they
have less geometry, and when they're closer up OSG automatically loads
up the more refined version (and would expire and unload the lower res
version eventually). But then the scene graph structure becomes a bit
more complicated.
Hope this helps,
J-S
--
______________________________________________________
Jean-Sebastien Guay jean-seba...@cm-labs.com
http://www.cm-labs.com/
http://whitestar02.webhop.org/
Thank you for your responses, I really appreciate the input.
Can I tell you little more about what I'm working on as that may clarify the problem. The application is a CAD app and it must be able to load in literally millions of objects. The objects are low detail meshes and for a worst case scenario, the scene has around 20million vertices. I'm not really concerned about detail, lighting looking too correct or even rendering performance, I'd gladly sacrifice some rendering performance to load the entire model in!
What the system must be able to do is load the entire model (we have some guideline models) into memory, under a 32bit OS limit. A legacy app being replaced uses OpenGL slow paths (Begin/End) and computes the normals manually. This has 1 normal per triangle (or less for some flat surfaces) whereas OSG is using 1 normal per vertex. Sure it looks a LOT better and its also higher performance in OSG, but it's also using approximately double the memory to render the same model.
I have heard about PagedLOD but I am not sure how much of a performance hit this will give us when the objects are loaded/unloaded, as there are literally millions of objects in a small viewable area. I just noticed that disabling the smoothing visitor for objects (ie: having no normals) saved several hundred megabytes, and found out by trial/error that in OSG each vertex Vec3f has a normal (Another Vec3f) and an index associated with it.
So would you happen to know if there is anything I can do (bar PagedLOD) to reduce the memory consumption in this application? I was hoping to target the normal generation and output one normal per triangle as opposed to one per vertex, as this would shave a few hundred megabytes from the app when under load.
Thank you,
James
------------------
Read this topic online here:
http://forum.openscenegraph.org/viewtopic.php?p=19275#19275
> So would you happen to know if there is anything I can do (bar PagedLOD) to reduce the memory consumption in this application? I was hoping to target the normal generation and output one normal per triangle as opposed to one per vertex, as this would shave a few hundred megabytes from the app when under load.
Well, I don't have anything to add. Your requirements are interesting,
and you can certainly try generating normals per primitive to see if
you'll get fast enough results make the savings in memory worth it. If
you have 20 million vertices in view at all times I can't see how
rendering on the slow path would yield a usable frame rate, but it might
be good enough for your needs, I wouldn't know. Try it and see.
If you're not concerned with lighting quality, why do you even need
normals? You could just disable lighting on your objects and do away
with normals entirely...
Here's another trick you could do, if you want lighting on, but this
would require using shaders. Remove your normal arrays, encode the
normals as theta+phi angles (2 floats instead of 3, and can even be
quantized into 2 half-floats which would physically take up only one
32-bit float per vertex) in a texture or vertex attributes, and then
calculate the vertex normal from this in the vertex shader. The
calculation in the shader is trivial, so the question is whether
generating the normals normally (SmoothingVisitor) and then encoding
them when a model is loaded would be too time consuming (note you could
do this as a pre-process before loading the model, and then save the
objects that way and as long as the user doesn't change them, you
wouldn't have to re-do this step each time).
Hope this helps,
J-S
--
______________________________________________________
Jean-Sebastien Guay jean-seba...@cm-labs.com
http://www.cm-labs.com/
http://whitestar02.webhop.org/
Well one idea I've had kicking around in my head for several years,
may be possible with a custom vertex shader.
Take a sphere, generate a mesh for it, take the surface normals and store them.
(well actually you don't need to generate the mesh, working out a
normal for a point on the sphere is trivial)
Then for every triangle/quad, simply chose one of the stored normals
which is 'closest' to your calculated normal
and 'associate' the index for that normal with the primitive. After
all if you are flat shading stuff,
the minor difference between the proper normal and the 'closest'
normal will only have a minor effect on the
resultant shading.
This scheme requires you be able to specify a 'normal index' array, I
don't think you can do this with the fixed pipeline,
which is why you'd need a customer vertex shader.
I'm not familiar enough with shaders though to say it's actually
possible to do that., but it looks doable.
This would save huge amounts of ram and you'd get away with only using
a byte for the normal index.
> I'm not familiar enough with shaders though to say it's actually
> possible to do that., but it looks doable.
Totally, it's good idea. It's actually reminiscent of spherical
harmonics, but in reverse (instead of sampling illumination in all
directions on a sphere, you're choosing the direction that best
represents the normal).
The only thing is that the smallest unit for per-vertex attributes is a
32-bit float, so using a byte doesn't necessarily save anything over a
normal stored as theta+phi angles quantized to two half-floats, and that
way you don't need the lookup table of normals to index into.
But it's a good idea, I agree, as long as you can accept a slight loss
in lighting quality because of approximated normals (will probably show
up as banding on smooth surfaces).
J-S
--
______________________________________________________
Jean-Sebastien Guay jean-seba...@cm-labs.com
http://www.cm-labs.com/
http://whitestar02.webhop.org/
Ah I didn't know that, ty. Seems like an odd limitation though.
How does the angles thing work? Not sure I follow you.
If you did the normal lookup, wouldn't you save a whole load
of shader processing time, or is memory bandwidth a bigger limitation with
shaders?
> Ah I didn't know that, ty. Seems like an odd limitation though.
Well, the video cards are implemented to take in only certain types of
data. Vertices, normals and colors are in general aggregates of 32-bit
floats (which is why no vertex type is ever a vector of 3 doubles).
> How does the angles thing work? Not sure I follow you.
Your intuition is right that normals around a vertex will always be in a
sphere. The thing is that you can encode that as spherical coordinates
(http://en.wikipedia.org/wiki/Spherical_coordinates), where r = 1 (since
a normal is always unit-length). So the direction could be encoded as
two angles, theta and phi, inclination and azimuth (see the Wikipedia
article).
Now, you already save one float that way over storing a 3-component
vector. If you want to save more, since the angles are small numbers,
you can store both of them into the lower and upper parts of a single
32-bit float (two 16 bit numbers) and re-extract them from the float in
the shader.
Another way is you could store only 2 of the 3 components of the normal
- since a normal is unit-length you can calculate the third component
easily. Those numbers will also be small (less than or equal to 1) so
you could do the same transformation into two 16-bit values and store
them into a single float. The spherical coordinates just came to mind
first as I've used it before, but the result would be the same.
> If you did the normal lookup, wouldn't you save a whole load
> of shader processing time, or is memory bandwidth a bigger limitation with
> shaders?
You'd save a little bit of processing time per vertex, but compared to a
texture lookup to get the right normal from the lookup table, I don't
know what would win. Seriously, what I've described above is child's
play for a GPU, and very easily parallelizable, which is what GPUs do best.
It's not really a matter of bandwidth since your lookup table would be
pretty small too. I expect you'd get similar performance in both cases,
for me it's just inconvenient to drag around an extra table when I don't
have to - the angles / quantized vector method above would all fit
nicely in the object itself without needing any extra data.
J-S
--
______________________________________________________
Jean-Sebastien Guay jean-seba...@cm-labs.com
http://www.cm-labs.com/
http://whitestar02.webhop.org/
Vertex indices are just available for backwards compatibility, I would
not recommend using them at all as they force the OSG down on to slow
paths where it assemble the actual data to send to graphics card on
each frame, as graphics cards do not support per primitive normals, or
decoupled vertex indicies.
The most efficient way to use OpenGL/graphcis hardware is with
straight vertex arrays and osg::DrawElements to manage indices.
Robert
The only way to do it with the fixed pipeline I can think of, is basically
just sort all your geometry into group's by the approximate normal, then
you don't have to store the normal index at all so you'd save more memory.
Doing it that way you're using BIND_OVERALL and vertex buffers so
you're still using
osgs fast path. Makes updating geometry a pain, but that doesn't sound like a
problem from your description.
Also with grouping the geometry this way you aren't restricted to a
using a power of
two number of normals, you can just try different numbers of normals
till you get
reasonable looking results.
> Just to throw another requirement into the mix, I don't think we can use
> vertex shaders, as we cannot guarantee what hardware the software will
> be run on. Basically integrated graphics is the lowest possible
> hardware, so what's that, DirectX 8? 9? Vertex shaders run on DX8 but
> you won't get many instructions.
Well you're kind of juggling conflicting requirements. You want the app
to run on low-end graphics, but you want to reduce its CPU-side memory
usage to a minimum by not storing normals. Generally you have a
trade-off to make by using more memory and getting good performance, and
getting lower performance but using less memory. That's just the way it
goes, but you'll have to try to know for sure. No one has a situation
exactly like yours, we can only offer guidance.
We've given you a few different alternatives, now you have to weigh the
pros and cons yourselves. Perhaps try a few of the alternatives in a
little test program before making changes in your software itself, and
test the alternatives in realistic settings (with lots of objects for
example) looking at the stats each time.
If you really want to use normal indices, try it out, but as Robert said
(and I and others have said before in this thread) this will drop you
down to slow paths. If that's good enough for you, then fine, but you
won't know unless you try it.
Hope this helps,