How to draw points efficiently
Asked Answered
C

2

5

My program receives PCL pointcloud and plot each point one by one using:

glBegin(GL_POINTS);
glVertex3f(point.x, point.y, point].z);
glEnd();

It works but due to the large number of points the program is pretty slow. Is there a more efficient way to do this?

Curia answered 21/6, 2017 at 22:9 Comment(0)
C
12

Jam all the points into a big VBO when the point-cloud changes & draw 'em all in one go using a single glDrawArrays() call. That way OpenGL can shift all the vertex data to GPU once instead of you spoon-feeding the driver geometry one glVertex() at a time every frame.

Heck, even vertex arrays will buy you a huge speed-up by avoiding hundreds of thousands of function-calls into the GL driver.

EDIT: Comparison:

10 million random points, using vertex buffer objects:

vertex buffer object

Vertex arrays:

vertex array

Display lists:

display list

And using immediate-mode:

immediate-mode

Code (hit 'n' to cycle between drawing methods):

// http://glew.sourceforge.net/
#include <GL/glew.h>

// http://freeglut.sourceforge.net/
#include <GL/freeglut.h>

// http://glm.g-truc.net/
#include <glm/glm.hpp>
#include <glm/gtc/random.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <vector>
#include <sstream>
#include <chrono>
#include <cstddef>

struct Vertex
{
    glm::vec4 pos;
    glm::vec4 color;
};
std::vector< Vertex > verts;
GLuint vbo = 0;
GLuint dlist = 0;

void init()
{
    // init geometry
    for( size_t i = 0; i < 10000000; i++ )
    {
        Vertex vert;
        vert.pos = glm::vec4( glm::linearRand( glm::vec3( -1.0f, -1.0f, -1.0f ), glm::vec3( 1.0f, 1.0f, 1.0f ) ), 1.0f );
        vert.color = glm::vec4( glm::linearRand( glm::vec3( 0.00f, 0.0f, 0.0f ), glm::vec3( 1.0f, 1.0f, 1.0f ) ), 1.0f );
        verts.push_back( vert );
    }

    // create display list
    dlist = glGenLists( 1 );
    glNewList( dlist, GL_COMPILE );
    glBegin( GL_POINTS );
    for( size_t i = 0; i < verts.size(); ++i )
    {
        glColor4fv( glm::value_ptr( verts[i].color) );
        glVertex4fv( glm::value_ptr( verts[i].pos) );
    }
    glEnd();
    glEndList();

    // create VBO
    glGenBuffers( 1, &vbo );
    glBindBuffer( GL_ARRAY_BUFFER, vbo );
    glBufferData( GL_ARRAY_BUFFER, sizeof( Vertex ) * verts.size(), verts.data(), GL_STATIC_DRAW );
}

unsigned int method = 0;
void keyboard( unsigned char key, int x, int y )
{
    if( 'n' == key )
    {
        method++;
        if( method > 3 ) method = 0;
    }
}

void display()
{
    // timekeeping
    static std::chrono::steady_clock::time_point prv = std::chrono::steady_clock::now();
    std::chrono::steady_clock::time_point cur = std::chrono::steady_clock::now();
    const float dt = std::chrono::duration< float >( cur - prv ).count();
    prv = cur;

    glClearColor( 0, 0, 0, 1 );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    gluPerspective( 60.0, w / h, 0.1, 10.0 );

    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();
    gluLookAt( 2, 2, 2, 0, 0, 0, 0, 0, 1 );

    static float angle = 0.0f;
    angle += dt * 6.0f;
    glRotatef( angle, 0, 0, 1 );

    // render
    switch( method )
    {
    case 0:
        // VBO
        glBindBuffer( GL_ARRAY_BUFFER, vbo );
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_COLOR_ARRAY );
        glVertexPointer( 4, GL_FLOAT, sizeof( Vertex ), (void*)offsetof( Vertex, pos ) );
        glColorPointer( 4, GL_FLOAT, sizeof( Vertex ), (void*)offsetof( Vertex, color ) );
        glDrawArrays( GL_POINTS, 0, verts.size() );
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_COLOR_ARRAY );
        glBindBuffer( GL_ARRAY_BUFFER, 0 );
        break;

    case 1:
        // vertex array
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_COLOR_ARRAY );
        glVertexPointer( 4, GL_FLOAT, sizeof( Vertex ), &verts[0].pos );
        glColorPointer( 4, GL_FLOAT, sizeof( Vertex ), &verts[0].color );
        glDrawArrays( GL_POINTS, 0, verts.size() );
        glDisableClientState( GL_VERTEX_ARRAY );
        glDisableClientState( GL_COLOR_ARRAY );
        break;

    case 2:
        // display list
        glCallList( dlist );
        break;

    case 3:
        // immediate mode
        glBegin( GL_POINTS );
        for( size_t i = 0; i < verts.size(); ++i )
        {
            glColor4fv( glm::value_ptr( verts[i].color) );
            glVertex4fv( glm::value_ptr( verts[i].pos) );
        }
        glEnd();
        break;
    }

    // info/frame time output
    std::stringstream msg;
    msg << "Using ";
    switch( method )
    {
    case 0: msg << "vertex buffer object"; break;
    case 1: msg << "vertex array"; break;
    case 2: msg << "display list"; break;
    case 3: msg << "immediate mode"; break;
    }
    msg << std::endl;
    msg << "Frame time: " << (dt * 1000.0f) << " ms";
    glColor3ub( 255, 255, 0 );
    glWindowPos2i( 10, 25 );
    glutBitmapString( GLUT_BITMAP_9_BY_15, (unsigned const char*)( msg.str().c_str() ) );

    glutSwapBuffers();
}

int main(int argc, char **argv)
{
    glutInit( &argc, argv );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DOUBLE );
    glutInitWindowSize( 600, 600 );
    glutCreateWindow( "GLUT" );
    glewInit();
    init();
    glutDisplayFunc( display );
    glutKeyboardFunc( keyboard );
    glutIdleFunc( display );
    glutMainLoop();
    return 0;
}
Curium answered 21/6, 2017 at 22:25 Comment(3)
@genpfault: How can I read an external file ,say .csv of structure x1,y1,z1 (newline) x2,y2,z2... etc. directly to a VB ? Is there a way or do we always have to use a vector (like vert) in between. Can you provide some insightIslek
@Sid133: You can use glMapBuffer() to grab a writable pointer corresponding to the VBO storage & avoid the vector.Curium
Why display list is almost as efficient as VBO? This impressed me a lot.Prosy
O
0

Yes definitely, the code you are showing is from a quite old version of OpenGL. In more recent versions you can pack your data together and send it to the GPU in one call. The code becomes a little bit more complex but it is worth it. I suggest you to look at this website : https://learnopengl.com/ It gathers everything you need to start using modern opengl. Hope it helped.

Olivas answered 21/6, 2017 at 22:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.