用glDrawArrays和GL_LINE_STRIP从向量绘制线图

6
如何使用GL_LINE_STRIP绘制多条线,但不要在这些线之间绘制额外的线段,因为它会跳到下一个值?请参见图像。

enter image description here

现在红线是图表中线的实际值,但黄线是因为它完成了line1的值并继续到下一个值,但仍然在这些值之间画一条线。 我使用的代码如下:vector1包含所有线的值。
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);

所以我的问题是如何解决这个问题?因此,它循环遍历向量,并仅绘制线的值,而不是在完成第一条线的绘制后跳转时绘制。

你尝试过生成多个顶点数组,并将每个连续的线存储在单独的数组中吗? - Jon Cage
是的,我试过了,但我觉得我做错了什么,所以它没有起作用。如果您有时间,能否给个例子? - Kahin
为什么不使用GL_LINES而使用GL_LINE_STRIP呢?听起来你正在使用一种原始类型,这种类型并不适合你尝试渲染的内容,从而使事情变得比必要的复杂。 - Reto Koradi
2个回答

15

你有几种选项来绘制多个不相连的线条:

多次绘制调用

最简单且最直接的方法是进行多次绘制调用。比如说,如果你当前在缓冲区中有1000个顶点,在绘制调用中这些顶点被用于绘制:

glDrawArrays(GL_LINE_STRIP, 0, 1000);

现在,如果这实际上是由250个顶点组成的4个不相连的线段集合,那么您只需要分别绘制每个线段集合:

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

当然,缺点是需要多次绘制调用。只要这仅涉及适量的调用,并且每次调用仍产生大量的工作量,这就可以接受。出于性能原因,不希望有许多小的绘制调用。

glMultiDrawArrays()

有各种调用可让您使用单个API调用提交多个绘制调用。例如,这相当于上面的调用序列:

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

据我理解,你想要实现的目标,这个解决方案可能非常适合你。

GL_LINES 取代 GL_LINE_STRIP

如果你在绘制时使用 GL_LINES,每对点将会被一个单独的线连接,并且你可以很容易地留出空隙。

但是这样做也存在一个显著的缺点:你需要几乎两倍的顶点数才能完成绘制,因为大多数顶点都会被重复。如果原来有 n 个顶点:

a0 a1 a2 a3 ... an

你需要2 * n - 2个顶点来生成相同的几何形状:

a0 a1 a1 a2 a2 a3 a3 ... an

好处是您可以使用单个绘制调用来绘制所有内容,有需要时也可以添加任意数量的间隙。

原语重启

在OpenGL 3.1及更高版本中,有一个名为“原语重启”的功能,对于您描述的情况非常方便。但是,它需要使用索引数组。如果几何图形更复杂,通常会经常使用索引数组,因此这通常不是问题。但是,由于您没有使用索引数组,并且在您的用例中没有实际需要它,因此可能不值得引入索引数组仅用于使用原语重启。

我不会在此处包含代码示例,但是如果您查找“OpenGL原语重启”,应该能够找到大量文档和示例。


这就是每个人都应该写答案的方式!非常有启发性,我最终使用了GL_LINES而不是GL_LINE_STRIP。 - slaviboy

2
使用GL_LINE_STRIP(或三角形等效物GL_TRIANGLE_STRIP)绘制的线条就像将笔放在纸上,而不曾经将笔拿起一样。您不能不在两个连续点之间画线。
如果您真的想使用GL_LINE_STRIP来绘制您的图形(这是一个好主意),然后再绘制其他内容,您需要创建不同的缓冲区,并使用不同的参数多次调用glDrawArrays进行绘制。
因此,假设vector1存储红色线条,vector2存储黄色线条。初始化部分应如下所示:
// 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);

接下来是绘图部分:

glUseProgram(lineShader->getProgram());

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

glBindVertexArray(Vector2_VAObject);
glDrawArrays(GL_LINE_STRIP,0,vector2.size());

谢谢您的回答,我现在明白了,但是如何编写这些缓冲区而不使程序变慢并进行不必要的调用呢? - Kahin
@Kahin 我添加了一个示例。由于我没有你的所有代码,所以我无法确认它是否适用于你的情况,但是该解决方案应该至少看起来像这样。 - Aracthor
谢谢,我会尝试一下并告诉你它是否有效。 - Kahin
我现在创建了两个不同值的向量,并按照您的示例进行操作,它有点起作用,它绘制第一个向量,然后擦除第一个向量并绘制第二个向量,所以我永远看不到两个向量,它总是最终绘制第二个向量并保留那个。 - Kahin
@Kahin 两个连续的glDrawArrays不会互相抵消。我认为这个新错误来自于你代码中的一个部分,我没有拥有它。但无论如何,这是另一个问题,另一个问题 ;) - Aracthor
我只需要在每次绑定和绘制之前进行清理 :) - Kahin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接