Search code examples
c++openglglslglm-mathslam

opengl shader for drawing feature point in slam


I want to draw points using opengl shader. Now my code using glvertex3f(pos.x, pos.y, pos.z) but when too many point to draw using it, it slow. So I want to using shader and glDrawarrays. But its not work. please check my code.

original code :

for (const auto lm : landmarks) {
     const openvslam::Vec3_t pos_w = lm->get_pos_in_world();
     glColor3ub(lm->color_[0], lm->color_[1], lm->color_[2]);
     glVertex3f(pos_w.cast<float>().eval().x(),pos_w.cast<float>().eval().y(), pos_w.cast<float>().eval().z());
    }

my code :

for (const auto lm : landmarks) {

const openvslam::Vec3_t pos_w = lm->get_pos_in_world();
int buffer_size =  local_landmarks.size();

glGenBuffers(2, buffers_);

glBindBuffer(GL_ARRAY_BUFFER, buffers_[0]);
glm::vec3 pos_pt = glm::vec3(pos_w.cast<float>().eval().x(),pos_w.cast<float>().eval().y(), pos_w.cast<float>().eval().z());
glBufferData(GL_ARRAY_BUFFER, 3*buffer_size*sizeof(float), &pos_pt , GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);

glBindBuffer(GL_ARRAY_BUFFER, buffers_[1]);
glm::vec3 color_pt = glm::vec3(lm->color_[0], lm->color_[1], lm->color_[2]);
glBufferData(GL_ARRAY_BUFFER, buffer_size*3*sizeof(float), &color_pt, GL_DYNAMIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);

Eigen::Matrix4f mvp = s_cam_shader_opengl->GetProjectionModelViewMatrix();
//Eigen::Matrix4f mvp = s_cam_shader_opengl->GetProjectionMatrix() * s_cam_shader_opengl->GetModelViewMatrix();
glUniformMatrix4fv(mvp_location, 1, GL_FALSE, mvp.data());

glPointSize(1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDrawArrays(GL_POINTS, 0, 3*num);

}

vertex shader

#version 460

uniform mat4 mvpMat;

layout (location = 0) in vec3 test_position;
layout (location = 1) in vec3 test_color;

out vec3 colorr;

void main(void){
       colorr = test_color;
       gl_Position = vec4(test_position,1.0);
        }

fragment shader

#version 460

uniform mat4 mvpMat;


    in vec3 colorr;
    out vec4 frag_color;

    void main(void) {
        frag_color = vec4(colorr, 1.0);
    }

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

+edit

I update code but it said segmentation error. Whats problem?

struct TLandmarkData
{
    glm::vec3 pos;
    glm::vec3 color;
};
using TLandmarks = std::vector<TLandmarkData>;

TLandmarks landmarks_;


...
code
...

glUseProgram(points_program_);


while(){

...

for (const auto lm : landmarks) {
TLandmarkData aaa;

glm::vec3 pos_pt = glm::vec3(pos_w.cast<float>().eval().x(),pos_w.cast<float>().eval().y(), pos_w.cast<float>().eval().z());
glm::vec3 color_pt = glm::vec3(lm->color_[0], lm->color_[1], lm->color_[2]);
aaa.pos = pos_pt;
aaa.color = color_pt;

landmarks_.push_back(aaa);
}

...

GLuint vbo_;
GLuint vao_;

glGenBuffers(1, &vbo_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
glBufferData(GL_ARRAY_BUFFER, landmarks_.size()*sizeof(*landmarks_.data()), landmarks_.data(), GL_STATIC_DRAW);

glGenVertexArrays(1, &vao_);
glBindVertexArray(vao_);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TLandmarkData), 0);
glEnableVertexAttribArray( 0 );
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TLandmarkData), (void*)(sizeof(glm::vec3)));
glEnableVertexAttribArray(1);

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


glDrawArrays(GL_POINTS, 0, landmarks_.size());


}

vertex shader

#version 460

layout (location = 0) in vec3 test_position;
layout (location = 1) in vec3 test_color;

out vec3 colorr;

void main(void){
       colorr = test_color;
       gl_Position =   vec4(test_position,1.0);
        }

fragment shader

#version 460

in vec3 colorr;
out vec4 frag_color;

void main(void) {
    frag_color = vec4(colorr, 1.0);
}

+

enter image description here


Solution

  • What you actually do is to create landmarks.size() buffers rather than 1 buffer. You have to create one single buffer. For the best performance gain you have to create tha buffer once (respectively when it changes only) and to do the world transformation in the shader.

    Use the following data structure to represent a point (or a similar aggregate):

    struct TLandmarkData
    {
        glm::vec3 pos;
        glm::vec3 color;
    };
    using TLandmarks = std::vector<TLandmarkData>;
    

    Create a Vertex Array Object and a Vertex Buffer Object (once at initialization):
    (See also Vertex Specification)

    TLandmarks landmarks;
    GLuint vbo_;
    GLuint vao_;
    
    glGenBuffers(1, &vbo_);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_);
    glBufferData(GL_ARRAY_BUFFER, landmarks.size()*sizeof(*landmarks.data()), landmarks.data(), GL_STATIC_DRAW);
    
    glGenVertexArrays(1, &vao_);
    glBindVertexArray(vao_);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TLandmarkData), 0);
    glEnableVertexAttribArray( 0 );
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(TLandmarkData), (void*)(sizeof(glm::vec3)));
    glEnableVertexAttribArray(1);
    
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    

    As you can see, you don't need any loop. If the data changes, the buffer (vbo_) can be updated (e.g. glBufferSubData).

    When you want to draw the points, then it is sufficient to bind the vertex array object. The count argument to glDrawArrays has to be the number of vertices:

    glBindVertexArray(vao_);
    glDrawArrays(GL_POINTS, 0, landmarks.size());
    

    Use a Uniform of type mat4, to transform the points to world coordinates in the vertex shader:

    #version 460
    
    uniform mat4 mvpMat;
    
    layout (location = 0) in vec3 test_position;
    layout (location = 1) in vec3 test_color;
    
    layout (location=0) uniform mat4 worldtransform;
    
    out vec3 colorr;
    
    void main(void){
        colorr = test_color;
        gl_Position = worldtransform * vec4(test_position,1.0);
    }
    

    Set the uniform (update it per frame) by glUniformMatrix4fv after the program is installed by glUseProgram:

    glm::mat4 toworld(1.0f);
    // set toworld
    // [...]
    
    glUseProgram(myProgram);
    glUniformMatrix4fv(0, 1, GL_FALSE, glm::value_ptr(toworld));