Vbo - draw batch of vertices?

Hi all

This a general question. For a live performance I am writing a piece of code whose aim is to control thousands of particles real-time, with different behaviors which are modified during the perfomance.

My question is: Vbos are the right place to “store” all my vertex data (which will be manipulated along the time)? I have read in the documentation that Vbos are OK for data that is not changing frequently, and this is not the case.

My understanding of OpenGL buffers is still limited, I hope you can point me in the right direction, specially because I want my application to be as stable as possible (well, who does not want that)

thanks!

Here I leave you an example of what I am trying to do (the complete example is attached as .zip file).

I use a VBO to store all the vertex and color data. The variable “behavior” determines the way particles are going to move in the screen. In this case, 0 represents boid behavior.

The performance is but 25fps: extremely poor for just 500 particles!! I am on a MBP intel core Duo, 8GB, OS X 10.6.8, NVIDIA GeForce GT 330M

Question 1:
Are Vbos the way to go in this case? How can I boost performance?

Question 2:
I initialize particles at random positions of the screen (ofRandom(screenWidth), ofRandom(screenHeight)). However they appear in the screen corners. Why is this?

//////CODE////////////////////////////////////////////////////////

Object.h

  
  
#ifndef __H__OBJECT  
#define __H__OBJECT  
  
#include "ofMain.h"  
  
  
  
class Object {  
  
  
public:	  
	  
	  
	void init(const int& _nParticles);  
	void setWSize(ofVec2f w);  
	void update();  
	void draw();  
	void setBehaviour(int n);  
	int getBehaviour() const;  
	int getNParticles() const;  
	  
	  
	  
private:  
  
	void borders(const int& index);  
	  
	//BOID BEHAVIOUR FUNCTIONS  
	void	flocking (const int& index);  
	ofVec3f separate (const int& thisBoid);  
	ofVec3f align    (const int& thisBoid);  
	ofVec3f cohesion (const int& thisBoid);  
	void	addBoid();  
	  
	  
	  
	int nParticles;  
	int behaviour;	  
  
	ofVec2f WSize;  
	ofVec3f* pnPos;  
	ofVec3f* pnVel;  
	ofVec3f* pnAccel;  
	ofColor* pnColor;  
	float*   pnTime;  
	  
	ofVbo vbo;  
	  
	static float maxSpeed, maxForce, r;  
  
	  
};  
  
#endif;  
  

Object.cpp

  
  
#include "Object.h"  
  
//static variables  
float Object::maxSpeed = 3.0f;  
float Object::maxForce = 0.1f;  
float Object::r = 4.0f;  
  
  
void Object::setBehaviour(int b) {  
	  
	behaviour = b;  
	//does this imply any change on vertex arrays?  
}  
  
  
  
int Object::getBehaviour() const {  
	return behaviour;  
	  
}  
  
  
void Object::setWSize(ofVec2f w) {  
	WSize = w;  
}  
  
  
int Object::getNParticles() const {  
	  
	return(nParticles);  
}  
  
  
  
void Object::init(const int& _nParticles) {  
  
	behaviour = 0;  
	  
	//prevention in case the object is already initalized...  
	// (avoid double allocation)  
	  
	if (nParticles > 0)  
	{  
		ofLog(OF_LOG_WARNING, "Object: Already initialized");  
		  
		delete[] pnPos;  
		delete[] pnVel;  
		delete[] pnColor;  
		delete[] pnAccel;  
		delete[] pnTime;  
		  
		nParticles = 0;  
	}  
	  
	  
	//initialization of arrays  
	  
	nParticles = _nParticles;  
	  
	pnPos	=	new ofVec3f [nParticles];  
	pnVel	=	new ofVec3f [nParticles];  
	pnAccel =	new ofVec3f [nParticles];  
	pnColor =	new ofColor [nParticles];	  
	pnTime  =	new float	[nParticles];  
	  
	  
	ofSeedRandom();  
	  
	  
	//initialize array data  
	for(int i=0; i<nParticles; i++) {  
		  
		pnPos[i].set(ofRandom(WSize.x), ofRandom(WSize.y), 0.0f);  
		pnVel[i].set(ofRandom(-1.0, 1.0f), ofRandom(-1.0f, 1.0f), ofRandom(-1.0f, 1.0f));  
		pnAccel[i] = 0;  
		pnColor[i].set(255, 255, 255, 255);  
		pnTime[i] = ofRandom(1.0f);  
	}  
	  
	vbo.setColorData(pnColor, nParticles, GL_STATIC_DRAW);  
	vbo.setVertexData(pnPos, nParticles, GL_DYNAMIC_DRAW);  
	  
}  
   
  
  
void Object::borders(const int& index) {  
  
		if (pnPos[index].x < -r) pnPos[index].x = WSize.x+r;  
		if (pnPos[index].y < -r) pnPos[index].y = WSize.y+r;  
		if (pnPos[index].x > WSize.x+r) pnPos[index].x = -r;  
		if (pnPos[index].y > WSize.y+r) pnPos[index].y = -r;  
}  
  
  
  
  
void Object::update() {   
	  
	switch(behaviour) {  
	  
		case 0:  
			  
			//UPDATE BOID  
			  
			for (int i=0; i<nParticles; i++) {   
				  
				flocking(i); //calculate acceleration for this boid in relation to other boids  
				  
				pnVel[i] += pnAccel[i];  
				pnVel[i].limit(maxSpeed);  
				  
				pnPos[i] += pnVel[i];  
				  
				borders(i);  
				  
				pnAccel[i] *= 0.0f;  
  
			}  
		  
		  
		  
		break;  
			  
			  
		/*	  
		case 1:  
		  
		borders();  
		break;  
			  
			  
			  
		case 2:  
		borders();	  
		break;  
		*/	  
			  
			  
		default:  
		break;  
	}  
	  
}  
  
  
  
