Instanced rendering the draw function block after rendering

Hi!
Here is my shaders :

if (settings.versionMajor >= 3 && settings.versionMinor >= 3) {
    glGenBuffers(1, &vboWorldMatrices);
    const std::string vertexShader =
    R"(#version 330 core
        layout (location = 0) in vec3 position;
        layout (location = 1) in vec4 color;
        layout (location = 2) in vec2 texCoords;
        layout (location = 10) in mat4 worldMat;
        uniform mat4 projectionMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 textureMatrix;
        out vec2 fTexCoords;
        out vec4 frontColor;
        void main() {
            gl_Position = worldMat * viewMatrix * projectionMatrix * vec4(position, 1.f);
            fTexCoords = (textureMatrix * vec4(texCoords, 1.f, 1.f)).xy;
            frontColor = color;
        }
    )";
    const std::string fragmentShader =
    R"(#version 330 core
    uniform sampler2D texture;
    in vec4 frontColor;
    in vec2 fTexCoords;
    void main() {
        gl_FragColor = texture2D (texture, fTexCoords.xy) * frontColor;
    })";

I’ve putted the first vertices and the world matrix in VBO like this :

RenderStates states:

if (frameBuffer.getSettings().versionMajor >= 3 && frameBuffer.getSettings().versionMinor >= 3) {
    math::Matrix4f viewMatrix = view.getViewMatrix().getMatrix();
    math::Matrix4f projMatrix = view.getProjMatrix().getMatrix();
    shader.setParameter("projectionMatrix", projMatrix);
    shader.setParameter("viewMatrix", viewMatrix);
    VertexBuffer vb(sf::TrianglesStrip);
    std::vector<float> matrices;
    for (unsigned int i = 0; i < m_instances.size(); i++) {
        if (m_instances[i].getAllVertices().getVertexCount() > 0) {
            std::vector<TransformMatrix*> tm = m_instances[i].getTransforms();
            for (unsigned int j = 0; j < tm.size(); j++) {
                std::array<float, 16> matrix = tm[j]->getMatrix().toGlMatrix();
                for (unsigned int n = 0; n < 16; n++) {
                    matrices.push_back(matrix[n]);
                }
            }
            std::cout<<"vbo world matrices!"<<std::endl;
            glCheck(glBindBuffer(GL_ARRAY_BUFFER, vboWorldMatrices));
            glCheck(glBufferData(GL_ARRAY_BUFFER, sizeof(matrices), &matrices[0], GL_DYNAMIC_DRAW));
            std::cout<<"vbo world matrices filled!"<<std::endl;
            if (m_instances[i].getVertexArrays().size() > 0) {
                for (unsigned int j = 0; j < m_instances[i].getVertexArrays()[0]->getVertexCount(); j++) {
                    vb.append((*m_instances[i].getVertexArrays()[0])[j]);
                }
            }
            std::cout<<"vertices filled"<<std::endl;
            states.shader = &shader;
            std::cout<<"shader set"<<std::endl;
            states.texture = m_instances[i].getMaterial().getTexture();
            std::cout<<"texture defined"<<std::endl;
            if (m_instances[i].getMaterial().getTexture() != nullptr) {
                math::Matrix4f texMatrix = m_instances[i].getMaterial().getTexture()->getTextureMatrix();
                shader.setParameter("textureMatrix", texMatrix);
                std::cout<<"texture set to shader"<<std::endl;
            }
            std::cout<<"draw instanced"<<std::endl;
            frameBuffer.drawInstanced(vb, vboWorldMatrices, sf::TrianglesStrip, 0, 4, tm.size(), states);
            }
        }

And I set the pointers in the draw function like this :

if (m_cache.lastVboBuffer != &vertexBuffer) {
               if (m_versionMajor >= 3 && m_versionMinor >= 3) {
                   std::cout<<"define pointers"<<std::endl;
                   glCheck(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.vboVertexBuffer));
                   glCheck(glEnableVertexAttribArray(0));
                   glCheck(glEnableVertexAttribArray(1));
                   glCheck(glEnableVertexAttribArray(2));
                   glCheck(glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE,sizeof(Vertex), (GLvoid*) 0));
                   glCheck(glVertexAttribPointer(1, 4,GL_UNSIGNED_BYTE,GL_TRUE,sizeof(Vertex),(GLvoid*) 12));
                   glCheck(glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 16));
                   glCheck(glDisableVertexAttribArray(0));
                   glCheck(glDisableVertexAttribArray(1));
                   glCheck(glDisableVertexAttribArray(2));
                   glCheck(glBindBuffer(GL_ARRAY_BUFFER, vboWorldMatrices));
                   for (unsigned int i = 0; i < 4 ; i++) {
                       glEnableVertexAttribArray(10 + i);
                       glVertexAttribPointer(10 + i, 4, GL_FLOAT, GL_FALSE, sizeof(math::Matrix4f),
                                               (const GLvoid*)(sizeof(GLfloat) * i * 4));
                       glVertexAttribDivisor(10 + i, 1);
                   }
               }
               m_cache.lastVboBuffer = &vertexBuffer;
           }
           if (m_versionMajor >= 3 && m_versionMinor >= 3) {
               static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES,
                                                  GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS};
               GLenum mode = modes[type];
               std::cout<<"draw instanced"<<std::endl;
               glDrawArraysInstanced(mode,start,nb,nbInstances);
               std::cout<<"instanced rendered"<<std::endl;
           }

