Render with-in boarders? (OPENGL | SDL 2)


#1

Surprisingly i finally got OpenGL to work with me, and it’s great, but i want to ask a simple question: How can i make OpenGL only render whats inside the camera’s view/boarders. It’s a 2D prototype, everything is a sprite. There’s over 7,000 tiles getting pushed to the render stack. I tried switching from glBegin/glEnd to VBO’s to see the performance would elevate and nothing happened, the same, slow speed. When i run the prototype using pure SDL it runs like a dream, 60 fps and all. But when OpenGL is in it the fps drops by atleast 50. So what steps would i have to take to get OpenGL to render only what’s in the screen/camera (I’m using a combination of OpenGL and SDL btw.)


#2

Hello!
Inside SDL there is a checking if statement, which restricts drawing of non-visible sprites. You just need to add a similar checking if statement to your code.


#3

If what your drawing are just rectangular images, possibly rotated, scaled, translated, and/or sheared, then you’re doing something wrong in your draw code, I can easily render 10 times as many at way more than 60fps on an old graphics card (geforce gt 710). You should set it up so you can draw them all with a single glDrawArrays or similar function call.


#4

They’re rectangular images. How would i go about drawing all the images with one call? In my load function i create a VBO and IBO

void load()
{
glGenBuffers(1, &VBO);
glBindBuffer( GL_ARRAY_BUFFER, VBO);
glBufferData( GL_ARRAY_BUFFER, 4 * sizeof(coord), vda, GL_DYNAMIC_DRAW );

glGenBuffers(1, &vars::tIBO);
glBindBuffer( GL_ARRAY_BUFFER, IBO);
glBufferData( GL_ARRAY_BUFFER, 4 * sizeof(GLuint), ind, GL_DYNAMIC_DRAW );

glBindBuffer( GL_ARRAY_BUFFER, NULL );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, NULL );

}

And i upload them to the VBO for drawing with my render function

void Render() { coord vda[ 4 ];
//Tex pos
vda[ 0 ].vex.ms = 0; vda[ 0 ].vex.mt = 0;
vda[ 1 ].vex.ms = 1; vda[ 1 ].vex.mt = 0;
vda[ 2 ].vex.ms = 1; vda[ 2 ].vex.mt = 1;
vda[ 3 ].vex.ms = 0; vda[ 3 ].vex.mt = 1;

//Vex poss
vda[ 0 ].pos.x = 0;  vda[ 0 ].pos.y = 0;
vda[ 1 ].pos.x = mov.w; vda[ 1 ].pos.y = 0;
vda[ 2 ].pos.x = mov.w; vda[ 2 ].pos.y = mov.h;
vda[ 3 ].pos.x = 0;  vda[ 3 ].pos.y = mov.h;


glTranslatef(mov.x, mov.y, 0.f);

glBindTexture( GL_TEXTURE_2D, tex);

//Enable vertex and texture coordinate arrays
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );

    //Bind vertex buffer
    glBindBuffer( GL_ARRAY_BUFFER, vars::tVBO );

    glBufferSubData( GL_ARRAY_BUFFER, 0, 4 * sizeof(coord), vda );

    glTexCoordPointer(2, GL_FLOAT, sizeof(coord), (GLvoid*)offsetof(coord, vex));

 
    glVertexPointer(2, GL_FLOAT, sizeof(coord), (GLvoid*)offsetof(coord, pos));

    
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vars::tIBO);
    glDrawElements(GL_QUADS, 4, GL_UNSIGNED_INT, NULL);

//Turn off
glDisableClientState( GL_TEXTURE_COORD_ARRAY );
glDisableClientState( GL_VERTEX_ARRAY );

}

I thought this storing the data from the image and it’s math data and then drawing it all at once, but the program still runs slowly. I’m new to OpenGL so forgive me :stuck_out_tongue:


#5

What you want to do is to group the rectangles by which texture they use, then, for each texture, use a single buffer filled with a quad for each rectangle you want to render, then after filling the buffer with all the quads, call glDrawArrays.

void drawAllRectangles(const std::vector<Rectangle> &rectangles)
{
    typedef GLuint TextureID;
    struct Vertex
    {
        float x, y;
        float u, v;
    };
    static_assert(sizeof(Vertex) == 4 * sizeof(float), "Vertex is packed incorrectly");
    std::unordered_map<TextureID, std::vector<Vertex>> verticesMap;
    std::size_t neededBufferVertexCount = 0;
    for(auto &rect : rectangles)
    {
        auto &vertices = verticesMap[rect.texture];
        vertices.push_back({rect.x,          rect.y,          0, 0});
        vertices.push_back({rect.x + rect.w, rect.y,          1, 0});
        vertices.push_back({rect.x + rect.w, rect.y + rect.h, 1, 1});
        vertices.push_back({rect.x,          rect.y + rect.h, 0, 1});
        if(neededBufferVertexCount < vertices.size())
            neededBufferVertexCount = vertices.size();
    }
    glBindBuffer(GL_ARRAY_BUFFER, buffer);
    // check buffer size
    if(bufferAllocatedSize < sizeof(Vertex) * neededBufferVertexCount)
    {
        // expand buffer
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * neededBufferVertexCount, nullptr, GL_DYNAMIC_DRAW);
        bufferAllocatedSize = sizeof(Vertex) * neededBufferVertexCount;
    }
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glVertexPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<GLvoid *>(offsetof(Vertex, x)));
    glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), reinterpret_cast<GLvoid *>(offsetof(Vertex, u)));
    for(auto &textureAndVertices : verticesMap)
    {
        TextureID texture = std::get<0>(textureAndVertices);
        auto &vertices = std::get<1>(textureAndVertices);
        glBindTexture(GL_TEXTURE_2D, texture);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vertex) * vertices.size(), static_cast<GLvoid *>(vertices.data()));
        glDrawArrays(GL_QUADS, 0, vertices.size());
    }
    // have the glDisable* calls to be consistent with your code, however, you could probably
    // just enable them in the init code and not have to enable or disable them in the render code.
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

Disclaimer: I have not actually tested the code I posted, so it may have bugs.