Optimizing rendering for many spheres

Started by
7 comments, last by trbabb 16 years, 8 months ago
I will be attempting to speed up some code that renders a bunch (tens of thousands and up) of solid lit spheres (think molecular dynamics simulator). The current code sticks a gluSphere in a display list and then calls that list for each sphere (with appropriate scales and translates and colors). What would be a good approach to speed this app up? Some approaches I am considering: 1. Billboarding using image of lit sphere as texture, with alpha values/alpha testing to draw only the sphere. The thought of calculating the billboard transform per sphere is a concern, but sending down one quad per sphere rather than O(100) for gluSphere (plus normals plus lighting calcs) ought to be a win. 2. Same as above, but using point sprite extension. Avoids the billboard calcs, but depends on the driver supporting point sprites of a useful size (maybe 256?), and I am not sure how to specify the size in modelling coordinates. Do I have to convert the physical radius of each sphere to pixels given the position and current view transform? Also, the issue of clipping based on the point location (causing spheres to pop in and out near the edges of the window) is ugly. 3. Vertex shader. I know next to nothing about this - is it applicable? If so, where should I go look? 4. Something else I haven't thought of? I think draw elements or vertex arrays can be used along with any of the above approaches. Frustum culling would help for some situations, occlusion probably not (because there are lots of little objects rather than relatively few larger ones). Thanks in advance for any thoughts- Chris
Advertisement
#1 will be the best method, visually theres not gonna be much difference (perhaps use actual spheres for the stuff close to the camera)
the calculations are not gonna be a concern
Quote:Original post by cmm
I will be attempting to speed up some code that renders a bunch (tens of thousands and up) of solid lit spheres (think molecular dynamics simulator). The current code sticks a gluSphere in a display list and then calls that list for each sphere (with appropriate scales and translates and colors).

What would be a good approach to speed this app up?
The fastest code is the one which will never be executed. If you have 10k molecules each counting say 2 atoms then there will be 20k spheres and 10k links. I don't know about your hardware but I hardly believe the human eye can perceive this. Consider dropping the rendering of far away detail.

Also, you should have a better indication of your bottleneck. The following supposes you're vertex-transform limited.

Quote:Original post by cmm
1. Billboarding using image of lit sphere as texture, with alpha values/alpha testing to draw only the sphere. The thought of calculating the billboard transform per sphere is a concern, but sending down one quad per sphere rather than O(100) for gluSphere (plus normals plus lighting calcs) ought to be a win.
Remember that when using batches shorter than 1k unique vertices the driver cost involved cancels out the real performance cost. Up to 1k vertices you should think at vertex transform like a O(1) operation. Drawing 4 unique vertices doesn't perform much faster than drawing 500 (it may be 10 times faster but not 100 as one would expect). Make a few tests and plot the results on a line. Up to 64k vertices the xform performance raises dramatically with the number of vertices.
Also consider what you're losing.
For correct shading you'll either have to use per-pixel techniques or recompute the billboard often. You also give up correct intersection.
It doesn't seem a good idea to use this method for all the entities, but only for those hundreds far away.

