Sprite Batching attempt programmable pipeline, VBOs nothing shown

I’m losing my hair over this, I’ve been scrounging the net for some good non-fixed pipeline examples of how to implement sprite batching so that I can efficiently render thousands of sprites. Someone gave me some code that looks wonderful and exactly like what I need, but I can’t figure out why absolutely nothing is being shown on-screen. I’m using SDL2 and i use that (not shown) to init a 3.0 GL context which works great for other things, but this does not. I even tried decoupling my image loading stuff from it, and just create a junk checkboard image in it.

The only thing I can think of is that my offsets aren’t specified correctly, but I’ve gone through them several times and can’t find anything wrong. All I want to do is be able to see 1 sprite. Then it should be easy to take it from there, with this code…

It doesn’t assert, the shaders compile fine, etc…so I don’t know what’s going on. And it isn’t a hidden color because I tried disabling glClearColor or setting it to something else.

And yeah, I’m very new to opengl.

Any help on pointing me in the right direction would be greatly appreciated, I simply cannot find material that covers this exact topic.


void Game::init()
{
   glewInit();
    glClearColor(1.f, 1.f, 1.f, 1.0f);

    glViewport(0, 0, SCREEN_W, SCREEN_H);

    tick();
    shutdown();
}

/* Each vertex is:
 * two floats for the 2d coordinate
 * four u8s for the color
 * two f32s for the texcoords
 * the vbo contains data of the aforementioned elements interleaved.
 * Each sprite has four vertices.
 */
typedef float spriteVertex[5];
GLuint new_vao = 0;
GLuint new_vbo = 0;
GLuint new_ebo = 0;
GLuint new_vs;
GLuint new_fs;
GLuint new_sp;
typedef uint32_t u32;
typedef float f32;


GLuint tex;
static int spriteCount = 1;
void initGL() {
    //////////////////// create texture, checkerboard texture
    glGenTextures( 1, &tex );
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, tex );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
    float pixels[] = {
        0.0f, 0.0f, 0.0f,   1.0f, 1.0f, 1.0f,
        1.0f, 1.0f, 1.0f,   0.0f, 0.0f, 0.0f
    };
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_FLOAT, pixels );







    //////////////////////

    glGenVertexArrays(1, &new_vao);
    glBindVertexArray(new_vao);

    glGenBuffers(1,&new_vbo);
    glBindBuffer(GL_ARRAY_BUFFER,new_vbo);
    glBufferData(
        GL_ARRAY_BUFFER,
        spriteCount * 4 * sizeof(spriteVertex),
        NULL,
        GL_DYNAMIC_DRAW);

    GLenum err = glGetError();
    if (err)
    {

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        assert(0);
    }

    std::vector<u32> indicesv;

    // prepare and upload indices as a one time deal
    const u32 indices[] = { 0, 1, 2, 0, 2, 3 }; // pattern for a triangle array
    // for each possible sprite, add the 6 index pattern
    for (size_t j = 0; j < spriteCount; j++)
    {
        for (size_t i = 0; i < sizeof(indices)/sizeof(*indices); i++)
        {
            indicesv.push_back(4*j + indices[i]);
        }
    }

    glGenBuffers(1,&new_ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,new_ebo);
    glBufferData(
        GL_ELEMENT_ARRAY_BUFFER,
        indicesv.size()*sizeof(u32),
        indicesv.data(),
        GL_STATIC_DRAW);

    err = glGetError();
    if (err)
    {

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);
        assert(0);
    }


    static const char* vshader_src =
        "#version 120
"

        "attribute vec2 position;"

        "attribute vec2 texcoord;"
        "varying vec2 frag_texcoord;"

        "attribute vec4 color;"
        "varying vec4 frag_color;"

        "void main() {"
        "    gl_Position = vec4( position, 0.0, 1.0 );"
        "    frag_texcoord = texcoord;"
        "    frag_color = color;"
        "}";

    static const char* fshader_src =
        "#version 120
