I had posted about this issue in a previous question, however, I thought instancing was causing it, whereas even using normal uniforms or a SSBO (which's what I'm using right now) causes it, which means the problem is elsewhere.
I've been trying to figure out the cause, and I might've found out something: only the first quad's vertex data seems to be uploaded / used, somehow.
For example, if I were to place random position and texcoord vectors to my other quads, they would still render somewhat correctly. I said somewhat because the output is the following:
The middle one is way too stretched and the last one's UV coords are wrong, since they aren't filling the whole quad, as you can see by the white background. So long as the first quad's were right, and whenever I changed the first quad's vertex data, it was propagated to the other ones as well, leading me to believe the other quads' vertex data are getting ignored and the first quad is being re-used.
My code:
// VAO definition
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
/*** VBO ***/
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
/*** Vertex Format ***/
//Position Vertex
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
//Normal Vertex
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(glm::vec3)));
//Texture Coordinate Vertex
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(glm::vec3) * 2));
/*** EBO ***/
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
/*** SSBO ***/
glGenBuffers(1, &SSBO);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
// Quad adding function
auto iter = std::find(quads.begin(), quads.end(), quad);
if(iter == quads.end())
{
quads.push_back(quad);
for(unsigned int i = 0; i < 4; i++)
vertices.push_back(quad->vertices[i]);
GLuint start = (indices.empty()) ? 0 : indices[indices.size() - 1] + 1;
GLuint newIndices[] = {
start, start + 1, start + 2,
start, start + 2, start + 3
};
for(unsigned int i = 0; i < 6; i++)
indices.push_back(newIndices[i]);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices[0], GL_DYNAMIC_DRAW);
instanceData.push_back(quad->data);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(RenderingData) * quads.size(), &instanceData[0], GL_DYNAMIC_DRAW);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, SSBO, 0, sizeof(RenderingData) * quads.size());
}
// Rendering
program->Use();
program->SetStorageBlock("RenderingData", 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture->GetTexture());
program->SetInteger("diffuseTextureArray", 0);
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, quads.size());
NOTE: I also tried to limit the vertex data uploading to the first quad, effectively making it the only one with valid data, a part from the per-instance data (RenderingData struct: a model matrix and an uint), which were all fed to the SSBO. This still lead to 3 quads being rendered, which shouldn't be the case under normal circumstances, right?
EDIT: I made a sample code which replicates the problem; it should render a green quad and a red quad, however, it just renders 2 red quads. Also, even if you try to change the second quads' attributes (even like placing all 0s), it'll still just render. Somehow only the first quads' vertices are getting read and used for each subsequent quad.
C++ code:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <vector>
#define GLEW_STATIC
#include "GL/glew.h"
#include "GLFW/glfw3.h"
#include <IL/il.h>
#include <IL/ilu.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
struct Vertex{
glm::vec2 pos, tex;
glm::vec3 color;
};
struct Quad{
std::vector<Vertex> vertices;
glm::mat4 model;
Quad(glm::vec2 pos, glm::vec2 size, const std::vector<Vertex> &v){
vertices = v;
model = glm::mat4();
model = glm::translate(model, glm::vec3(pos, 0.f));
model = glm::scale(model, glm::vec3(size, 1.f));
}
};
class Renderer{
private:
GLuint VAO, VBO, instanceVBO, EBO;
std::vector<Quad*> quads;
std::vector<Vertex> vertices;
std::vector<glm::mat4> models;
std::vector<GLuint> indices;
public:
Renderer(){
/*** VAO ***/
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
/*** Per-vertex VBO ***/
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0); // Position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
glEnableVertexAttribArray(1); // Tex Coord
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(glm::vec2)));
glEnableVertexAttribArray(2); // Color
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*)(sizeof(glm::vec2) * 2));
/*** Per-instance VBO ***/
glGenBuffers(1, &instanceVBO);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(3); // Model matrix - row1
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), nullptr);
glVertexAttribDivisor(3, 1);
glEnableVertexAttribArray(4); // Model matrix - row2
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4)));
glVertexAttribDivisor(4, 1);
glEnableVertexAttribArray(5); // Model matrix - row3
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4) * 2));
glVertexAttribDivisor(5, 1);
glEnableVertexAttribArray(6); // Model matrix - row4
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4) * 3));
glVertexAttribDivisor(6, 1);
/*** EBO ***/
glGenBuffers(1, &EBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, 0, nullptr, GL_DYNAMIC_DRAW);
}
~Renderer(){
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &instanceVBO);
glDeleteBuffers(1, &EBO);
glDeleteVertexArrays(1, &VAO);
}
void AddQuad(Quad *const quad){
quads.push_back(quad);
for(unsigned int i = 0; i < 4; i++)
vertices.push_back(quad->vertices[i]);
models.push_back(quad->model);
GLuint start = ((indices.empty()) ? 0 : indices[indices.size() - 1] + 1);
GLuint newIndices[] = {
start, start + 1, start + 2,
start, start + 3, start + 2
};
for(unsigned int i = 0; i < 6; i++)
indices.push_back(newIndices[i]);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), &vertices[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::mat4) * quads.size(), &models[0], GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices[0], GL_DYNAMIC_DRAW);
}
void Render(GLuint &program){
glUseProgram(program);
glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0, quads.size());
}
void CheckQuads(){
for(unsigned int i = 0; i < quads.size(); i++)
{
std::cout << "Quad n." << i << "\n";
for(unsigned int j = 0; j < 4; j++)
{
std::cout << "\tVert " << j << ":\n";
std::cout << "\t" << quads[i]->vertices[j].pos.x << " " << quads[i]->vertices[j].pos.y << "\n";
std::cout << "\t" << quads[i]->vertices[j].tex.x << " " << quads[i]->vertices[j].tex.y << "\n";
std::cout << "\t" << quads[i]->vertices[j].color.r << " " << quads[i]->vertices[j].color.g << " " << quads[i]->vertices[j].color.b << "\n";
}
}
}
};
GLuint GenerateShader(const std::string &filename, GLenum shaderType)
{
GLuint shader;
std::ifstream file(filename.c_str());
std::stringstream ss;
ss << file.rdbuf();
file.close();
const GLchar *source = ss.str().c_str();
shader = glCreateShader(shaderType);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
return shader;
}
int main()
{
/*** Window + Context ***/
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow *window = glfwCreateWindow(640, 480, "OpenGL Incorrect Rendering Test", nullptr, nullptr);
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
glewInit();
glViewport(0, 0, 640, 480);
glClearColor(0.f, 0.f, 0.f, 1.f);
glm::mat4 projection = glm::ortho(0.f, 640.f, 480.f, 0.f, -1.f, 1.f);
/*** Shader Program ***/
GLuint program = glCreateProgram();
glAttachShader(program, GenerateShader("vertex_shader.glsl", GL_VERTEX_SHADER));
glAttachShader(program, GenerateShader("fragment_shader.glsl", GL_FRAGMENT_SHADER));
glLinkProgram(program);
glUseProgram(program);
glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
/*** Renderer ***/
Renderer *renderer = new Renderer();
// Quad 1
Vertex v1 = {glm::vec2(0.f, 0.f), glm::vec2(0.f, 0.f), glm::vec3(1.f, 0.f, 0.f)};
Vertex v2 = {glm::vec2(1.f, 0.f), glm::vec2(0.f, 0.f), glm::vec3(1.f, 0.f, 0.f)};
Vertex v3 = {glm::vec2(1.f, 1.f), glm::vec2(0.f, 0.f), glm::vec3(1.f, 0.f, 0.f)};
Vertex v4 = {glm::vec2(0.f, 1.f), glm::vec2(0.f, 0.f), glm::vec3(1.f, 0.f, 0.f)};
renderer->AddQuad(new Quad(glm::vec2(300, 10), glm::vec2(150, 150), {v1, v2, v3, v4}));
// Quad 2
Vertex v5 = {glm::vec2(0.f, 0.f), glm::vec2(0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)};
Vertex v6 = {glm::vec2(1.f, 0.f), glm::vec2(0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)};
Vertex v7 = {glm::vec2(1.f, 1.f), glm::vec2(0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)};
Vertex v8 = {glm::vec2(0.f, 1.f), glm::vec2(0.f, 0.f), glm::vec3(0.f, 1.f, 0.f)};
renderer->AddQuad(new Quad(glm::vec2(30, 10), glm::vec2(150, 150), {v5, v6, v7, v8}));
while(!glfwWindowShouldClose(window))
{
glfwPollEvents();
if(glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
glClear(GL_COLOR_BUFFER_BIT);
renderer->Render(program);
glfwSwapBuffers(window);
}
delete renderer;
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}
vertex_shader.glsl:
#version 430 core
layout (location = 0) in vec2 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 color;
layout (location = 3) in mat4 model;
out vec2 uv;
out vec3 col;
uniform mat4 projection;
void main()
{
uv = texCoord;
col = color;
gl_Position = projection * model * vec4(position, 0.f, 1.f);
}
fragment_shader.glsl:
#version 430 core
in vec2 uv;
in vec3 col;
out vec4 color;
void main()
{
color = vec4(col, 1.f);
}