I am trying to use the same vertex shader for two different use cases to preserve memory, but I am having issues where the modifications of one shader run are passed on to the next run, even after being reset. Specifically, I am trying to use the shader to both show a regular texture, while also modifying the vertex attributes in some cases to show portions of a texture. The problem is, this "cropping" of the texture is applied to the texture that should be shown 100%.
Here is what it looks like, where the sprite on the right is drawn first:
Both textures are showing only a portion of their actual images, but only the one on the left should be doing so. Strangely, the one on the right shows a full texture for a second, before being replaced with what is shown. This tells me that the vertex attributes of the first sprite are modified by the second (left) sprite, but I don't know how to reset them. Here is my vertex shader:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
uniform mat4 transform;
uniform mat4 view;
uniform mat4 projection;
void main()
{
gl_Position = projection * view * transform * vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
Here is how I modify the vertex attributes:
void setSelection(Quad selection) {
vertices[3] = selection.x + selection.w;
vertices[4] = selection.y + selection.h;
vertices[8] = selection.x + selection.w;
vertices[9] = selection.y;
vertices[13] = selection.x;
vertices[14] = selection.y;
vertices[18] = selection.x;
vertices[19] = selection.y + selection.h;
};
...
float vertices[20] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
};
This code changes the shown vertex array's texture coordinates. They are applied with this code:
void Sprite::render() {
// Set shader uniforms if the shader is initialized
if (shader != nullptr) {
applyTransforms();
shader->useShader();
shader->setMat4("transform", trans);
shader->setVec4("color", color.asVec4());
shader->setMat4("view", Camera::getLookMat());
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
tex->bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
However, if I have two objects (both instances of the same class), one with a modified array (left) and one with an unmodified array (right, as shown in the picture), the modified array takes precedence, and I can't figure out how to keep it from being changed without using a different shader object, but I don't want to do that. Is there anything I am missing that would allow me to change the attributes in between draw cycles?
ADDITION 1
Here is the entire Sprite
class (right image is an object):
Sprite::Sprite(std::shared_ptr<Texture> texture, std::shared_ptr<Shader> shader) : tex(texture), color(1u, 1u, 1u, 1u), shader(shader) {
glInit();
}
Sprite::Sprite(const std::string& path, std::shared_ptr<Shader> shader) : tex(new Texture(path)), color(1u, 1u, 1u, 1u), shader(shader) {
glInit();
}
Sprite::~Sprite() {
glDeleteVertexArrays(1, &vao);
vao = NULL;
glDeleteBuffers(1, &vbo);
vbo = NULL;
glDeleteBuffers(1, &ebo);
ebo = NULL;
}
void Sprite::setColor(RGBA color) {
this->color = color;
}
void Sprite::render() {
// Set shader uniforms if the shader is initialized
if (shader != nullptr) {
applyTransforms();
shader->useShader();
shader->setMat4("transform", trans);
shader->setVec4("color", color.asVec4());
shader->setMat4("view", Camera::getLookMat());
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
glBindBuffer(GL_ARRAY_BUFFER, 0);
tex->bind();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
void Sprite::glInit() {
// GL initialization
// Buffers
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies, GL_STATIC_DRAW);
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Verticies
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * FLOAT_SIZE, (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * FLOAT_SIZE, (void*)(3 * FLOAT_SIZE));
glEnableVertexAttribArray(1);
}
Here's the header:
class Sprite : public Object{
public:
Sprite(std::shared_ptr<Texture> texture, std::shared_ptr<Shader> shader = nullptr);
Sprite(const std::string& path, std::shared_ptr<Shader> shader = nullptr);
~Sprite();
void setColor(RGBA color);
inline RGBA getColor() const { return color; };
virtual void render();
inline std::weak_ptr<Texture> getTexture() const { return tex; };
protected:
// Pointers for texture and shader (if initialized)
std::shared_ptr<Texture> tex;
std::shared_ptr<Shader> shader;
GLuint vbo = 0, vao = 0, ebo = 0;
RGBA color;
float vertices[20] = {
// positions // texture coords
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // bottom right
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // bottom left
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // top left
};
GLuint indicies[6] = {
0, 1, 3,
1, 2, 3
};
// Initialize GL objects
virtual void glInit();
};
And here's the subclass (left image, header only):
class SpriteSheet : public Sprite {
public:
SpriteSheet(std::shared_ptr<Texture> texture, std::shared_ptr<Shader> shader = nullptr) : Sprite(texture, shader) {};
SpriteSheet(const std::string& path, std::shared_ptr<Shader> shader = nullptr) : Sprite(path, shader) {};
~SpriteSheet() { Sprite::~Sprite(); };
void setSelection(Quad selection) {
vertices[3] = selection.x + selection.w;
vertices[4] = selection.y + selection.h;
vertices[8] = selection.x + selection.w;
vertices[9] = selection.y;
vertices[13] = selection.x;
vertices[14] = selection.y;
vertices[18] = selection.x;
vertices[19] = selection.y + selection.h;
};
};
The Object
class only contains positional vectors, and is therefore not relevant. RGBA
and Quad
are abstractions of glm::vec4
.
EDIT 2
I have figured out that if two SpriteSheet
objects are declared, the one declared last will be the one that takes priority with its texture coordinates being applied to all objects, regardless of render order.
setSelection
is a class method andvertices
are a member of the same class? So you are not modifying the same global array via a free function? \$\endgroup\$vertices
is actually a member of theSprite
class (same as therender
method), whereassetSelection
is actually a member of a subclass ofSprite
. All functions shown are class functions. \$\endgroup\$