"

        "varying vec2 frag_texcoord;"
        "varying vec4 frag_color;"

        "uniform sampler2D sampler;"

        "void main(void) {"
        "    gl_FragColor = frag_color * texture2D(sampler,frag_texcoord);"
        "}";

    GLint status;
    GLchar infolog[1024];
    GLsizei infolog_len;

    new_vs = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(new_vs, 1, &vshader_src, NULL);
    glCompileShader(new_vs);

    glGetShaderiv(new_vs,GL_COMPILE_STATUS,&status);

    if (status == GL_FALSE)
    {
        glGetShaderInfoLog(new_vs,sizeof(infolog),&infolog_len,infolog);
        std::cout
                << "Failed to compile SpriteBatch vertex shader."
                << std::endl
                << infolog << std::endl;

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);
        glDeleteShader(new_vs);
        assert(0);
    }

    new_fs = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(new_fs, 1, &fshader_src, NULL);
    glCompileShader(new_fs);

    glGetShaderiv(new_fs,GL_COMPILE_STATUS,&status);

    if (status == GL_FALSE)
    {
        glGetShaderInfoLog(new_fs,sizeof(infolog),&infolog_len,infolog);
        std::cout
                << "Failed to compile SpriteBatch fragment shader."
                << std::endl
                << infolog << std::endl;

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);
        glDeleteShader(new_vs);
        glDeleteShader(new_fs);
        assert(0);
    }

    new_sp = glCreateProgram();
    glAttachShader(new_sp, new_vs);
    glAttachShader(new_sp, new_fs);
    glLinkProgram(new_sp);

    glGetProgramiv(new_sp,GL_LINK_STATUS,&status);

    if (status == GL_FALSE)
    {
        glGetProgramInfoLog(new_sp,sizeof(infolog),&infolog_len,infolog);
        std::cout << "Failed to link SpriteBatch shader program."
                  << std::endl
                  << infolog << std::endl;

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);
        glDeleteShader(new_vs);
        glDeleteShader(new_fs);
        glDeleteProgram(new_sp);
        assert(0);
    }

    glValidateProgram(new_sp);
    glGetProgramiv(new_sp,GL_VALIDATE_STATUS,&status);

    if (status == GL_FALSE)
    {

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);
        glDeleteShader(new_vs);
        glDeleteShader(new_fs);
        glDeleteProgram(new_sp);
        assert(0);
    }

    size_t buffer_offset = 0;

    GLint pos_attrib = glGetAttribLocation(new_sp, "position");
    glEnableVertexAttribArray(pos_attrib);
    glVertexAttribPointer(
        pos_attrib,
        2,
        GL_FLOAT,
        GL_FALSE,
        sizeof(spriteVertex),
        (const GLvoid*)buffer_offset);
    buffer_offset += sizeof(f32) * 2;

    GLint color_attrib = glGetAttribLocation(new_sp, "color");

    glEnableVertexAttribArray(color_attrib);
    glVertexAttribPointer(
        color_attrib,
        4,
        GL_UNSIGNED_BYTE,
        GL_TRUE,
        sizeof(spriteVertex),
        (const GLvoid*)buffer_offset);
    buffer_offset += sizeof(u32);

    GLint texcoord_attrib = glGetAttribLocation(new_sp, "texcoord");
    glEnableVertexAttribArray(texcoord_attrib);
    glVertexAttribPointer(
        texcoord_attrib,
        2,
        GL_FLOAT,
        GL_FALSE,
        sizeof(spriteVertex),
        (const GLvoid*)buffer_offset);

    err = glGetError();
    if (err)
    {

        glDeleteVertexArrays(1,&new_vao);
        glDeleteBuffers(1,&new_vbo);
        glDeleteBuffers(1,&new_ebo);

        assert(0);
    }
}

ACTUAL RENDER CODE, EACH FRAME:


void render() {
     // vertices that will be uploaded.
    spriteVertex vertices[4];

    // transform vertices and copy them to the buffer
    vertices[0][0] = vertices[0][1] = vertices[1][0] = vertices[3][1] = 0;
    /*    vertices[1][1] = f32(tex.size().y()) * std::abs(uvrect.height);
        vertices[2][0] = f32(tex.size().x()) * std::abs(uvrect.width);
        vertices[2][1] = f32(tex.size().y()) * std::abs(uvrect.height);
        vertices[3][0] = f32(tex.size().x()) * std::abs(uvrect.width);
        */

    vertices[1][1] = f32(1.0f);
    vertices[2][0] = f32(1.0f);
    vertices[2][1] = f32(1.0f);
    vertices[3][0] = f32(1.0f);


    // copy color to the buffer
    // copy color to the buffer
    for (size_t i = 0; i < sizeof(vertices)/sizeof(*vertices); i++)
    {
        uint32_t* colorp = reinterpret_cast<uint32_t*>(&vertices[i][2]);
        //        *colorp = color.bgra;
        uint8_t red = 120;
        uint8_t blue = 0;
        uint8_t green = 120;
        uint8_t alpha = 255;
        int32_t color = red | (green << 8) | (blue << 16) | (alpha << 24);
        *colorp = color;
    }

    // copy texcoords to the buffer
    vertices[0][3] = vertices[1][3] = 0.0f;
    vertices[0][4] = vertices[3][4] = 1.0f;
    vertices[1][4] = vertices[2][4] = 0.0f;
    vertices[2][3] = vertices[3][3] = 1.0f;

    // finally upload everything to the actual vbo
    glBindBuffer(GL_ARRAY_BUFFER,new_vbo);
    glBufferSubData(
        GL_ARRAY_BUFFER,
        0,
        spriteCount * sizeof(vertices),
        vertices);





    //////////////////////////////////////////////////////////////////////////////////////////
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,new_ebo);
    glBindBuffer(GL_ARRAY_BUFFER,new_vbo);
    glBindVertexArray(new_vao);

    glUseProgram(new_sp);


    glDrawElements(
        GL_TRIANGLES,
        6*(spriteCount), // 6 indices per 2 triangles
        GL_UNSIGNED_INT,
        (const GLvoid*)0);

    glUseProgram(0);
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER,0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);

    glDisable(GL_BLEND);
}

double fps = 0.0;

void Game::tick()
{
    Uint32 startTime = SDL_GetTicks();
    int frameCount = 0;

    initGL();
    while (m_running) {
        fps =(frameCount / float(SDL_GetTicks() - startTime)) * 1000;

        handleEvents();

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        render();


        SDL_GL_SwapWindow(m_window);

        ++frameCount;
    }

shutdown:
    shutdown();
}

I see two problems:

In the call to glDrawElements you pass the offset of the end of your indexes, that should be the offset of the start of the indexes: (GLvoid *)0.

In the glBufferSubData call you also pass the wrong offset and size, those should be 0 and spriteCount * sizeof(vertices), at least until you want to draw more than one sprite.

Okay, thanks. Fixed and edited my original post. Still nothing visible though. So those were probably just one of many problems.

Okay, I was giving it bad vertex coordinates, so now I see an uncolored untextured square like I should. Then I tried to use only the frag color and the color is being sent and works great.

Now the only issue is that my texture isn’t showing up on it no matter what. Even if I take color out of the equation.

EDIT: tex coord values seem fine, i tested them by setting the frag output colors to their values…

SOLVED! yay, turns out i wasn’t setting my min/mag filters. WHEW! works.