How to draw individual surfaces from entire object with reasonable performance?

Hello folks,

I have my Opengl Application which captures vertices from .obj file and to draw on the viewport.
I want to draw surfaces/lines/vertices separately but not at once based on the rendring mode from user with reasonable performance

What can I do to achieve my goal?

below are my drawing codes

void Draw() {
    vao.bind();

    glm::vec3 pos = camera.getPosition();
    glm::mat4 vp = camera.getProjection() * camera.getView();

    shader->bind();

    for (int i = 0; i < objects.size(); i++) {
        objectData = Objects[i];
        mesh = objectData.shape->getMesh();
	
        vbo = mesh.getVBO();
        ibo = mesh.getIBO();
	
        vao.bind(vbo, 0);
        vao.bind(ibo);
	
        shader->setUniform("u_albedoMap", material->getTexture("Albedo").getHandle());
        shader->setUniform("u_normalMap", material->getTexture("Normal").getHandle());
        shader->setUniform("u_roughnessMap", material->getTexture("Roughness").getHandle());
        shader->setUniform("u_metalnessMap", material->getTexture("Metalness").getHandle());
        shader->setUniform("u_displacementMap", material->getTexture("Displacement").getHandle());
        shader->setUniform("u_emissiveMap", material->getTexture("Emissive").getHandle());
	
        shader->setUniform("u_normalMatrix", glm::inverseTranspose(glm::mat3(objectData.matrix)));
        shader->setUniform("u_model", objectData.matrix);
        shader->setUniform("u_mvp", vp * objectData.matrix);
        shader->setUniform("u_viewPos", pos);
        shader->setUniform("u_objectIndex", i);

        glDrawElements(GL_TRIANGLES, ibo.Count(), GL_UNSIGNED_INT, nullptr);
    }
}

Ensure that each surface corresponds to a contiguous region of the index buffer then only draw the appropriate regions, either with multiple glDrawElements calls or a single glMultiDrawElements call.

You’ll need to sort the faces by surface as part of the import process. OBJ doesn’t require that faces are grouped by group/object/material within the file.

Can I ask you what’s the right way to use glMultiDrawElements?
I’ve searched for and tried with suggested but it doesn’t draw anything

Assume that we have 8xvertices, what should be the way?

[Experiment 1] : Working properly

glDrawElements(GL_TRIANGLES, ibo.Count(), GL_UNSIGNED_INT, nullptr);

[Experiment 2] : Nothing has drawn

for (int i = 0; i < indiceCount; i++) {
    GLvoid* starts[] = { (GLvoid*)indices[i], (GLvoid*)indices[i + 1], (GLvoid*)indices[i + 2] };
    glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, (const void**)starts);
}

[Experiment 3] : Nothing has drawn

GLvoid* starts[] = { (GLvoid*)0, (GLvoid*)1, (GLvoid*)2, (GLvoid*)0, (GLvoid*)2, (GLvoid*)3, (GLvoid*)4, (GLvoid*)5, (GLvoid*)6, (GLvoid*)4, (GLvoid*)6, (GLvoid*)7 };
GLsizei count[] = { 3, 3, 3, 3 };

glMultiDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (const void**)starts, 4);

glMultiDrawElements(mode, counts, type, indices, drawcount)

is essentially equivalent to:

for (int i=0; i<drawcount; i++)
    glDrawElements(mode, counts[i], type, starts[i]);

Note that when you’re using a portion of an element array, the pointer needs to be the byte offset into the array (cast to pointer type), not the starting index. So multiply the starting index by 4 for GL_UNSIGNED_INT.

float vertices[] = {
    // positions          // texture coords
    0.5f,  0.0f, 0.5f,   1.0f, 0.0f, // bottom right
    -0.5f, 0.0f, -0.5f,  0.0f, 1.0f, // top left
    -0.5f, 0.0f, 0.5f,   0.0f, 0.0f, // bottom left
    0.5f, 0.5f, -0.5f,   1.0f, 1.0f  // top right
};

.
.
.

unsigned int indices[] = {
    0, 1, 2, // First
    0, 3, 1  // Second
};

Glsizei counts[] = { 3, 3 };
int drawCount = 2;
for (int i = 0; i < drawCount; i++)
    glDrawElements(GL_TRIANGLES, counts[i], GL_UNSIGNED_INT, (const void**)indices[i * 3]);

Still above gives me an error :sob:

If you’re using a client-side array, this should be:

(const void*)&indices[i * 3]);

With a buffer bound to GL_ELEMENT_ARRAY_BUFFER, it’s

(const void*)(4*(i * 3)));

@GClements Thanks for kind support on this.

I’ve compared performance of each and found that second approach is much slower.

[1st Approach]

glDrawElements(GL_TRIANGLES, ibo->getCount(), GL_UNSIGNED_INT, nullptr);

[Result]


[2nd Approach]

for (int i = 0; i < indiceCount; i++) {
    glDrawElements(GL_TRIANGLES, count[i], GL_UNSIGNED_INT, (const void*)(4 * (i * 3)));
}

[Result]

Is there a way for me to improve overall performance?

What I would like to do is following based on the mode set by user

  • Object selection
  • Surface selection
  • Line selection
  • Point selection

Each draw call starts one triangle later than the one before. If counts[i]==3, you’re drawing one triangle at a time, which is slow. If counts[i]>3, you’re drawing the same triangle repeatedly, which is even slower.

You need to track where each surface starts and ends. You should have an array, e.g. GLuint starts[], such that starts[i+1]=starts[i]+counts[i], then use:

glDrawElements(GL_TRIANGLES, count[i], GL_UNSIGNED_INT, (const void*)(starts[i]));

Thanks,

I think that works but doesn’t really much improve performance for my case.
I found that Blender only utilize Memory but it doesn’t consumes CPU/GPU.

What is the technique here, as you can see there are complicated models in one single screen and I can split every single objects into surface/line/vertice mode with no performance issue.

I would like to know what can be the way to do such thing!