It display the message instanced rendered but after this, it blocks…

Oh, it doesn"t block anymore and the texture was not displayed because I enabled GL_TEXTURE_2D and GL_TEXTURE_3D at the same time.
But I’ve a black screen.
I don’t understand very well instanced rendering, if I understand well instanced rendering allow us to pass less vertices to opengl so I passed only the vertices of the first tile in a VBO, and I passed the transformations matrices of all my tiles into another VBO.
But it doesn’t work, I have a black screen.

That part seems okay. Specifying an attribute divisor of 1 means that the corresponding attribute array will be indexed with the instance ID rather than the vertex ID.

This is wrong:

Using sizeof on a std::vector will return the (fixed) size of the housekeeping structure, not the size of the data. You need to use the .size() method and multiply the result by the element size (in this case, sizeof float).

Ok I’ve changed this line :
`

glCheck(glBufferData(GL_ARRAY_BUFFER, matrices.size() * sizeof(float), &matrices[0], GL_DYNAMIC_DRAW));

`
But I still have a black screen.
I don’t understand the code looks to be good.

Whether it looks good or not, indications are you’ve got at least one bug in there. So, debug it! Make simplifying changes until you get some output. For instance, hack the fragment shader to always write out white. Nuke everything in the vertex shader except the gl_Position setting (with minimal matrix math). Etc.

Yeah, this looks backwards. Try reversing the order of the matrices on the right hand side.

You want to end up with the gl_Position value you compute in clip space. Given your math, I’m inferring that position is in object space. So you probably want this:

  • glPosition = projectionMatrix * viewMatrix * worldMat * vec4( position, 1 )
  • CLIP-SPACE = (CLIP <- EYE) * (EYE <- WORLD) * (WORLD <- OBJECT) * OBJECT-SPACE

If in the end you try a bunch of things and get stumped, then post a short, stand-alone GLUT- or GLFW-based test program, and I’ll bet someone will download and run it and give you some ideas.

I’ll make test in the main to find where is the problem.

