Instanced Rendering, First Attempt

So I tried rendering multiple particles with a for loop and found that that eats up quite a bit of data/memory. I found out about this pretty legit sounding thing known as instancing! I thought it looked pretty complex and I was right! I tried my best to implement it for silly little particles but all I get is a blank screen. It loads the particles and everything just fine, only problem is during rendering where I get the blank screen. I will post my code below and see what you guys think. This is very beneficial to my current project. I am probably doing something completely wrong here, due to the fact that I don’t really understand it. I will post the code below as well as my vertex shader. Thanks for the help in advance!

void Particles::setup(string texturedata, double maxparticles)
{

	maxpart = maxparticles;

	particle_position_data = new GLfloat[maxpart * 12];
	particle_texcoord_data = new GLfloat[maxpart * 12];

	particle.resize(maxpart);

	for(int i = 0; i<maxpart; i++)
		particle[i].life = -1;

	moving = false;

	alpha = startalpha = 1;

	velx = vely = rotatespeed = rotating = shrinking = decaying = life = sw = sh = 0;

	glGenBuffers(1, &particle_position_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
	glBufferData(GL_ARRAY_BUFFER, maxparticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

	glGenBuffers(1, &particle_texcoord_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
	glBufferData(GL_ARRAY_BUFFER, maxparticles * 4 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

}

void Particles::setmove(  double startx, double starty, double startwidth, double startheight, double rotation, double xvel, double yvel, double lifeseconds, bool decay, bool shrink, bool rotate)
{
	for(int i=0; i<maxpart; i++)
	{
		particle[i].x = startx + (rand()%30);
		particle[i].y = starty + (rand()%30);
		particle[i].size = startwidth;

		particle[i].spritex = 0;
		particle[i].spritey = 0;
		particle[i].xnext = 1;
		particle[i].ynext = 1;


		// first triangle of particle quad
		particle_position_data[12*i+0] = particle[i].x;
		particle_position_data[12*i+1] = particle[i].y - particle[i].size;
		particle_position_data[12*i+2] = particle[i].x;
		particle_position_data[12*i+3] = particle[i].y;
		particle_position_data[12*i+4] = particle[i].x + particle[i].size;
		particle_position_data[12*i+5] = particle[i].y;

		// second triangle
		particle_position_data[12*i+6] = particle[i].x + particle[i].size;
		particle_position_data[12*i+7] = particle[i].y;
		particle_position_data[12*i+8] = particle[i].x + particle[i].size;
		particle_position_data[12*i+9] = particle[i].y - particle[i].size;
		particle_position_data[12*i+10] = particle[i].x;
		particle_position_data[12*i+11] = particle[i].y - particle[i].size;



		//first triangle for texcoords
		particle_texcoord_data[12*i+0] = particle[i].spritex;
		particle_texcoord_data[12*i+1] = particle[i].spritey;
		particle_texcoord_data[12*i+2] = particle[i].spritex;
		particle_texcoord_data[12*i+3] = particle[i].spritey + particle[i].ynext;
		particle_texcoord_data[12*i+4] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+5] = particle[i].spritey + particle[i].ynext;

		//second triangle
		particle_texcoord_data[12*i+6] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+7] = particle[i].spritex + particle[i].ynext;
		particle_texcoord_data[12*i+8] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+9] = particle[i].spritey;
		particle_texcoord_data[12*i+10] = particle[i].spritex;
		particle_texcoord_data[12*i+11] = particle[i].spritey;

		particlecount = i;
	}
}



void Particles::render(unsigned int program, unsigned int Texture, Camera camera, RenderPipeline pipeline, double r, double g, double b)
{
	pipeline.pushMatrix();
	pipeline.ortho(0,640,640,0,-1,1);
		
	pipeline.translate( camera.calcx(), camera.calcy(), 0);
	pipeline.updateMatrices(program);
				
		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glBufferData(GL_ARRAY_BUFFER, maxpart * 12 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat) * 4, particle_position_data);

		glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
		glBufferData(GL_ARRAY_BUFFER, maxpart * 12 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat) * 4, particle_texcoord_data);


		glBindTexture(GL_TEXTURE_2D, Texture);
		glUniform1i(glGetUniformLocation(program,"img"),0);


		//color and boolean variables for particle arrays
		glUniform4f(glGetUniformLocation(program,"color"),r,g,b,1);
		glUniform1i(glGetUniformLocation(program,"array"),1); //this lets the shader know that we have an array of particles and are indexing

		int position=glGetAttribLocation(program, "vertexpos");
		int texcoord=glGetAttribLocation(program, "texcoord");



		//attribute of particle's vertices
		glEnableVertexAttribArray(position);
		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

		
		//attribute of particles' texture coordinates
		glEnableVertexAttribArray(texcoord);
		glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
		glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);


		//glVertexAttribDivisor(index, 0);  every particle has same vertices (this will not be used due to the complexity of putting all this info in the shader
		glVertexAttribDivisor(position, 1); // positions, one per particle
		glVertexAttribDivisor(texcoord, 1); // same with the texture coordinates

		// draw all the silly little particles
		glDrawArraysInstancedEXT(GL_TRIANGLE_STRIP, 0, 4, particlecount);

		glDisableVertexAttribArray(position);
		glDisableVertexAttribArray(texcoord);
		glBindBuffer(GL_ARRAY_BUFFER,0);
			
	pipeline.popMatrix();
}







