3
$\begingroup$

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:

middle one is way too stretched and the last one's UV coords are wrong

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);
}
$\endgroup$
  • 1
    $\begingroup$ I edited the question to include the image, code and link to the other question. However, in my opinion, it would have been better to edit the other question and include the new things you found out there. It is also encouraged to update the question to include what you have tried to solve the problem even after you posted it. See our Help Center. $\endgroup$ – Nero Nov 1 '15 at 11:38
  • $\begingroup$ Oh, I did not know about this. Thank you for both editing my questions and providing advice. $\endgroup$ – Darkilon Nov 1 '15 at 14:26
1
$\begingroup$

Seems like I had misunderstood instancing, that's why my code was acting strange: I was trying to use instancing while also trying to render different quads using different vertex data.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.