Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Line graph with glDrawArrays and GL_LINE_STRIP from vector

How can I draw many lines with GL_LINE_STRIP, but not to draw a extra line between these lines, because it jumps to next value? See image

enter image description here

Now the red lines are the actual values for the lines in the graph but the yellow ones are because it finished the values for line1 and goes on to next but still draw a line between these values.

The code I'm using is like this: vector1 is containing all the line values.

glGenVertexArrays(1,&Vector1_VAObject);
glGenBuffers(1,&Vector1_VBObject);

glBindVertexArray(Vector1_VAObject);
glBindBuffer(GL_ARRAY_BUFFER, Vector1_VBObject);

glBufferData(GL_ARRAY_BUFFER,vector1.size()*sizeof(GLfloat), &vector1[0] ,GL_STATIC_DRAW);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE, 3*sizeof(GLfloat),(GLvoid*)0);
//Clean
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);

glUseProgram(lineShader->getProgram());
glBindVertexArray(Vector1_VAObject);

glDrawArrays(GL_LINE_STRIP,0,vector1.size());


glBindVertexArray(0);

So my question is how can I fix this problem? So it loops through the vector and only draws the values for the lines and not when its is jumps after finish drawing first line..

like image 222
Kahin Avatar asked Jul 30 '15 12:07

Kahin


2 Answers

You have a few options to draw multiple disconnected line strips:

Multiple Draw Calls

The easiest and most direct approach is that you make multiple draw calls. Let's say you currently have 1000 vertices in your buffer, and make a draw call that ends up as:

glDrawArrays(GL_LINE_STRIP, 0, 1000);

Now, if this is actually 4 disconnected sets of lines with 250 vertices each, you simply draw each set of lines separately:

glDrawArrays(GL_LINE_STRIP, 0, 250);
glDrawArrays(GL_LINE_STRIP, 250, 250);
glDrawArrays(GL_LINE_STRIP, 500, 250);
glDrawArrays(GL_LINE_STRIP, 750, 250);

The downside is of course that you need multiple draw calls. As long as this is just a moderate number of calls, and each call is still generating a substantial amount of work, this should be fine. For performance reasons, it's undesirable to have many small draw calls.

glMultiDrawArrays()

There are various calls that allow you to essentially submit multiple draw calls with a single API call. For example, this is equivalent to the call sequence above:

GLint firstA[4] = {0, 250, 500, 750};
GLint countA[4] = {250, 250, 250, 250};
glMultiDrawArrays(GL_LINE_STRIP, firstA, countA, 4);

The way I understand what you're trying to do, this solution might be ideal for you.

GL_LINES instead of GL_LINE_STRIP

If you use GL_LINES for your draw calls, each pair of points will be connected by a separate line, and you can easily have gaps.

There is a significant penalty, though: You need almost twice as many vertices in your buffer, since most vertices will be repeated. Where you previously had n vertices:

a0 a1 a2 a3 ... an

you need 2 * n - 2 vertices for the same geometry:

a0 a1 a1 a2 a2 a3 a3 ... an

The benefit is that you can draw everything with a single draw call, with as many gaps as you need.

Primitive Restart

In OpenGL 3.1 and later, there is a feature called "primitive restart" that can be very convenient for cases like the one you describe. However, it requires using an index array. Index arrays are often used anyway if the geometry is more complex, so that's normally not an obstacle. But since you're not using an index array, and there's no real need for it in your use case, it's probably not worth introducing index arrays just for using primitive restart.

I'm not going to include a code example here, but you should be able to find plenty of documentation and examples if you look for "OpenGL primitive restart".

like image 136
Reto Koradi Avatar answered Nov 04 '22 02:11

Reto Koradi


A draw with GL_LINE_STRIP (or triangle equivalent GL_TRIANGLE_STRIP) is like to put a pen on a paper and draw something without ever take the pen off. You cannot to not draw an line between two consecutive points.

If you really want to use GL_LINE_STRIP to draw your graph (which is a good idea) and then something else, you'll have to make different buffers and draw many times glDrawArrays, with different parameters.

So let's assume vector1 store red lines and vector2 the yellow one. The initialization part would looks like this :

// vector1 store
glGenVertexArrays(1,&Vector1_VAObject);
glGenBuffers(1,&Vector1_VBObject);

glBindVertexArray(Vector1_VAObject);
glBindBuffer(GL_ARRAY_BUFFER, Vector1_VBObject);

glBufferData(GL_ARRAY_BUFFER,vector1.size()*sizeof(GLfloat), &vector1[0] ,GL_STATIC_DRAW);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE, 3*sizeof(GLfloat),(GLvoid*)0);

// vector2 store
glGenVertexArrays(1,&Vector2_VAObject);
glGenBuffers(1,&Vector2_VBObject);

glBindVertexArray(Vector2_VAObject);
glBindBuffer(GL_ARRAY_BUFFER, Vector2_VBObject);

glBufferData(GL_ARRAY_BUFFER,vector2.size()*sizeof(GLfloat), &vector2[0] ,GL_STATIC_DRAW);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE, 3*sizeof(GLfloat),(GLvoid*)0);

//Clean
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArray(0);

And then the draw part :

glUseProgram(lineShader->getProgram());

glBindVertexArray(Vector1_VAObject);
glDrawArrays(GL_LINE_STRIP,0,vector1.size());

glBindVertexArray(Vector2_VAObject);
glDrawArrays(GL_LINE_STRIP,0,vector2.size());
like image 2
Aracthor Avatar answered Nov 04 '22 04:11

Aracthor