//particle class
struct PartContainer
{
	double x;
	double y;
	double size;
	double life;

	double spritex, spritey;
	double xnext, ynext;
};

class Particles
{
public:
	
	vector <PartContainer> particle;

	void setup(string texturedata, double maxparticles);
	void setmove( double startx, double starty, double startwidth, double startheight, double rotation = 10, double xvel=0, double yvel=0, double lifeseconds=10, bool decay = true, bool shrink = true, bool rotate = true);
	void move();
	void render(unsigned int program, unsigned int Texture, Camera camera, RenderPipeline pipeline, double r = 1, double g = 1, double b = 1);

	bool moving, decaying, shrinking, rotating;
	double alpha, startalpha, velx, vely, life, sw, sh, rotatespeed;

	int maxpart, particlecount;
	bool vertset;

	double size;

	GLfloat* particle_position_data;
	GLfloat* particle_texcoord_data;

	GLuint particle_position_buffer;
	GLuint particle_texcoord_buffer;
};

And here is the Vertex Shader

attribute vec2 vertexpos;
attribute vec2 texcoord;

varying vec2 texcoordvarying;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 normalMatrix;
 
void main()
{
        gl_Position=modelViewProjectionMatrix*vec4(vertexpos,0.0,1.0);
		texcoordvarying=texcoord;
}

All of your data is per-instance, so all of the triangle’s vertices are identical.

Instanced rendering requires a combination of per-vertex and per-instance data.

E.g. for particles, you’d provide position and texture coordinate data per vertex, plus a position per instance. The vertex shader would then offset each vertex’ position by the instance position.

So, your Particles::setmove() method should look something like:


void Particles::setmove(  double startx, double starty, double startwidth, double startheight, double rotation, double xvel, double yvel, double lifeseconds, bool decay, bool shrink, bool rotate)
{
    for(int i=0; i<maxpart; i++)
    {
	particle[i].x = startx + (rand()%30);
	particle[i].y = starty + (rand()%30);
	particle[i].size = startwidth;
 
	particle[i].spritex = 0;
	particle[i].spritey = 0;
	particle[i].xnext = 1;
	particle[i].ynext = 1;
 
	particle_position[2*i+0] = particle[i].x;
	particle_position[2*i+1] = particle[i].y;

	particle_size[i] = particle[i].size;

	particle_texcoord[2*i+0] = particle[i].spritex;
	particle_texcoord[2*i+1] = particle[i].spritey;

	particle_texsize[2*i+0] = particle[i].xnext;
	particle_texsize[2*i+1] = particle[i].ynext;
 
	particlecount = i;
    }

    // first triangle of particle quad
    particle_position_data[0] = 0;
    particle_position_data[1] = -1;
    particle_position_data[2] = 0;
    particle_position_data[3] = 0;
    particle_position_data[4] = 1;
    particle_position_data[5] = 0;

    // second triangle
    particle_position_data[6] = 1;
    particle_position_data[7] = 0;
    particle_position_data[8] = 1;
    particle_position_data[9] = -1;
    particle_position_data[10] = 0;
    particle_position_data[11] = -1;


    //first triangle for texcoords
    particle_texcoord_data[0] = 0;
    particle_texcoord_data[1] = 0;
    particle_texcoord_data[2] = 0;
    particle_texcoord_data[3] = 1;
    particle_texcoord_data[4] = 1;
    particle_texcoord_data[5] = 1;

    //second triangle
    particle_texcoord_data[6] = 1;
    particle_texcoord_data[7] = 1;
    particle_texcoord_data[8] = 1;
    particle_texcoord_data[9] = 0;
    particle_texcoord_data[10]= 0;
    particle_texcoord_data[11]= 0;
}

