Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ OpenGL mesh rendering

Tags:

c++

mesh

opengl

I know there are a lot of resources about this on the internet but they didn't quite seem to help me.

What I want to achieve:
I am baking a mesh from data which stores the vertices inside a vector<Vector3>.
(Vector3 is a sctruct containg float x, y, z)
It stores triangles in a map<int, vector<int>>
(the key of the map is the submesh and the vector<int> the triangles)
the uv inside a vector<Vector2>
(Vector2 is a struct containing float x, y)
and a color value in vector<Color>
(the color value applies to vertices like the uv does)

Now I want to write a code that can read that data and draw it to the screen with maximum performance

What I got:

static void renderMesh(Mesh mesh, float x, float y, float z) {
    if (mesh.triangles.empty()) return;
    if (mesh.vertices.empty()) return;
    if (mesh.uvs.empty()) return;
    glColor3f(1, 1, 1);
    typedef std::map<int, std::vector<int>>::iterator it_type;
    for (it_type iterator = mesh.triangles.begin(); iterator != mesh.triangles.end(); iterator++) {
        int submesh = iterator->first;
        if (submesh < mesh.textures.size()) glBindTexture(GL_TEXTURE_2D, mesh.textures[submesh].id);
        else glBindTexture(GL_TEXTURE_2D, 0);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        for (int i = 0; i < iterator->second.size(); i += 3) {
            int t0 = iterator->second[i + 0];
            int t1 = iterator->second[i + 1];
            int t2 = iterator->second[i + 2];
            Vector3 v0 = mesh.vertices[t0];
            Vector3 v1 = mesh.vertices[t1];
            Vector3 v2 = mesh.vertices[t2];
            Color c0 = mesh.vertexColors[t0];
            Color c1 = mesh.vertexColors[t1];
            Color c2 = mesh.vertexColors[t2];
            Vector2 u0 = mesh.uvs[t0];
            Vector2 u1 = mesh.uvs[t1];
            Vector2 u2 = mesh.uvs[t2];
            glBegin(GL_TRIANGLES);
            glColor4f(c0.r / 255.0f, c0.g / 255.0f, c0.b / 255.0f, c0.a / 255.0f); glTexCoord2d(u0.x, u0.y); glVertex3f(v0.x + x, v0.y + y, v0.z + z);
            glColor4f(c1.r / 255.0f, c1.g / 255.0f, c1.b / 255.0f, c1.a / 255.0f); glTexCoord2d(u1.x, u1.y); glVertex3f(v1.x + x, v1.y + y, v1.z + z);
            glColor4f(c2.r / 255.0f, c2.g / 255.0f, c2.b / 255.0f, c2.a / 255.0f); glTexCoord2d(u2.x, u2.y); glVertex3f(v2.x + x, v2.y + y, v2.z + z);
            glEnd();
            glColor3f(1, 1, 1);
        }
    }
}

The problem:
I found out that the way I render is not the best way and that you can achieve higher performance with glDrawArrays (I think it was called).
Could you help me rewriting my code to fit with glDrawArrays, since what I found so far on the internet did not help me too much.

Thanks, and if there is any more information needed just ask.

like image 422
Sheldon Avatar asked Jan 23 '16 12:01

Sheldon


1 Answers

The use of functions like glBegin and glEnd is deprecated. Functions like glDrawArrays have a better performance, but slightly more complicated to use.

The problem of glBegin render techniques is you have to communicate each vertex one by one each time you want to draw something. Today, graphic cards are able to render thousands of vertices very quickly, but if you give it one by one, the render will become laggy regardless your graphic card performance.

The main advantage of glDrawArrays is you have to initialize your arrays once, and then draw it with one call. So first, you need to fill at the start of your program an array for each attribute. In your case: positions, colors and texture coords. It must be float arrays, something like this:

std::vector<float> vertices;
std::vector<float> colors;
std::vector<float> textureCoords;

for (int i = 0; i < iterator->second.size(); i += 3) {
    int t0 = iterator->second[i + 0];
    int t1 = iterator->second[i + 1];
    int t2 = iterator->second[i + 2];
    vertices.push_back(mesh.vertices[t0].x);
    vertices.push_back(mesh.vertices[t0].y);
    vertices.push_back(mesh.vertices[t0].z);
    vertices.push_back(mesh.vertices[t1].x);
    vertices.push_back(mesh.vertices[t1].y);
    vertices.push_back(mesh.vertices[t1].z);
    vertices.push_back(mesh.vertices[t2].x);
    vertices.push_back(mesh.vertices[t2].y);
    vertices.push_back(mesh.vertices[t2].z);

    // [...] Same for colors and texture coords.
}

Then, in another function set only for display, you can use these arrays in order to draw it:

// Enable everything you need
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);

// Set your used arrays
glVertexPointer(3, GL_FLOAT, 0, vertices.data());
glColorPointer(4, GL_FLOAT, 0, colors.data());
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords.data());

// Draw your mesh
glDrawArrays(GL_TRIANGLES, 0, size); // 'size' is the number of your vertices.

// Reset initial state
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);

Of course, you'll have to enable other attributes you want to use, like texture or blending.


NOTE:

If you wish to learn about performance, there are also other functions using indices in order to reduce the size of data used, like glDrawElements.

There are also other more advanced OpenGL techniques that allows you to increase performance by saving your data directly on the graphic card memory, like Vertex Buffer Objects.

like image 52
Aracthor Avatar answered Oct 19 '22 23:10

Aracthor