Ok the problem is here, when I use a vertex structure to put vertex position, color and texture coordinates, it doesn’t draw anything anymore.
If I don’t use a vertex structure but an array of float instead it works.

 int main(int argc, char* argv[])
{
    /*EXPORT_CLASS_GUID(BoundingVolumeBoundingBox, BoundingVolume, BoundingBox)
    EXPORT_CLASS_GUID(EntityTile, Entity, Tile)
    EXPORT_CLASS_GUID(EntityTile, Entity, BigTile)
    EXPORT_CLASS_GUID(EntityWall, Entity, g2d::Wall)
    EXPORT_CLASS_GUID(EntityDecor, Entity, g2d::Decor)
    EXPORT_CLASS_GUID(EntityAnimation, Entity, Anim)
    EXPORT_CLASS_GUID(EntityHero, Entity, Hero)
    EXPORT_CLASS_GUID(EntityMesh, Entity, Mesh)
    MyAppli app(sf::VideoMode(800, 600), "Test odfaeg");
    return app.exec();*/
    sf::Window window(sf::VideoMode(800, 600), "Test instanced rendering", sf::Style::Default, sf::ContextSettings(24, 8, 8, 4, 6));
    glEnable(GL_DEPTH_TEST);
    const std::string vertexShader =
                R"(#version 330 core
                    layout (location = 0) in vec3 position;
                    layout (location = 1) in vec4 color;
                    layout (location = 2) in vec2 texCoords;
                    layout (location = 3) in mat4 worldMat;
                    /*uniform mat4 projectionMatrix;
                    uniform mat4 viewMatrix;
                    uniform mat4 textureMatrix;*/
                    out vec2 fTexCoords;
                    out vec4 frontColor;
                    void main() {
                        gl_Position = worldMat * vec4(position, 1.f);
                        //fTexCoords = (textureMatrix * vec4(texCoords, 1.f, 1.f)).xy;
                        frontColor = color;
                    }
                )";
                const std::string fragmentShader =
                R"(#version 330 core
                uniform sampler2D texture;
                in vec4 frontColor;
                in vec2 fTexCoords;
                void main() {
                    gl_FragColor = /*texture2D (texture, fTexCoords.xy) **/ frontColor;
                })";
    sf::Shader shader;
    if (!shader.loadFromMemory(vertexShader, fragmentShader)) {
        std::cerr<<"Failed to load shaders"<<std::endl;
        return -1;
    }
    TransformMatrix tm;
    std::vector<float> matrices;
    float offset = 0.1f;
    for (int y = -10; y < 10; y += 2)
    {
        for (int x = -10; x < 10; x += 2)
        {
            tm.setTranslation(Vec3f((float)x / 10.0f + offset, (float)y / 10.0f + offset, 0));
            tm.update();
            std::array<float, 16> matrix = tm.getMatrix().transpose().toGlMatrix();
            for (unsigned int n = 0; n < 16; n++) {
                matrices.push_back(matrix[n]);
            }
        }
    }
    // store instance data in an array buffer
    // --------------------------------------
    unsigned int instanceVBO;
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        std::cerr<<"failed to initialize glew!"<<std::endl;
        return -1;
    }
    glGenBuffers(1, &instanceVBO);
    glBindBuffer(GL_ARRAY_BUFFER, instanceVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * matrices.size(), &matrices[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    Vertex v1(sf::Vector3f(-0.05f,  0.05f, 0.f), sf::Color(255, 0, 0, 255), sf::Vector2f(0, 0));
    Vertex v2(sf::Vector3f(0.05f, -0.05f, 0.f),  sf::Color(0, 255, 0, 255), sf::Vector2f(0, 0));
    Vertex v3(sf::Vector3f(-0.05f, -0.05f, 0.f),  sf::Color(0, 0, 255, 255), sf::Vector2f(0, 0));
    Vertex v4(sf::Vector3f(-0.05f,  0.05f, 0.f),  sf::Color(255, 0, 0, 255), sf::Vector2f(0, 0));
    Vertex v5(sf::Vector3f(0.05f, -0.05f, 0.f),  sf::Color(0, 255, 0, 255), sf::Vector2f(0, 0));
    Vertex v6(sf::Vector3f(0.05f,  0.05f, 0.f),  sf::Color(0, 255, 255, 255), sf::Vector2f(0, 0));
    std::vector<Vertex> vertices;
    vertices.push_back(v1);
    vertices.push_back(v2);
    vertices.push_back(v3);
    vertices.push_back(v4);
    vertices.push_back(v5);
    vertices.push_back(v6);



    unsigned int quadVAO, quadVBO;
    glGenVertexArrays(1, &quadVAO);
    glGenBuffers(1, &quadVBO);
    glBindVertexArray(quadVAO);
    glCheck(glBindBuffer(GL_ARRAY_BUFFER, quadVBO));
    glCheck(glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW));
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,  sizeof(Vertex), (void*)0);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE,  sizeof(Vertex), (void*) 12);
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE,  sizeof(Vertex), (void*) 16);
    // also set instance data
    for (unsigned int i = 0; i < 4; i++) {
        glEnableVertexAttribArray(3+i);
        glBindBuffer(GL_ARRAY_BUFFER, instanceVBO); // this attribute comes from a different vertex buffer
        glVertexAttribPointer(3+i, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f), (const GLvoid*)(sizeof(GLfloat) * i * 4));
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glVertexAttribDivisor(3+i, 1); // tell OpenGL this is an instanced vertex attribute.
    }

    while (window.isOpen()) {
        sf::Event event;
        while (window.pollEvent(event)) {
            if (event.type == sf::Event::Closed) {
                window.close();
            }
        }
        // render
        // ------
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(shader.getNativeHandle());
        glBindVertexArray(quadVBO);
        glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); // 100 triangles of 6 vertices each
        glBindVertexArray(0);
        window.display();
    }
    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &quadVAO);
    glDeleteBuffers(1, &quadVBO);
    return 0;
}