particle_position_data and particle_texcoord_data would be per-vertex (attribute divisor = 0) while the others would be per-instance (attribute divisor = 1).

The “position = base + offset*size” calculations which were being done there would be moved to the vertex shader.

Apart from saving memory, this may also reduce the amount of data you need to upload each frame. E.g. if you move a particle but don’t change its size, you only need to upload a new offset, rather than 4 new vertices (you’re actually defining 6 vertices per triangle pair, which would be correct for GL_TRIANGLES but GL_TRIANGLE_STRIP only needs 4).

[QUOTE=GClements;1262543]All of your data is per-instance, so all of the triangle’s vertices are identical.

Instanced rendering requires a combination of per-vertex and per-instance data.

E.g. for particles, you’d provide position and texture coordinate data per vertex, plus a position per instance. The vertex shader would then offset each vertex’ position by the instance position.

So, your Particles::setmove() method should look something like:


void Particles::setmove(  double startx, double starty, double startwidth, double startheight, double rotation, double xvel, double yvel, double lifeseconds, bool decay, bool shrink, bool rotate)
{
    for(int i=0; i<maxpart; i++)
    {
	particle[i].x = startx + (rand()%30);
	particle[i].y = starty + (rand()%30);
	particle[i].size = startwidth;
 
	particle[i].spritex = 0;
	particle[i].spritey = 0;
	particle[i].xnext = 1;
	particle[i].ynext = 1;
 
	particle_position[2*i+0] = particle[i].x;
	particle_position[2*i+1] = particle[i].y;

	particle_size[i] = particle[i].size;

	particle_texcoord[2*i+0] = particle[i].spritex;
	particle_texcoord[2*i+1] = particle[i].spritey;

	particle_texsize[2*i+0] = particle[i].xnext;
	particle_texsize[2*i+1] = particle[i].ynext;
 
	particlecount = i;
    }

    // first triangle of particle quad
    particle_position_data[0] = 0;
    particle_position_data[1] = -1;
    particle_position_data[2] = 0;
    particle_position_data[3] = 0;
    particle_position_data[4] = 1;
    particle_position_data[5] = 0;

    // second triangle
    particle_position_data[6] = 1;
    particle_position_data[7] = 0;
    particle_position_data[8] = 1;
    particle_position_data[9] = -1;
    particle_position_data[10] = 0;
    particle_position_data[11] = -1;


    //first triangle for texcoords
    particle_texcoord_data[0] = 0;
    particle_texcoord_data[1] = 0;
    particle_texcoord_data[2] = 0;
    particle_texcoord_data[3] = 1;
    particle_texcoord_data[4] = 1;
    particle_texcoord_data[5] = 1;

    //second triangle
    particle_texcoord_data[6] = 1;
    particle_texcoord_data[7] = 1;
    particle_texcoord_data[8] = 1;
    particle_texcoord_data[9] = 0;
    particle_texcoord_data[10]= 0;
    particle_texcoord_data[11]= 0;
}

particle_position_data and particle_texcoord_data would be per-vertex (attribute divisor = 0) while the others would be per-instance (attribute divisor = 1).

The “position = base + offset*size” calculations which were being done there would be moved to the vertex shader.

