Implementing Animation with Sprite Sheets

Hello. As the title suggests, I am trying to implement animation using a sprite sheet. I’ve read online that the way to do this is to change the texture coordinates of the sprite so that openGL will only show the current frame in the sprite sheet.
So I’ve figured out what the texture coordinates should be in each frame of animation, but I’m a bit confused as to how I change the texture coordinates of something once it’s already made.

I construct a sprite object, which i use for showing images on the screen here:


Sprite::Sprite(Shader intendedShader)
{
	//Verticies making up the square and texture coordinates 
	GLfloat vertices[] = {
		//X    Y     Z     TexX  TexY
		0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
		-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
		-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
		0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
		0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
		-0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
	};

	//Generates their IDs and saves them in vars
	glGenVertexArrays(1, &vertexArray);
	glGenBuffers(1, &vertexBuffer);

	//Binds the VAO and VBO so future GL fx calls will affet them
	glBindVertexArray(vertexArray);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

	//Copies the Vertex data into the buffer
	glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

	//Tells openGL how to interpret this array of random numbers
	//glVertexAttribPointer
	//Param 1: Location. Which attribute in the vertex shader we're setting up
	//Param 2: Size. How many values the vertex attribute has 
	//Param 3: Type. What type the vertex attributes are
	//Param 4: Normalization. Whether or not the values should be normalized 
	//Param 5: Stride. Space in bytes between attribute sets 
	//Param 6: Offset. After how many bytes the info begins (cast as glvoid*)

	//Sets up Vertex position information
	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
		(GLvoid*)0);
	//Activates Vertex Position Information
	glEnableVertexAttribArray(0);

	//Sets up Texture coordinate information 
	glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
		(GLvoid*)(3 * sizeof(GLfloat)));
	//Activates texture coordinate information 
	glEnableVertexAttribArray(1);

	//We're done with the buffers now, so unbinds them 
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindVertexArray(0);
}

The array vertices has three points of a vertex, then the two texture coordinates associated with that vertex. I store the vertex array in a member in the sprite class so I can pass it in to draw arrays later. However, I’m not sure how to change this information after the sprite has been constructed.

Do I need to re-bind the vertex array/buffer, make a new array of vertices with the updated texture coordinate information, and then call glBufferData and glVertexAttribPointer all over again, like I did when the sprite was constructed? It seems like a ton of stuff to do. I imagined there must be an easier way to get at this texture coordinate information so that I can update it, but I’m not sure what it is.

No. You can replace some portion of the data in a buffer either using glBufferSubData() or by mapping the buffer with glMapBuffer() (or glMapBufferRange() etc) then updating the mapped region.

You don’t need to call glVertexAttribPointer() again because the attribute is still associated with the same buffer, offset, stride, etc, so a draw call will read from the same locations but will be reading the new data.

If you typically don’t update the texture coordinates and vertex positions at the same time, it may be preferable to store them in separate buffers rather than interleaving the values.

Also, you typically wouldn’t use a separate VAO for each sprite, but store all of your sprites (or at least all sprites of the same type) as a single “batch”, so that you can render them all with a single draw call.

Could you clarify how to make a seperate buffer? I tried to do that but, it caused my program to crash with an unhanded access violation. Here is what I changed the code to:


Sprite::Sprite(Shader intendedShader)
{
//Verticies making up the square 
GLfloat vertices[] = {
//X    Y     Z     
0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f, 
0.5f, -0.5f, 0.0f, 
0.5f, 0.5f, 0.0f, 
-0.5f, 0.5f, 0.0f, 
};
 
GLfloat texCoords[] = {
    1.0f, 0.0f,
    0.0f, 1.0f,
    0.0f, 0.0f,
    1.0f, 0.0f,
    1.0f, 1.0f,
    0.0f, 1.0f,
 };

//Generates their IDs and saves them in vars
glGenVertexArrays(1, &vertexArray);
glGenBuffers(1, &vertexBuffer);
glGenBuffers(1, &textureBuffer);
//Binds the VAO and VBO so future GL fx calls will affet them
glBindVertexArray(vertexArray);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

//Copies the Vertex data into the buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

//Sets up Vertex position information
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat),
		(GLvoid*)0);
//Activates Vertex Position Information
glEnableVertexAttribArray(0);

//Sets up texture buffer 
glBindBuffer(GL_ARRAY_BUFFER, textureBuffer);
glBufferData(GL_TEXTURE_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);

//Sets up Texture coordinate information 
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat),
		(GLvoid*)0);
//Activates texture coordinate information 
glEnableVertexAttribArray(1);

//We're done with the buffers now, so unbinds them 
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}

glBufferData(GL_TEXTURE_BUFFER, sizeof(texCoords), texCoords, GL_STATIC_DRAW);

That’s not what GL_TEXTURE_BUFFER is for. That’s for talking about buffer textures. That has nothing to do with buffer objects that happen to store texture coordinates. Your buffer is still a vertex array buffer (which is why you bound it to GL_ARRAY_BUFFER), so that’s what the first parameter should be.