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.
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;
}
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.
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.
Related
I have to render the mandelbrot set and I was wondering if someone could point out some flaws with my code - at the moment, the output windows just shows a black screen. I think that my mandelbrot mathematics are correct, because I have used the same code to output a .tga of the mandelbrot - is it something to do with the OpenGl method I am using to output the pixels?
Full code:
#include <Windows.h>
#include <GL\glew.h>
#include <GL\freeglut.h>
#include <iostream>
#include <stdlib.h>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <complex>
#include <fstream>
#include <thread>
#include <mutex>
#include <vector>
#include <Windows.h>
// Import things we need from the standard library
using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::complex;
using std::cout;
using std::endl;
using std::ofstream;
// ...other useful includes
using std::cout;
using std::endl;
using std::thread;
using std::mutex;
using std::lock;
using std::unique_lock;
using std::vector;
const int width = 600, height = 600; // window size
int windowID;
// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
// (You may need to turn this up if you zoom further into the set.)
const int MAX_ITERATIONS = 500;
bool fullScreen = false;
bool need_to_draw = true;
//****************************************
// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot(double left, double right, double top, double bottom)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the screen buffer
glBegin(GL_POINTS); // start drawing in single pixel mode
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
// Work out the point in the complex plane that
// corresponds to this pixel in the output image.
complex<double> c(left + (x * (right - left) / width),
top + (y * (bottom - top) / height));
// Start off z at (0, 0).
complex<double> z(0.0, 0.0);
// Iterate z = z^2 + c until z moves more than 2 units
// away from (0, 0), or we've iterated too many times.
int iterations = 0;
while (abs(z) < 2.0 && iterations < MAX_ITERATIONS)
{
z = (z * z) + c;
++iterations;
}
if (iterations == MAX_ITERATIONS)
{
glColor3f(1.0, 0.0, 0.0); // Set color to draw mandelbrot
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
glVertex2i(x, y);
}
else
{
glColor3f(0.0, 0.0, 0.0); //Set pixel to black
// z escaped within less than MAX_ITERATIONS
// iterations. This point isn't in the set.
glVertex2i(x, y);
}
}
}
glEnd();
glutSwapBuffers();
need_to_draw = false;
}
int main(int argc, char** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLsizei windowX = (glutGet(GLUT_SCREEN_WIDTH) - width) / 2;
GLsizei windowY = (glutGet(GLUT_SCREEN_HEIGHT) - height) / 2;
glutInitWindowPosition(windowX, windowY);
glutInitWindowSize(width, height);
windowID = glutCreateWindow("Mandelbrot");
if (need_to_draw)
{
compute_mandelbrot(-2.0, 1.0, 1.125, -1.125);
}
glShadeModel(GL_SMOOTH);
glEnable(GL_DEPTH_TEST);
glViewport(0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glutMainLoop();
return 0;
}
The identity GL_PROJECTION matrix doesn't give you a 1-to-1 unit-to-pixel mapping like you're assuming, it's a +/-(1,1,1) cube.
Use glOrtho() to get the matrix you want:
glOrtho( 0, width, 0, height, -1, 1 );
All together:
#include <GL/glut.h>
#include <complex>
using std::complex;
// Render the Mandelbrot set into the image array.
// The parameters specify the region on the complex plane to plot.
void compute_mandelbrot( double left, double right, double top, double bottom )
{
// The number of times to iterate before we assume that a point isn't in the
// Mandelbrot set.
// (You may need to turn this up if you zoom further into the set.)
const int MAX_ITERATIONS = 500;
const int width = glutGet( GLUT_WINDOW_WIDTH );
const int height = glutGet( GLUT_WINDOW_HEIGHT );
glBegin( GL_POINTS ); // start drawing in single pixel mode
for( int y = 0; y < height; ++y )
{
for( int x = 0; x < width; ++x )
{
// Work out the point in the complex plane that
// corresponds to this pixel in the output image.
complex<double> c( left + ( x * ( right - left ) / width ),
top + ( y * ( bottom - top ) / height ) );
// Start off z at (0, 0).
complex<double> z( 0.0, 0.0 );
// Iterate z = z^2 + c until z moves more than 2 units
// away from (0, 0), or we've iterated too many times.
int iterations = 0;
while( abs( z ) < 2.0 && iterations < MAX_ITERATIONS )
{
z = ( z * z ) + c;
++iterations;
}
if( iterations == MAX_ITERATIONS )
{
glColor3f( 1.0, 0.0, 0.0 ); // Set color to draw mandelbrot
// z didn't escape from the circle.
// This point is in the Mandelbrot set.
glVertex2i( x, y );
}
else
{
glColor3f( 0.0, 0.0, 0.0 ); //Set pixel to black
// z escaped within less than MAX_ITERATIONS
// iterations. This point isn't in the set.
glVertex2i( x, y );
}
}
}
glEnd();
}
void display()
{
glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
const int width = glutGet( GLUT_WINDOW_WIDTH );
const int height = glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( 0, width, 0, height, -1, 1 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
compute_mandelbrot( -2.0, 1.0, 1.125, -1.125 );
glutSwapBuffers();
}
int main( int argc, char** argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA );
glutInitWindowSize( 300, 300 );
glutCreateWindow( "Mandelbrot" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
Here's the screenshot of what I am doing. Currently, I'm stuck from drawing a curved borders into this rectangle.
My first solution was: draw a quartered circle behind the rectangle, but if I adjust the opacity of the shape, as you can see, the quartered circle gets shown.
I know this is pretty basic for you guys but I'm not really good at math.
I did try to reuse the computed edges of the arc and add the size of border but I got this as a result.
I also think of bezier curves as a replacement but I think it is more efficient to just reuse the computed vertices and add all the missing ones. Also, I don't know how to compute for the curved points of bezier curves and finding the right amount of t would be very computationally expensive so I don't implement it.
Here's the code how I draw the inner quartered circle and I think I can just reuse it.
void drawArc(int x, int y,
int startAngle, int endAngle,
uint32_t radiusX, uint32_t radiusY,
int border_x, int border_y,
const rgb color,
const rgb bcX, const rgb bcY,
uint8_t opacity)
{
if (radiusX <= 0 || radiusY <= 0) return;
static constexpr float DTR = 3.14159 / 180;
float cx, cy;
int step;
static std::vector<float> verts;
static std::vector<uint8_t> colors;
if (startAngle < endAngle)
{
step = +1;
++ endAngle;
} else
{
step = -1;
-- endAngle;
}
verts.clear();
colors.clear();
verts.push_back(x);
verts.push_back(y);
colors.push_back(color[R]);
colors.push_back(color[G]);
colors.push_back(color[B]);
colors.push_back(opacity);
while (startAngle != endAngle)
{
cx = cos(DTR * startAngle) * radiusX;
cy = sin(DTR * startAngle) * radiusY;
verts.push_back(x + cx);
verts.push_back(y - cy);
colors.push_back(color[R]);
colors.push_back(color[G]);
colors.push_back(color[B]);
colors.push_back(opacity);
startAngle += step;
}
drawElements(GL_POLYGON, sizeof(arcIndices) / sizeof(arcIndices[0]), GL_FLOAT,
&verts[0], &colors[0], &arcIndices[0]);
if (border_x != 0 || border_y != 0)
{
//remove (x, y)
verts.erase(verts.begin(), verts.begin() + 2);
// float px, py;
//
// px = *(verts.begin() + 0);
// py = *(verts.begin() + 1);
//
// glPointSize(5);
//
// glBegin(GL_POINTS);
//
// glColor3ub(0,0,255);
// glVertex2i(px, py);
//
// px = *(verts.end() - 2);
// py = *(verts.end() - 1);
//
// glColor3ub(255,0,0);
// glVertex2i(px , py);
// glEnd();
//attempting to reuse the edges
//I think the last vertices are opposed
//that's why I got a crossed out lines??
for (int i = 0;i <= 90; ++i)
{
verts.push_back(verts[i + 0] + border_x);
verts.push_back(verts[i + 1] + border_y);
colors.push_back(bcX[R]);
colors.push_back(bcX[G]);
colors.push_back(bcX[B]);
colors.push_back(opacity);
}
//91 = steps from 0-90 degree revolution
//182 = 91 * 2
unsigned int index[182 + 91 * 2];
for (int i = 0;i < 182 + 91 * 2; ++i)
index[i] = i;
drawElements(GL_LINE_LOOP, verts.size() / 2, GL_FLOAT,
&verts[0], &colors[0], &index[0]);
}
}
Edit:
Can't I just reuse the pre-calculated (x,y) before?
Sorry for too much use of pictures
The red dots are pre-calculated (x, y) I'm referring to and just append the next arc base on this.
I'm gonna render many of this kind so I need as efficient as possible(w/o too much use to trigo functions).
Update:
And here is the result I got from using stencil buffer as what Andon M. Coleman suggested:
Btw, as you can see, I am trying to emulate my own UI using OpenGL :D
You expressed an interest in seeing how this could be solved using the stencil buffer yesterday, so I am following up with some basic pseudo-code.
glClearStencil (0x0);
glClear (GL_STENCIL_BUFFER_BIT);
glEnable (GL_STENCIL_TEST);
glStencilFunc (GL_ALWAYS, 0x0, 0x0);
// Add 1 to stencil buffer at every location the object to be bordered is visible
glStencilOp (GL_KEEP, GL_KEEP, GL_INCR);
// Draw your grey object
// Only draw the red border where the grey object was never drawn (stencil = 0x0)
glStencilFunc (GL_EQUAL, 0x0, 0xff);
// Draw your red quarter circles
glDisable (GL_STENCIL_TEST);
Clearing the stencil buffer everytime you draw your outlined object is probably overkill. If you opt to clear the stencil buffer once per-frame instead, you can do some pretty interesting things. For instance, if you drew the outlines as a separate pass after all non-outlined shapes are drawn you could use this stencil buffer setup to outline the union (instead of including the intersection of objects as part of the drawn outline) of any overlapping objects.. this would allow you to construct more complicated shapes from your simple rounded rectangles.
Of course for this to work, your pixel format must have a stencil buffer. I will have to leave that part up to you, because the process of setting that up is implementation specific.
GL_POLYGON is only for convex polygons.
Link together the vertices on your inner and outer radii to form quads/triangles:
#include <GL/glut.h>
#include <cmath>
void Torus2d
(
float angle, // starting angle in radians
float length, // length of arc in radians, >0
float radius, // inner radius, >0
float width, // width of torus, >0
unsigned int samples // number of circle samples, >=3
)
{
if( samples < 3 ) samples = 3;
const float outer = radius + width;
glBegin( GL_QUAD_STRIP );
for( unsigned int i = 0; i <= samples; ++i )
{
float a = angle + ( i / (float)samples ) * length;
glVertex2f( radius * cos( a ), radius * sin( a ) );
glVertex2f( outer * cos( a ), outer * sin( a ) );
}
glEnd();
}
void display()
{
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( -4 * ar, 4 * ar, -4, 4, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
Torus2d( 0, 1.57079633, 2, 1, 20 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
how can drawing small circle in side big circle using algorthim of circle
each circle consist of 9 or 8 point and link each 4point to gather and link this with 4 under and so on
.................................................
........................................................
You need to generate the inner and outer points in one loop.
Give this a shot:
#include <GL/glut.h>
#include <cmath>
void Torus2d( float inner, float outer, unsigned int pts )
{
glBegin( GL_QUAD_STRIP );
for( unsigned int i = 0; i <= pts; ++i )
{
float angle = ( i / (float)pts ) * 3.14159f * 2.0f;
glVertex2f( inner * cos( angle ), inner * sin( angle ) );
glVertex2f( outer * cos( angle ), outer * sin( angle ) );
}
glEnd();
}
void display()
{
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( -4 * ar, 4 * ar, -4, 4, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
Torus2d( 2, 3, 20 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "GLUT" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
Find below a faster (less trigono) solution in Go (can be easily translated to c++) which was inspired by Sieglord's Abode article
func Torus(cx, cy, inner, outer float64, num_segments int) {
theta := 2 * math.Pi / float64(num_segments)
c := math.Cos(theta) //precalculate the sine and cosine
s := math.Sin(theta)
t_in := 0.0
t_out := 0.0
x_in := inner //we start at angle = 0
x_out := outer //we start at angle = 0
y_in := 0.0
y_out := 0.0
gl.Begin(gl.QUAD_STRIP)
for ii := 0; ii <= num_segments; ii++ {
gl.Vertex2f(float32(x_in+cx), float32(y_in+cy)) //output vertex inner
gl.Vertex2f(float32(x_out+cx), float32(y_out+cy)) //output vertex outer
//apply the rotation matrix
t_in = x_in
t_out = x_out
x_in = c*x_in - s*y_in
x_out = c*x_out - s*y_out
y_in = s*t_in + c*y_in
y_out = s*t_out + c*y_out
}
gl.End()
}
I'm developing a simple sprite-based 2D game in C++ that uses OpenGL for hardware-accelerated rendering, and SDL for window management and user input handling. Since it's a 2D game, I'm only ever going to need to draw quads, but because the number of sprites is dynamic, I can never rely on there being a constant number of quads. Consequently, I need to rebuffer all of the vertex data via my VBO each frame (since there may be more or fewer quads than there were in the last frame, and thus the buffer may be a different size).
The prototype program I have so far creates a window and allows the user to add and remove quads in a diagonal row by using the up and down arrow keys. Right now the quads I'm drawing are simple, untextured white squares. Here is the code I'm working with (compiles and works correctly under OS X 10.6.8 and Ubuntu 12.04 with OpenGL 2.1):
#if defined(__APPLE__)
#include <OpenGL/OpenGL.h>
#endif
#if defined(__linux__)
#define GL_GLEXT_PROTOTYPES
#include <GL/glx.h>
#endif
#include <GL/gl.h>
#include <SDL.h>
#include <iostream>
#include <vector>
#include <string>
struct Vertex
{
//vertex coordinates
GLint x;
GLint y;
};
//Constants
const int SCREEN_WIDTH = 1024;
const int SCREEN_HEIGHT = 768;
const int FPS = 60; //our framerate
//Globals
SDL_Surface *screen; //the screen
std::vector<Vertex> vertices; //the actual vertices for the quads
std::vector<GLint> startingElements; //the index where the 4 vertices of each quad begin in the 'vertices' vector
std::vector<GLint> counts; //the number of vertices for each quad
GLuint VBO = 0; //the handle to the vertex buffer
void createVertex(GLint x, GLint y)
{
Vertex vertex;
vertex.x = x;
vertex.y = y;
vertices.push_back(vertex);
}
//creates a quad at position x,y, with a width of w and a height of h (in pixels)
void createQuad(GLint x, GLint y, GLint w, GLint h)
{
//Since we're drawing the quads using GL_TRIANGLE_STRIP, the vertex drawing
//order is from top to bottom, left to right, like so:
//
// 1-----3
// | |
// | |
// 2-----4
createVertex(x, y); //top-left vertex
createVertex(x, y+h); //bottom-left vertex
createVertex(x+w, y); //top-right vertex
createVertex(x+w, y+h); //bottom-right vertex
counts.push_back(4); //each quad will always have exactly 4 vertices
startingElements.push_back(startingElements.size()*4);
std::cout << "Number of Quads: " << counts.size() << std::endl; //print out the current number of quads
}
//removes the most recently created quad
void removeQuad()
{
if (counts.size() > 0) //we don't want to remove a quad if there aren't any to remove
{
for (int i=0; i<4; i++)
{
vertices.pop_back();
}
startingElements.pop_back();
counts.pop_back();
std::cout << "Number of Quads: " << counts.size() << std::endl;
}
else
{
std::cout << "Sorry, you can't remove a quad if there are no quads to remove!" << std::endl;
}
}
void init()
{
//initialize SDL
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, SDL_OPENGL);
#if defined(__APPLE__)
//Enable vsync so that we don't get tearing when rendering
GLint swapInterval = 1;
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &swapInterval);
#endif
//Disable depth testing, lighting, and dithering, since we're going to be doing 2D rendering only
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glDisable(GL_DITHER);
glPushAttrib(GL_DEPTH_BUFFER_BIT | GL_LIGHTING_BIT);
//Set the projection matrix
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, -1.0, 1.0);
//Set the modelview matrix
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
//Create VBO
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
}
void gameLoop()
{
int frameDuration = 1000/FPS; //the set duration (in milliseconds) of a single frame
int currentTicks;
int pastTicks = SDL_GetTicks();
bool done = false;
SDL_Event event;
while(!done)
{
//handle user input
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_KEYDOWN:
switch (event.key.keysym.sym)
{
case SDLK_UP: //create a new quad every time the up arrow key is pressed
createQuad(64*counts.size(), 64*counts.size(), 64, 64);
break;
case SDLK_DOWN: //remove the most recently created quad every time the down arrow key is pressed
removeQuad();
break;
default:
break;
}
break;
case SDL_QUIT:
done = true;
break;
default:
break;
}
}
//Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//replace the current contents of the VBO with a completely new set of data (possibly including either more or fewer quads)
glBufferData(GL_ARRAY_BUFFER, vertices.size()*sizeof(Vertex), &vertices.front(), GL_DYNAMIC_DRAW);
glEnableClientState(GL_VERTEX_ARRAY);
//Set vertex data
glVertexPointer(2, GL_INT, sizeof(Vertex), 0);
//Draw the quads
glMultiDrawArrays(GL_TRIANGLE_STRIP, &startingElements.front(), &counts.front(), counts.size());
glDisableClientState(GL_VERTEX_ARRAY);
glBindBuffer(GL_ARRAY_BUFFER, 0);
//Check to see if we need to delay the duration of the current frame to match the set framerate
currentTicks = SDL_GetTicks();
int currentDuration = (currentTicks - pastTicks); //the duration of the frame so far
if (currentDuration < frameDuration)
{
SDL_Delay(frameDuration - currentDuration);
}
pastTicks = SDL_GetTicks();
// flip the buffers
SDL_GL_SwapBuffers();
}
}
void cleanUp()
{
glDeleteBuffers(1, &VBO);
SDL_FreeSurface(screen);
SDL_Quit();
}
int main(int argc, char *argv[])
{
std::cout << "To create a quad, press the up arrow. To remove the most recently created quad, press the down arrow." << std::endl;
init();
gameLoop();
cleanUp();
return 0;
}
At the moment I'm using GL_TRIANGLE_STRIPS with glMultiDrawArrays() to render my quads. This works, and seems do be pretty decent in terms of performance, but I have to wonder whether using GL_TRIANGLES in conjunction with an IBO to avoid duplicate vertices would be a more efficient way to render? I've done some research, and some people suggest that indexed GL_TRIANGLES generally outperform GL_TRIANGLE_STRIPS, but they also seem to assume that the number of quads would remain constant, and thus the size of the VBO and IBO would not have to be rebuffered each frame. That's my biggest hesitation with indexed GL_TRIANGLES: if I did implement indexed GL_TRIANGLES, I would have to rebuffer the entire index buffer each frame in addition to rebuffering the entire VBO each frame, again because of the dynamic number of quads.
So basically, my question is this: Given that I have to rebuffer all of my vertex data to the GPU each frame due to the dynamic number of quads, would it be more efficient to switch to indexed GL_TRIANGLES to draw the quads, or should I stick with my current GL_TRIANGLE_STRIP implementation?
You'll probably be fine using un-indexed GL_QUADS/GL_TRIANGLES and a glDrawArrays() call.
SDL_Surface *screen;
...
screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 0, SDL_OPENGL);
...
SDL_FreeSurface(screen);
Don't do that:
The returned surface is freed by SDL_Quit and must not be freed by the caller. This rule also includes consecutive calls to SDL_SetVideoMode (i.e. resize or resolution change) because the existing surface will be released automatically.
EDIT: Simple vertex array demo:
// g++ main.cpp -lglut -lGL
#include <GL/glut.h>
#include <vector>
using namespace std;
// OpenGL Mathematics (GLM): http://glm.g-truc.net/
#include <glm/glm.hpp>
#include <glm/gtc/random.hpp>
using namespace glm;
struct SpriteWrangler
{
SpriteWrangler( unsigned int aSpriteCount )
{
verts.resize( aSpriteCount * 6 );
states.resize( aSpriteCount );
for( size_t i = 0; i < states.size(); ++i )
{
states[i].pos = linearRand( vec2( -400, -400 ), vec2( 400, 400 ) );
states[i].vel = linearRand( vec2( -30, -30 ), vec2( 30, 30 ) );
Vertex vert;
vert.r = (unsigned char)linearRand( 64.0f, 255.0f );
vert.g = (unsigned char)linearRand( 64.0f, 255.0f );
vert.b = (unsigned char)linearRand( 64.0f, 255.0f );
vert.a = 255;
verts[i*6 + 0] = verts[i*6 + 1] = verts[i*6 + 2] =
verts[i*6 + 3] = verts[i*6 + 4] = verts[i*6 + 5] = vert;
}
}
void wrap( const float minVal, float& val, const float maxVal )
{
if( val < minVal )
val = maxVal - fmod( maxVal - val, maxVal - minVal );
else
val = minVal + fmod( val - minVal, maxVal - minVal );
}
void Update( float dt )
{
for( size_t i = 0; i < states.size(); ++i )
{
states[i].pos += states[i].vel * dt;
wrap( -400.0f, states[i].pos.x, 400.0f );
wrap( -400.0f, states[i].pos.y, 400.0f );
float size = 20.0f;
verts[i*6 + 0].pos = states[i].pos + vec2( -size, -size );
verts[i*6 + 1].pos = states[i].pos + vec2( size, -size );
verts[i*6 + 2].pos = states[i].pos + vec2( size, size );
verts[i*6 + 3].pos = states[i].pos + vec2( size, size );
verts[i*6 + 4].pos = states[i].pos + vec2( -size, size );
verts[i*6 + 5].pos = states[i].pos + vec2( -size, -size );
}
}
struct Vertex
{
vec2 pos;
unsigned char r, g, b, a;
};
struct State
{
vec2 pos;
vec2 vel; // units per second
};
vector< Vertex > verts;
vector< State > states;
};
void display()
{
// timekeeping
static int prvTime = glutGet(GLUT_ELAPSED_TIME);
const int curTime = glutGet(GLUT_ELAPSED_TIME);
const float dt = ( curTime - prvTime ) / 1000.0f;
prvTime = curTime;
// sprite updates
static SpriteWrangler wrangler( 2000 );
wrangler.Update( dt );
vector< SpriteWrangler::Vertex >& verts = wrangler.verts;
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// set up projection and camera
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
double w = glutGet( GLUT_WINDOW_WIDTH );
double h = glutGet( GLUT_WINDOW_HEIGHT );
double ar = w / h;
glOrtho( -400 * ar, 400 * ar, -400, 400, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_COLOR_ARRAY );
glVertexPointer( 2, GL_FLOAT, sizeof( SpriteWrangler::Vertex ), &verts[0].pos.x );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( SpriteWrangler::Vertex ), &verts[0].r );
glDrawArrays( GL_TRIANGLES, 0, verts.size() );
glDisableClientState( GL_VERTEX_ARRAY );
glDisableClientState( GL_COLOR_ARRAY );
glutSwapBuffers();
}
// run display() every 16ms or so
void timer( int extra )
{
glutTimerFunc( 16, timer, 0 );
glutPostRedisplay();
}
int main(int argc, char **argv)
{
glutInit( &argc, argv );
glutInitWindowSize( 600, 600 );
glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
glutCreateWindow( "Sprites" );
glutDisplayFunc( display );
glutTimerFunc( 0, timer, 0 );
glutMainLoop();
return 0;
}
You can get decent performance with just vertex arrays.
Ideally most/all of your dts should be <= 16 milliseconds.
I need to draw a Polygon iteratively. for example, I want to draw a Polygon with 8 corners. I need to draw the first line with GL_LINES and then draw the second line with the same length and an angle of 135° between them, the third line has also an angle of 135° to the second line, etc.
I want to make a loop to render it but I don't know how. I have an approach, but it doesn't work properly.
the second point of line n-1 should be the first point of n and so on...
At the end, I need to get a closed Polygon. the last point of the last line should be the first point of the first line.
Use GL_LINE_LOOP, that will connect your last vertex to your first automatically:
#include <GL/glut.h>
#include <cmath>
void glPolygon( unsigned int sides )
{
if( sides < 3 ) return;
const float PI = 3.14159;
const float step = ( 2 * PI ) / static_cast< float >( sides );
glBegin( GL_LINE_LOOP );
for( unsigned int i = 0; i < sides; ++i )
{
glVertex2f( cos( i * step ), sin( i * step ) );
}
glEnd();
}
void display()
{
glClear( GL_COLOR_BUFFER_BIT );
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
double ar = glutGet( GLUT_WINDOW_WIDTH ) / (double)glutGet( GLUT_WINDOW_HEIGHT );
glOrtho( -2 * ar, 2 * ar, -2, 2, -1, 1);
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glColor3ub( 255, 0, 0 );
glPolygon( 8 );
glutSwapBuffers();
}
int main( int argc, char **argv )
{
glutInit( &argc, argv );
glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
glutInitWindowSize( 640, 480 );
glutCreateWindow( "Polygons" );
glutDisplayFunc( display );
glutMainLoop();
return 0;
}
If you're dead-set on GL_LINES this works:
void glPolygonLines( unsigned int sides )
{
if( sides < 3 ) return;
const float PI = 3.14159f;
const float step = ( 2 * PI ) / static_cast< float >( sides );
glBegin( GL_LINES );
for( unsigned int i = 0; i < sides; ++i )
{
unsigned int cur = ( i + 0 ) % sides;
unsigned int nxt = ( i + 1 ) % sides;
glVertex2f( cos( cur * step ), sin( cur * step ) );
glVertex2f( cos( nxt * step ), sin( nxt * step ) );
}
glEnd();
}