Apart from saving memory, this may also reduce the amount of data you need to upload each frame. E.g. if you move a particle but don’t change its size, you only need to upload a new offset, rather than 4 new vertices (you’re actually defining 6 vertices per triangle pair, which would be correct for GL_TRIANGLES but GL_TRIANGLE_STRIP only needs 4).[/QUOTE]

Thanks, but can you show me what the vertex shader should look like? I’m still not completely comfortable with messing with the vertex shader, fragment shaders are fine and dandy to mess with though. I will try and see what i can do.

Something like:


// per instance
attribute vec2 position;
attribute float size;
attribute vec2 texcoord;
attribute vec2 texsize;

// per vertex
attribute vec2 position_data;
attribute vec2 texcoord_data;
 
varying vec2 texcoordvarying;
 
uniform mat4 modelViewProjectionMatrix;

void main()
{
    vec2 vpos = position + size * position_data;
    vec2 vtex  = texcoord + texsize * texcoord_data;
    texcoordvarying=vtex;
    gl_Position=modelViewProjectionMatrix*vec4(vpos,0.0,1.0);
}

Ok I tried out what was said, and it still doesn’t work. I’m not exactly sure what I’m doing wrong though.

Here is the updated code:

void Particles::setup(string texturedata, double maxparticles)
{

	maxpart = maxparticles;

	size.resize(maxpart);

	particle_position_data = new GLfloat[maxpart * 2];
	particle_texcoord_data = new GLfloat[maxpart * 2];
	particle_width_data = new GLfloat[maxpart];
	particle_texcoord_size = new GLfloat[maxpart];

	particle_pos_base[0] = 0;
	particle_pos_base[1] = -1;
	particle_pos_base[2] = 0;
	particle_pos_base[3] = 0;
	particle_pos_base[4] = 1;
	particle_pos_base[5] = 0;
	particle_pos_base[6] = 1;
	particle_pos_base[7] = 0;
	particle_pos_base[8] = 1;
	particle_pos_base[9] = -1;
	particle_pos_base[10] = 0;
	particle_pos_base[11] = -1;


	
	particle_tex_base[0] = 0;
	particle_tex_base[1] = 0;
	particle_tex_base[2] = 0;
	particle_tex_base[3] = 1;
	particle_tex_base[4] = 1;
	particle_tex_base[5] = 1;
	particle_tex_base[6] = 1;
	particle_tex_base[7] = 1;
	particle_tex_base[8] = 1;
	particle_tex_base[9] = 0;
	particle_tex_base[10] = 0;
	particle_tex_base[11] = 0;

	particle.resize(maxpart);
	

	for(int i = 0; i<maxpart; i++)
		particle[i].life = -1;

	moving = false;

	alpha = startalpha = 1;

	velx = vely = rotatespeed = rotating = shrinking = decaying = life = sw = sh = 0;

	glGenBuffers(1, &part_pos_base);
	glBindBuffer(GL_ARRAY_BUFFER, part_pos_base);
	glBufferData(GL_ARRAY_BUFFER, sizeof(particle_pos_base), particle_pos_base, GL_STATIC_DRAW);
	
	glGenBuffers(1, &part_tex_base);
	glBindBuffer(GL_ARRAY_BUFFER, part_tex_base);
	glBufferData(GL_ARRAY_BUFFER, sizeof(particle_tex_base), particle_tex_base, GL_STATIC_DRAW);




	glGenBuffers(1, &particle_width_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_width_buffer);
	glBufferData(GL_ARRAY_BUFFER, maxparticles * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

	glGenBuffers(1, &particle_position_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
	glBufferData(GL_ARRAY_BUFFER, maxparticles * 2 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

	glGenBuffers(1, &particle_texcoord_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
	glBufferData(GL_ARRAY_BUFFER, maxparticles * 2 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);

}

void Particles::setmove(  double startx, double starty, double startwidth, double startheight, double rotation, double xvel, double yvel, double lifeseconds, bool decay, bool shrink, bool rotate)
{
	for(int i=0; i<maxpart; i++)
	{
		particle[i].x = startx + (rand()%30);
		particle[i].y = starty + (rand()%30);
		particle[i].size = startwidth;

		particle[i].spritex = 0;
		particle[i].spritey = 0;
		particle[i].xnext = 1;
		particle[i].ynext = 1;


		// first triangle of particle quad
		particle_position_data[2*i+0] = particle[i].x;
		particle_position_data[2*i+1] = particle[i].y;
		particle_width_data[i] = particle[i].size;
		
		//first triangle for texcoords
		particle_texcoord_data[2*i+0] = particle[i].spritex;
		particle_texcoord_data[2*i+1] = particle[i].spritey;
		

		particlecount = i;
	}
}


void Particles::render(unsigned int program, unsigned int Texture, Camera camera, RenderPipeline pipeline, double r, double g, double b)
{
	pipeline.pushMatrix();
	pipeline.ortho(0,640,640,0,-1,1);
		
	pipeline.translate( camera.calcx(), camera.calcy(), 0);
	pipeline.updateMatrices(program);
				
		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glBufferData(GL_ARRAY_BUFFER, maxpart * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat), particle_width_data);

		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glBufferData(GL_ARRAY_BUFFER, maxpart * 2 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat) * 2, particle_position_data);

		glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
		glBufferData(GL_ARRAY_BUFFER, maxpart * 2 * sizeof(GLfloat), NULL, GL_STREAM_DRAW);
		glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat) * 2, particle_texcoord_data);


		glBindTexture(GL_TEXTURE_2D, Texture);
		glUniform1i(glGetUniformLocation(program,"img"),0);
		glUniform1i(glGetUniformLocation(program,"array"),1);


		//color variables for particle arrays
		glUniform4f(glGetUniformLocation(program,"color"),r,g,b,1);


		int position=glGetAttribLocation(program, "vertexpos");
		int texcoord=glGetAttribLocation(program, "texcoord");


		int pos_base = glGetAttribLocation(program, "vertbase");
		int tex_base = glGetAttribLocation(program, "texbase");
		int part_width = glGetAttribLocation(program, "partwidth");


		//attribute of particle's base
		glEnableVertexAttribArray(pos_base);
		glBindBuffer(GL_ARRAY_BUFFER, part_pos_base);
		glVertexAttribPointer(pos_base, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

		//attribute of particle's texcoord base
		glEnableVertexAttribArray(tex_base);
		glBindBuffer(GL_ARRAY_BUFFER, part_tex_base);
		glVertexAttribPointer(tex_base, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);




		//attribute of particle's vertices
		glEnableVertexAttribArray(position);
		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

		
		//attribute of particles' texture coordinates
		glEnableVertexAttribArray(texcoord);
		glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
		glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

		//attribute of particles' size
		glEnableVertexAttribArray(part_width);
		glBindBuffer(GL_ARRAY_BUFFER, particle_width_buffer);
		glVertexAttribPointer(part_width, 1, GL_FLOAT, GL_FALSE, 0, (void*)0);


		glVertexAttribDivisor(pos_base, 0); //every particle has same vertices (this will not be used due to the complexity of putting all this info in the shader
		glVertexAttribDivisor(tex_base,0);

		glVertexAttribDivisor(part_width,1);
		glVertexAttribDivisor(position, 1); // positions, one per particle
		glVertexAttribDivisor(texcoord, 1); // same with the texture coordinates

		// draw all the silly little particles
		glDrawArraysInstancedEXT(GL_TRIANGLE_STRIP, 0, 12, particlecount);

		glDisableVertexAttribArray(position);
		glDisableVertexAttribArray(texcoord);

		glDisableVertexAttribArray(pos_base);
		glDisableVertexAttribArray(tex_base);
		glDisableVertexAttribArray(part_width);

		glBindBuffer(GL_ARRAY_BUFFER,0);

		
		glUniform1i(glGetUniformLocation(program,"array"),0);
			
	pipeline.popMatrix();
}

And here is my updated vertex shader:



attribute vec2 vertexpos;
attribute vec2 texcoord;

attribute vec2 vertbase;
attribute vec2 texbase;
attribute float partwidth;

varying vec2 texcoordvarying;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
uniform mat4 modelViewProjectionMatrix;
uniform mat4 normalMatrix;

uniform bool array;
 
void main()
{

	if(!array)
	{
        gl_Position=modelViewProjectionMatrix*vec4(vertexpos,0.0,1.0);
		texcoordvarying=texcoord;
	}

	else
	{
		vec2 vpos = vertexpos + partwidth * vertbase;
		vec2 vtex = texcoord + 1.0 * texbase;
		texcoordvarying = vtex;
		gl_Position = modelViewProjectionMatrix*vec4(vpos, 0.0, 1.0);
	}


}

Thanks for all the help so far, together we can get through this!

A few things stand out:

  • [li] The particle_pos_base and particle_tex_base arrays contain 2 distinct triangles, but you’re rendering with GL_TRIANGLE_STRIP rather than GL_TRIANGLES.
    [/li][li] You’re passing 12 for the count argument when there are only 6 vertices (the count is the number of vertices, not individual floats).
    [/li][li] There are a few copy-paste errors, e.g. this
    [/li][QUOTE=Jacob16682;1262677]
    glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer); glBufferData(GL_ARRAY_BUFFER, maxpart * sizeof(GLfloat), NULL, GL_STREAM_DRAW); glBufferSubData(GL_ARRAY_BUFFER, 0, particlecount * sizeof(GLfloat), particle_width_data);
    [/QUOTE]
    binds particle_position_buffer but copies particle_width_data into it.