Try sanity checking your structure size and field offsets to make sure that they are what you think they are:

#include <stdio.h>
#include <stddef.h>
...
    printf( "sizeof( Vertex )             = %zu\n", sizeof( Vertex ) );
    printf( "offsetof( Vertex, pos      ) = %zu\n", offsetof( Vertex, pos      ) );
    printf( "offsetof( Vertex, color    ) = %zu\n", offsetof( Vertex, color    ) );
    printf( "offsetof( Vertex, texcoord ) = %zu\n", offsetof( Vertex, texcoord ) );

Hi! Structure size and offsets are correct :

`

.fctbNone{ } .fctbStyle0Style2{ } sizeof( Vertex ) = 24 O> offsetof( Vertex, pos ) = 0 O> offsetof( Vertex, color ) = 12 O> offsetof( Vertex, texcoord ) = 16

`
It’s like it displays quads at the first frame and then it disappear.
I really don’t understand, the code is correct.

I’ve found why it doesn’t work in the big code.
It seems when I disable the vertex attrib array it doesn’t draw anything so I put it in comment and now it works :

void RenderTarget::drawInstanced(VertexBuffer& vertexBuffer, unsigned int vboWorldMatrices, enum sf::PrimitiveType type, unsigned int start, unsigned int nb, unsigned int nbInstances, RenderStates states) {
            if (vertexBuffer.getVertexCount() == 0) {
                return;
            }

            if (activate(true))
            {

                // First set the persistent OpenGL states if it's the very first call
                if (!m_cache.glStatesSet)
                    resetGLStates();
                // Apply the view
                if (m_cache.viewChanged || m_view.viewUpdated)
                    applyCurrentView();
                // Apply the blend mode
                if (states.blendMode != m_cache.lastBlendMode)
                    applyBlendMode(states.blendMode);
                sf::Uint64 textureId = states.texture ? states.texture->m_cacheId : 0;
                /*if (textureId > 0)
                     std::cout<<"texture id : "<<textureId<<" "<<states.texture->m_texture<<std::endl;*/
                if (textureId != m_cache.lastTextureId)
                    applyTexture(states.texture);

                // Apply the shader
                if (states.shader)
                    applyShader(states.shader);
                if (m_versionMajor >= 3 && m_versionMinor >= 3)
                    glCheck(glBindVertexArray(m_vao));
                if (m_cache.lastVboBuffer != &vertexBuffer) {
                    if (m_versionMajor >= 3 && m_versionMinor >= 3) {
                        std::cout<<"define pointers"<<std::endl;
                        glCheck(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.vboVertexBuffer));
                        glCheck(glEnableVertexAttribArray(0));
                        glCheck(glEnableVertexAttribArray(1));
                        glCheck(glEnableVertexAttribArray(2));
                        glCheck(glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE,sizeof(Vertex), (GLvoid*) 0));
                        glCheck(glVertexAttribPointer(1, 4,GL_UNSIGNED_BYTE,GL_TRUE,sizeof(Vertex),(GLvoid*) 12));
                        glCheck(glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 16));
                        glCheck(glBindBuffer(GL_ARRAY_BUFFER, 0));
                        for (unsigned int i = 0; i < 4 ; i++) {
                            glCheck(glEnableVertexAttribArray(3 + i));
                            glCheck(glBindBuffer(GL_ARRAY_BUFFER, vboWorldMatrices));
                            glCheck(glVertexAttribPointer(3 + i, 4, GL_FLOAT, GL_FALSE, sizeof(math::Matrix4f),
                                                    (const GLvoid*)(sizeof(GLfloat) * i * 4)));
                            glCheck(glBindBuffer(GL_ARRAY_BUFFER, 0));
                            glCheck(glVertexAttribDivisor(3 + i, 1));
                        }
                    }
                    m_cache.lastVboBuffer = &vertexBuffer;

                }
                if (m_versionMajor >= 3 && m_versionMinor >= 3) {
                   /* glCheck(glEnableVertexAttribArray(0));
                    glCheck(glEnableVertexAttribArray(1));
                    glCheck(glEnableVertexAttribArray(2));
                    for (unsigned int i = 0; i < 4 ; i++) {
                        glCheck(glEnableVertexAttribArray(10 + i));
                    }
                    glCheck(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.vboVertexBuffer));*/
                    static const GLenum modes[] = {GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES,
                                                       GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, GL_QUADS};
                    GLenum mode = modes[type];
                    std::cout<<"draw instanced"<<std::endl;
                    glCheck(glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.vboVertexBuffer));
                    glCheck(glDrawArraysInstanced(mode,start,nb,nbInstances));
                    std::cout<<"instanced rendered"<<std::endl;
                    /*glCheck(glDisableVertexAttribArray(0));
                    glCheck(glDisableVertexAttribArray(1));
                    glCheck(glDisableVertexAttribArray(2));
                    for (unsigned int i = 0; i < 4 ; i++) {
                        glCheck(glDisableVertexAttribArray(3 + i));
                    }*/
                    glCheck(glBindBuffer(GL_ARRAY_BUFFER, 0));
                    glCheck(glBindVertexArray(0));
                }
            }
        }

