0

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

2255jj
  • 11
  • 3

1 Answers1

0

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));
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you for your answer. In my algorithm landmark(size and pos) is change and update new data very fast so i use for loop. How can I update and draw points in realtime? – 2255jj Feb 26 '20 at 09:25
  • @2255jj You can update the buffer and specify the vao in each frame, too. (in that case you don't need the matrix transformation). Generate `std::vector;` dynamically and use the code of my answer. Note if the number of points does not change, it is sufficient to change the content of `vbo_` by [`glBufferSubData`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glBufferSubData.xhtml) (per frame). – Rabbid76 Feb 26 '20 at 09:27
  • Update code but it said segmentation error. Whats problem? Please check. – 2255jj Feb 26 '20 at 10:10
  • @2255jj You missed `glBindVertexArray(vao_);` before `glDrawArrays`. If `vao_` and `vbo_` are just temporary, then you have to delete them by `glDeleteBuffers(1, &vbo_);` `glDeleteVertexArrays(1, &vao_);` – Rabbid76 Feb 26 '20 at 10:53
  • Thanks, it works but some problem. It draw unknown big red square and poins's color is all white.. And one more question, can I use glDrawarrays with glbegin()~ glvertex() ~ glend() together in same window? – 2255jj Feb 26 '20 at 23:46
  • sorry I dont know that. – 2255jj Feb 27 '20 at 07:44
  • Could you answer my new question please..? – 2255jj Feb 27 '20 at 07:58