Also, while not an error, there’s no need to re-create the buffer with glBufferData() each frame. That only needs to be called once (per array) during initialisation, then the data can be overwritten each frame with glBufferSubData().

In short, there’s nothing fundamentally wrong with the code, it just needs debugging.

Ok, so I am going to attempt a different approach here that has gotten me some progress. Instead of instancing the particles I just decided to make a large vertex array that will render all the particles. It fixed the nothing rendering at all problem but now the particles won’t render. Everything looks fine I just don’t see what is wrong. I’ve tried using debugger to check for anything wrong but it doesn’t look like there is. Can you guys help?

Here is the updated code:

void Particles::setup(string texturedata, double maxparticles)
{
 
	maxpart = maxparticles;
 
	particle_position_data = new GLfloat[maxpart * 12];
	particle_texcoord_data = new GLfloat[maxpart * 12];
 
	particle.resize(maxpart);
 
	for(int i = 0; i<maxpart; i++)
		particle[i].life = -1;
 
	moving = false;
 
	alpha = startalpha = 1;
 
	velx = vely = rotatespeed = rotating = shrinking = decaying = life = sw = sh = 0;
 
	glGenBuffers(1, &particle_position_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(particle_position_data), particle_position_data, GL_STREAM_DRAW);
 
	glGenBuffers(1, &particle_texcoord_buffer);
	glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
	glBufferData(GL_ARRAY_BUFFER, sizeof(particle_texcoord_data), particle_texcoord_data, GL_STREAM_DRAW);
 
}

