Reputation: 1757
I've seen a similar post about this subject here, however, my question is a little bit different.
I have a 2D plot which will be comprised of circles at varying locations with varying sizes. Currently, my rendering scheme uses a display list to store a pre-drawn circle which can be actively re-sized and translated by the user using glScalef/glTranslatef. However, because I am rendering thousands of circles, the resize and drawing becomes extremely slow. Each circle can have a different radius and color so these things must be done within the loop.
What would be some things I could try to improve the speed of circle rendering when the user changes say the size of the circles? I've looked into VBO like the above link says but it was ambiguous to how much of a performance gain I would receive for this type of application where my object is constantly changing in size.
Upvotes: 3
Views: 3761
Reputation: 52082
because I am rendering thousands of circles, the resize and drawing becomes extremely slow
With just vertex arrays this is getting about 60ms per frame on an Intel HD Graphics 3000 with 10,000 circles:
// g++ -O3 circles.cpp -o circles -lglut -lGL
#include <GL/glut.h>
#include <vector>
#include <iostream>
#include <cmath>
using namespace std;
// returns a GL_TRIANGLE_FAN-able buffer containing a unit circle
vector< float > glCircle( unsigned int subdivs = 20 )
{
vector< float > buf;
buf.push_back( 0 );
buf.push_back( 0 );
for( unsigned int i = 0; i <= subdivs; ++i )
{
float angle = i * ((2.0f * 3.14159f) / subdivs);
buf.push_back( cos(angle) );
buf.push_back( sin(angle) );
}
return buf;
}
struct Circle
{
Circle()
{
x = ( rand() % 200 ) - 100;
y = ( rand() % 200 ) - 100;
scale = ( rand() % 10 ) + 4;
r = rand() % 255;
g = rand() % 255;
b = rand() % 255;
a = 1;
}
float x, y;
float scale;
unsigned char r, g, b, a;
};
vector< Circle > circles;
vector< float > circleGeom;
void init()
{
srand( 0 );
for( size_t i = 0; i < 10000; ++i )
circles.push_back( Circle() );
circleGeom = glCircle( 100 );
}
void display()
{
int beg = glutGet( GLUT_ELAPSED_TIME );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -100 * ar, 100 * ar, -100, 100, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glEnableClientState( GL_VERTEX_ARRAY );
glVertexPointer( 2, GL_FLOAT, 0, &circleGeom[0] );
for( size_t i = 0; i < circles.size(); ++i )
{
Circle& c = circles[i];
c.scale = ( rand() % 10 ) + 4;
glPushMatrix();
glTranslatef( c.x, c.y, 0 );
glScalef( c.scale, c.scale, 0 );
glColor3ub( c.r, c.g, c.b );
glDrawArrays( GL_TRIANGLE_FAN, 0, circleGeom.size() / 2 );
glPopMatrix();
}
glDisableClientState( GL_VERTEX_ARRAY );
glutSwapBuffers();
int end = glutGet( GLUT_ELAPSED_TIME );
double elapsed = (double)( end - beg );
cout << elapsed << "ms" << endl;
}
void timer(int extra)
{
glutPostRedisplay();
glutTimerFunc(16, timer, 0);
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 600, 600 );
glutCreateWindow( "Circles" );
init();
glutDisplayFunc( display );
glutTimerFunc(0, timer, 0);
glutMainLoop();
return 0;
}
Upvotes: 1
Reputation: 1691
Second using instanced arrays with glDrawElementsInstanced or glDrawArraysInstanced as a clean solution that transfers well to other types of geometry.
If you want/need to stick to OpenGL 2 (eg has to run on an iThing for example) and you only need circles, also consider point sprites. Origin of each circle is the point vertex value. Store the radius as the S value of a texture coordinate, the X value of a surface normal, whatever. Enable blending, GL_PROGRAM_POINT_SIZE, maybe point smoothing; and write a vertex shader which just sets gl_PointSize to the radius you want. Instant circles.
Upvotes: 0
Reputation: 52082
ARB_instanced_arrays
-based instancing would probably be the cleanest.
You'll have a single circle with M vertices you'll draw N times, storing your per-circle x/y position, radius, and color as vertex attributes and using glVertexAttribDivisor()
appropriately.
Gets trickier if you want radius-adaptive LOD. You'll probably have to dig into geometry shaders for that.
Upvotes: 0