If the whole molecule is going to be replicated, build a batch drawing it completely. You'll find that drawing the whole H2O molecule (with links) isn't much slower than drawing oxygen alone.
Quote:Original post by cmm
2. Same as above, but using point sprite extension. Avoids the billboard calcs, but depends on the driver supporting point sprites of a useful size (maybe 256?), and I am not sure how to specify the size in modelling coordinates. Do I have to convert the physical radius of each sphere to pixels given the position and current view transform? Also, the issue of clipping based on the point location (causing spheres to pop in and out near the edges of the window) is ugly.
Exactly, point sprites are dead.
Quote:Original post by cmm
3. Vertex shader. I know next to nothing about this - is it applicable? If so, where should I go look?
I don't think. Vertex shaders define how each vertex is transformed, they have nothing to do with replicating geometry.
Some more exotic things coming to mind are instancing (this may be worth a look) and GS vertex replication (which I don't think to be good enough for this).
Quote:Original post by cmm
4. Something else I haven't thought of? I think draw elements or vertex arrays can be used along with any of the above approaches.
Both will save little if you use billboarding: there's not enough work to save on the vertex side.
Quote:Original post by cmm
Frustum culling would help for some situations, occlusion probably not (because there are lots of little objects rather than relatively few larger ones).
Depends on the intended usage pattern as well. Frustum culling is generally a good idea but if you have a molecule cloud and the user will be 80% of its time out of this cloud, there's little point in implementing it. Occlusion does have similar issues. In both cases, grouping molecules in clusters is probably required to not hog your CPU - checking 100k tests won't be a problem nowadays if you're just drawing but when you add something other it may quickly become a problem.

Previously "Krohm"


I implemented the billboard and point sprite versions and compared them to the original approach (a gluSphere in a display list), if anyone is interested I can post code. For each sphere, color and position are specified. Tested on an ATI X600, Gforce 6600GT, and Quadro Fx560 and the results scaled fairly similarly across these.

At 10,000 spheres, calling a display list containing a gluSphere for each position was around 5 to 10 FPS, the billboards and point sprite methods were at 60 FPS (vsync limit). At 100,000 spheres, the dlist/gluSphere method drops to 1 to 3 FPS, billboards drop to between 30 and 40, point sprites stayed at 60. At 1 million, dlist is less than 1, billboards are around 4-5, point sprites are about 15 FPS.


The point sprites are the clear winners in performance, but the drawbacks mentioned in my original post remain, so I will implement billboards, at least initially.

My only remaining question is how to match the appearance of the lit gluSpheres using billboards. The original code uses shininess and specular reflections (with a single fixed directional light) and I haven't figured out how to reproduce that effect via texture mapping while still being able to specify a color per sphere. Is there a way to post images here?
Hmm..

Render the a grayscale sphere to texture, then use glTexEnv(GL_MODULATE)? I haven't used this before myself, but it sounds like it might be what you need.

I think you mean GL_BLEND, you can't get a white specular highlight via glTexEnv(GL_MODULATE), because the resulting color for GL_MODULATE is Ct * Cf, where Ct is the color of the texture and Cf is the color of the fragment.

If I want to draw a red sphere, Cf is (1,0,0) and Ct would be (1,1,1) at the specular highlight location, so GL_MODULATE produces an output of (1,0,0) (red, not the desired white highlight).

Actually the texture is 2 channels, luminance (or intensity) and alpha. The alpha channel is binary (0/255) and is just used to trim off the corners of the quad via alpha testing so only a disk is drawn for each "sphere". The luminance is just a "light map" (I think that is the term) - ie, it is 1 where the specular highlight should be.

So using glTexEnvi(GL_BLEND), with a GL_TEXTURE_ENV_COLOR Cc of (1,1,1), the resulting color is (1-Ct) * Cf + Ct * Cc, which is pretty close to what I want (the sphere is white where the texture is white, but red where the texture is darker), but it doesn't match the colors of the lit solid spheres. I wanted to post images because the differences are hard to describe.

Maybe I just need to play with the texture values or the GL_TEXTURE_ENV_COLOR some more.

Oh, I see the problem.

Okay, here's what I think you want. I don't know if there's a way to do this in the pipeline; you may need to write a shader:

Work in HSV space. Get H and S from the GL_COLOR, and let your sphere texture be the V.

the balls here are in fact sprites
to handle lighting correctly u will need to use a normalmap
for these ball textures rgb==normal + a==alpha (for alphatest)
A normal map wouldn't be a bad idea either, although if you are using directional lights there is probably no need for that, and might be more costly.

If you WERE to use normal mapping, hell, you could compute the normal analytically, since your object is known to be a sphere. Then you'd ONLY need alpha.

This topic is closed to new replies.

Advertisement