void Object::draw () {  
	  
	ofSetLineWidth(3.0f);  
	  
	//update the vbo  
	vbo.setColorData(pnColor, nParticles, GL_STATIC_DRAW);  
	vbo.setVertexData(pnPos, nParticles, GL_DYNAMIC_DRAW);  
	// and draw  
	vbo.draw(GL_POINTS, 0, nParticles); //arguments: drawMode, first, total  
	  
	  
}  
  
  
/////////////////////////////////////////////////////////////////////////////////////  
// FUNCTIONS FOR BOID BEHAVIOR                    ///////////////////////////////////  
/////////////////////////////////////////////////////////////////////////////////////  
  
  
void Object::flocking(const int& index) {  
	  
	ofVec3f sep = separate(index);  
	ofVec3f ali = align(index);  
	ofVec3f coh = cohesion(index);  
	  
	//Arbitrarily weight these forces  
	sep *= 1.2f;  
	ali *= 1.5f;  
	coh *= 1.0f;  
	  
	//Add the force vectors to acceleration  
	pnAccel[index] += sep;  
	pnAccel[index] += ali;  
	pnAccel[index] += coh;	  
		  
}  
  
  
  
  
ofVec3f Object::separate (const int& thisBoid) {  
	  
	float desiredseparation = 25.0f;  
	ofVec3f steer(0.0f, 0.0f, 0.0f);  
	int count = 0;  
	  
	//For every boid in the system, check if it is too close  
	for (int i = 0; i < nParticles; i++) {  
  
		ofVec3f other = pnPos[i];  
		  
		float d = ofVec3f(pnPos[thisBoid] - other).length();  
		  
		if ((d >0) && (d < desiredseparation)) {  
		  
			ofVec2f diff = pnPos[thisBoid] - other;  
			diff.normalize();  
			diff /= d; //weight by distance. The closer, the stronger the steer vector  
			  
			steer += diff;  
			count++;  
		}  
		  
	}  
	  
	//average distance of this boid to all other boids  
	if(count > 0)  steer /= (float)count;  
	  
	if (steer.length() > 0) {  
		steer.normalize();  
		steer *= maxSpeed;  
		steer -= pnVel[thisBoid];  
		steer.limit(maxForce);  
	}  
	  
	return steer;  
	  
}  
  
  
  
  
  
ofVec3f Object::align (const int& thisBoid) {  
	  
	float neighbordist = 50.0f;  
	  
	ofVec3f steer(0.0f, 0.0f, 0.0f);  
	  
	int count = 0;  
	  
	  
	for (int i = 0; i < nParticles; i++) {  
		  
		float d = ofVec3f(pnPos[thisBoid] - pnPos[i]).length();  
		  
		if ((d >0) && (d < neighbordist)) {  
			  
			steer += pnVel[i];  
			count++;  
		}  
	}  
	  
	  
	if (count > 0) {    
		steer /= (float)count;  
	}  
	  
	  
	if (steer.length() > 0) {  
		  
		steer.normalize();  
		steer *= maxSpeed;  
		steer -= pnVel[thisBoid];  
		steer.limit(maxForce);  
	}  
	  
	return steer;  
}  
  
  
  
ofVec3f Object::cohesion (const int& thisBoid) {  
  
	float neighbordist = 50.0f;  
	  
	ofVec3f sum (0.0f, 0.0f, 0.0f);  
	  
	int count = 0;  
	  
	for (int i = 0; i < nParticles; i++) {  
	  
		float d = ofVec3f(pnPos[thisBoid] - pnPos[i]).length();  
		  
		if ((d > 0.0f) && (d < neighbordist)) {  
		  
			sum += pnPos[i];  
			count++;  
			  
		}  
  
		  
	}  
	  
	if (count > 0) {    
		sum /= (float)count;  
		  
		//steer vector towards the location  
		  
		ofVec3f steer; //the steering vector  
		ofVec3f desired = sum - pnPos[thisBoid];  
		float d = desired.length(); //magnitude  
		  
		if (d > 0.0f) {  
			  
			desired.normalize();  
			desired *= maxSpeed;  
			  
			//steering = desired - velocity  
			steer = desired-pnVel[thisBoid];  
			steer.limit(maxForce);  
		}  
		else steer.set(0.0f, 0.0f);  
		  
		return steer;   
	}  
  
	return sum;  
  
}  
  
  
  
  
  
void Object::addBoid() {  
	  
	//still to determine....  
}  
  

VBO_example_particles.zip

vbo’s are usually the way to do something like this, i would recomend to use ofVboMesh. With some old cards i’ve seen sometimes how vbo’s are slower than plain ofMesh, but usually it’s the opposite. if you plan to update positions frequently (ie: every frame) use mesh.setUsage(GL_STREAM_DRAW) or vbo.setUsage(GL_STREAM_DRAW)

Much appreciated Arturo!!