void Particles::setmove(  double startx, double starty, double startwidth, double startheight, double rotation, double xvel, double yvel, double lifeseconds, bool decay, bool shrink, bool rotate)
{
	for(int i=0; i<maxpart; i++)
	{
		particle[i].x = startx;
		particle[i].y = starty;
		particle[i].size = startwidth;
 
		particle[i].spritex = 0;
		particle[i].spritey = 0;
		particle[i].xnext = 1;
		particle[i].ynext = 1;
 
 
		// first triangle of particle quad
		particle_position_data[12*i+0] = particle[i].x;
		particle_position_data[12*i+1] = particle[i].y - particle[i].size;
		particle_position_data[12*i+2] = particle[i].x;
		particle_position_data[12*i+3] = particle[i].y;
		particle_position_data[12*i+4] = particle[i].x + particle[i].size;
		particle_position_data[12*i+5] = particle[i].y;
 
		// second triangle
		particle_position_data[12*i+6] = particle[i].x + particle[i].size;
		particle_position_data[12*i+7] = particle[i].y;
		particle_position_data[12*i+8] = particle[i].x + particle[i].size;
		particle_position_data[12*i+9] = particle[i].y - particle[i].size;
		particle_position_data[12*i+10] = particle[i].x;
		particle_position_data[12*i+11] = particle[i].y - particle[i].size;
 
 
 
		//first triangle for texcoords
		particle_texcoord_data[12*i+0] = particle[i].spritex;
		particle_texcoord_data[12*i+1] = particle[i].spritey;
		particle_texcoord_data[12*i+2] = particle[i].spritex;
		particle_texcoord_data[12*i+3] = particle[i].spritey + particle[i].ynext;
		particle_texcoord_data[12*i+4] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+5] = particle[i].spritey + particle[i].ynext;
 
		//second triangle
		particle_texcoord_data[12*i+6] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+7] = particle[i].spritex + particle[i].ynext;
		particle_texcoord_data[12*i+8] = particle[i].spritex + particle[i].xnext;
		particle_texcoord_data[12*i+9] = particle[i].spritey;
		particle_texcoord_data[12*i+10] = particle[i].spritex;
		particle_texcoord_data[12*i+11] = particle[i].spritey;
 
		particlecount = i;
	}
}