About sizes:
position 3
color 4
tex 2
mat4 16
sum 25
that doesn’t match your vertex-size of 24

As an aside: aren’t the attribute “world-matrix” destined to become a cause of error?
As named, one would expect it to be a common value for all the items you juggle (hench the natural choise for a uniform).
As a parameter for individual pose (position & direction) it repeats the position attribute and should probably be named model_matrix? Then, again, I’ve never tried instanced drawing!

Check your VAO bindings. Remember that all state related to attribute arrays is stored in the currently bound VAO. The current GL_ARRAY_BUFFER binding isn’t stored in the VAO and isn’t used by draw calls. What is stored in the VAO and used by draw calls is the buffer which was bound to that target at the time of each glVertexAttribPointer call (each such call stores all of its parameters plus the current VBO). While not applicable here, the current GL_ELEMENT_ARRAY_BUFFER binding is also stored in the VAO.

Also, I note that for the per-instance state you’re enabling array 10+i but disabling array 3+i.

The matrix isn’t included in the vertex size (it’s per-instance, not per-vertex). The vertex structure consists of 3 floats (position), 4 unsigned bytes (colour), 2 floats (texture coordinates), which is 24 bytes in total.

Makes sense. Glad you found it!

Yeah, looking back for this in your code (and stripping off all the glCheck() clutter), I see it:

glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glVertexAttribPointer(0, 3,GL_FLOAT,GL_FALSE,sizeof(Vertex), (GLvoid*) 0);
glVertexAttribPointer(1, 4,GL_UNSIGNED_BYTE,GL_TRUE,sizeof(Vertex),(GLvoid*) 12);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (GLvoid*) 16);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(2);

The enable state for a specific vertex attribute doesn’t get latched into the bound VAO when you call glVertexAttribPointer(), but rather whenever you call glDisableVertexAttribArray() or glEnableVertexAttribArray().

So after the above, the enable state of attrs 0, 1, and 2 is “disabled”.

By the way, re glCheck() and Checking for OpenGL Errors, you may want to consult this wiki page and read up on The Easy Way to do this.

The problem was that : I must enable vertexAttribArray before drawing otherwise it draw nothing.

Solved, thanks.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.