void Particles::render(unsigned int program, unsigned int Texture, Camera camera, RenderPipeline pipeline, double r, double g, double b)
{
	pipeline.pushMatrix();
	pipeline.ortho(0,640,640,0,-1,1);
 
	pipeline.translate( camera.calcx(), camera.calcy(), 0);
	pipeline.updateMatrices(program);
 
		//  correct size of data to upload. Size of *bytes* of all the data.
		int position_data_size = 
			( sizeof(GLfloat) * 2 ) * 	// Bytes per vertex.
			( maxpart * 6 );		// Number of vertices.
		int texcoord_data_size = 
			( sizeof(GLfloat) * 2 ) * 	// Bytes per vertex.
			( maxpart * 6 );		// Number of vertices.

		// enable vertex attrib for position, bind it, upload it, and set the vertex attrib ptr.
		int position=glGetAttribLocation(program, "vertexpos");
		glEnableVertexAttribArray(position);
		glBindBuffer(GL_ARRAY_BUFFER, particle_position_buffer);
		glBufferSubData(GL_ARRAY_BUFFER, 0, position_data_size, particle_position_data);
		glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
 
		// enable vertex attrib for texcoord, bind it, upload it, and set the vertex attrib ptr.
		int texcoord=glGetAttribLocation(program, "texcoord");
		glEnableVertexAttribArray(texcoord);
		glBindBuffer(GL_ARRAY_BUFFER, particle_texcoord_buffer);
		glBufferSubData(GL_ARRAY_BUFFER, 0, texcoord_data_size, particle_texcoord_data);
		glVertexAttribPointer(texcoord, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
 
 		// texture + uniforms
		glBindTexture(GL_TEXTURE_2D, Texture);
		glUniform1i(glGetUniformLocation(program,"img"),0);
 		glUniform4f(glGetUniformLocation(program,"color"),r,g,b,1);
 

 
		// draw all the silly little particles
		glDrawArrays(GL_TRIANGLES, 0, maxpart * 6);
 
		glDisableVertexAttribArray(position);
		glDisableVertexAttribArray(texcoord);
		glBindBuffer(GL_ARRAY_BUFFER,0);
 
	pipeline.popMatrix();
}

Also going to post a link to the program itself for anyone to debug if they want. I have been told debugger isn’t the best to use so maybe someone else has a better substitute. https://www.dropbox.com/s/7cinrp3o2ldg002/New%20Compressed%20(zipped)%20Folder.zip?dl=0

I really need this working by next week. There is probably some small dumb mistake I am not seeing but I don’t know what it could be. If anyone wants I can just do an entire code dump tomorrow and see if that helps.

Thanks for all the help so far though!

Like I said yesterday, here is my awesome code dump:

https://www.dropbox.com/s/3mqe0aii759uv81/Code%20Dump.zip?dl=0

Now the particle class was part of a library I made while the application folder is the game I am trying to implement the particles in. I am not sure if this will help all that much but everything is there otherwise. I really hope this helps because I can’t find the problem.

particle_position_data is a pointer. sizeof(particle_position_data) will give you the size of a pointer, which isn’t what you want. Use “maxpart * 12 * sizeof(GLfloat)”, or use std::vector instead of an array.

It works now! Thanks man! I know I must’ve been pretty annoying but thanks a ton for staying with this!