glDrawArrays 与 glDrawElements
好吧,我仍然在努力让它发挥作用。我的代码的重要部分是:
def __init__(self, vertices, normals, triangles):
self.bufferVertices = glGenBuffersARB(1)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferVertices)
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ADT.arrayByteCount(vertices), ADT.voidDataPointer(vertices), GL_STATIC_DRAW_ARB)
self.vertices = vertices
self.bufferNormals = glGenBuffersARB(1)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferNormals)
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ADT.arrayByteCount(normals), ADT.voidDataPointer(normals), GL_STATIC_DRAW_ARB)
self.normals = normals
self.bufferTriangles = glGenBuffersARB(1)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ADT.arrayByteCount(triangles), ADT.voidDataPointer(triangles), GL_STATIC_DRAW_ARB)
self.triangles = triangles
glDisableClientState(GL_VERTEX_ARRAY) **(Not sure if any of the following influence in any way)**
glDisableClientState(GL_NORMAL_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)
从我迄今为止读到的有关 VBO 的内容来看,我认为这里没有任何问题。现在我有了顶点、法线(尚未使用)和三角形索引缓冲区。现在进行实际绘制:
def draw(self, type):
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)
**Again above line not sure if they have any use.**
glEnableClientState(GL_VERTEX_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferVertices)
glVertexPointer(3, GL_FLOAT, 0, None)
glEnableClientState(GL_NORMAL_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferNormals)
glNormalPointer(GL_FLOAT, 0, None)
if type == GL_POINTS:
#glDrawArrays( GL_POINTS, 0, len(self.vertices) );
glDrawElements(type, len(self.vertices), GL_UNSIGNED_SHORT, 0)
else:
#glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)**(If I uncomment this doesnt seem to make any difference?!)**
#glDrawArrays( GL_TRIANGLES, 0, len(self.triangles) );
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)**(What does it draw now since GL_ELEMENT_ARRAY_BUFFER_ARB is binded to 0 ?!)**
现在 glDrawArrays 可以工作了。但在我必须绘制三角形的情况下,它不会绘制我在 bufferTriangles 中定义的三角形(从我读到的内容来看,这是正常的,因为 drawArrays 不使用索引?或者我在这里错了?)。问题是,如果我尝试使用 glDrawElements 一切都会崩溃:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000003150ebbc
Crashed Thread: 0
Thread 0 Crashed:
0 com.apple.GeForce8xxxGLDriver 0x1a3e7050 gldGetTextureLevel + 743600
1 com.apple.GeForce8xxxGLDriver 0x1a3e7563 gldGetTextureLevel + 744899
2 GLEngine 0x1a206eee gleDrawArraysOrElements_VBO_Exec + 1950
现在我在这里缺少什么?据我所知,我可能在某个地方传递了一个错误的指针?请注意,即使我尝试使用 glDrawElements(type, 24, GL_UNSIGNED_INT, 0) 它仍然会崩溃,即使定义的三角形数量要大得多,所以我认为它与大小没有任何关系。
问候, 博格丹
编辑: 好吧,现在我已经做了一些额外的检查,这是我目前的情况:我已将 len(triangles) 更改为 ADT.byteCount,但还没有解决方案。所以我检查了我获得的所有数据,如下所示: 顶点数组包含 ~60000 * 3 = 180000 个 GL_Float 类型的顶点条目,法线数组也是如此。由于只有 < 62535 个顶点我对三角形使用无符号短整型。所以我的 len(三角形) 是 ~135000。我还更改了 glDrawElements(GL_TRIANGLES, len(self.triangles), GL_UNSIGNED_SHORT, 0) 。我还检查了三角形数组中的所有数据都在 0 到 62534 之间,因为我我想也许是一些超出范围的指数滑入了低谷。这里还有什么问题吗? 哦,glDrawElements(GL_POINTS, ...) 是如何工作的?它还需要某种索引吗?
编辑2 我已经更新了上面的代码,正如那里所说,现在绘制元素绘制我的 GL_POINTS,但我不确定他从哪里获得索引?或者在 GL_POINTS 的情况下不需要它们?对于 GL_TRIANGLES,它的工作原理如下,带有 glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles) 注释,但是既然元素缓冲区绑定到 0,那么这里又需要什么样的索引?另一件事是 glDrawElements 不会绘制 glDrawArrays 绘制的所有点。为了更好地解释:
glDrawArrays( GL_POINTS, 0, len(self.vertices) );
这正确地绘制了我的所有点:
glDrawElements(type, len(self.vertices), GL_UNSIGNED_SHORT, 0)
这似乎明显比 glDrawArrays 绘制的点少得多。现在有趣的是,如果我传递像 10 * len(self.vertices) 这样的大小来绘制元素,它将绘制所有点(有些可能两次或更多;我可以检查这个吗?),但它不会崩溃吗?
问候
EDIT3
有关数组的一些更精确的信息:
vertices - 浮点数组,
len(vertices) = 180000 byteCount(vertices) = 720000 个
三角形 - numpy.uint16 的数组
len(triangles) = 353439 字节计数(三角形)= 706878 分钟(三角形)= 0 max(triangles) = 59999 ,所以它们应该指向有效的顶点
绘图完成:
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)
更新
好吧,就在我认为我得到的时候这应该如何工作,我尝试跳过元素的 VBO,然后直接:
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, ADT.voidDataPointer(self.triangles))
现在这不仅可以工作,还可以绘制我所有的三角形完美,但 FPS 更好。 VBO不是应该更快吗?什么可能导致上述方法有效,但以下方法崩溃:
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ADT.arrayByteCount(triangles), ADT.voidDataPointer(triangles), GL_STATIC_DRAW_ARB)
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)
Ok so I'm still struggling to get this to work. The important parts of my code are:
def __init__(self, vertices, normals, triangles):
self.bufferVertices = glGenBuffersARB(1)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferVertices)
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ADT.arrayByteCount(vertices), ADT.voidDataPointer(vertices), GL_STATIC_DRAW_ARB)
self.vertices = vertices
self.bufferNormals = glGenBuffersARB(1)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferNormals)
glBufferDataARB(GL_ARRAY_BUFFER_ARB, ADT.arrayByteCount(normals), ADT.voidDataPointer(normals), GL_STATIC_DRAW_ARB)
self.normals = normals
self.bufferTriangles = glGenBuffersARB(1)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ADT.arrayByteCount(triangles), ADT.voidDataPointer(triangles), GL_STATIC_DRAW_ARB)
self.triangles = triangles
glDisableClientState(GL_VERTEX_ARRAY) **(Not sure if any of the following influence in any way)**
glDisableClientState(GL_NORMAL_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)
I don't think there is anything wrong here from what I've read so far about VBO's. So now I have my vertex, normals(not used yet) and triangle indices buffers. Now for the actual draw:
def draw(self, type):
glDisableClientState(GL_VERTEX_ARRAY)
glDisableClientState(GL_NORMAL_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0)
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0)
**Again above line not sure if they have any use.**
glEnableClientState(GL_VERTEX_ARRAY)
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferVertices)
glVertexPointer(3, GL_FLOAT, 0, None)
glEnableClientState(GL_NORMAL_ARRAY);
glBindBufferARB(GL_ARRAY_BUFFER_ARB, self.bufferNormals)
glNormalPointer(GL_FLOAT, 0, None)
if type == GL_POINTS:
#glDrawArrays( GL_POINTS, 0, len(self.vertices) );
glDrawElements(type, len(self.vertices), GL_UNSIGNED_SHORT, 0)
else:
#glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)**(If I uncomment this doesnt seem to make any difference?!)**
#glDrawArrays( GL_TRIANGLES, 0, len(self.triangles) );
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)**(What does it draw now since GL_ELEMENT_ARRAY_BUFFER_ARB is binded to 0 ?!)**
Now the glDrawArrays works. But in the case where I have to draw my triangles it doesn't draw the triangles I have defined in bufferTriangles(this is normal from what I've read since drawArrays doesn't use indices ? Or am I wrong here? ). The problem is that if I try to use the glDrawElements everything crashes with:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x000000003150ebbc
Crashed Thread: 0
Thread 0 Crashed:
0 com.apple.GeForce8xxxGLDriver 0x1a3e7050 gldGetTextureLevel + 743600
1 com.apple.GeForce8xxxGLDriver 0x1a3e7563 gldGetTextureLevel + 744899
2 GLEngine 0x1a206eee gleDrawArraysOrElements_VBO_Exec + 1950
Now am what am I missing here? From what I can understand I'm probably passing a bad pointer somewhere? Note that even if I try to use glDrawElements(type, 24, GL_UNSIGNED_INT, 0) it still crashes even tho the number of triangles defined is way way larger so I don't think it has anything to do with the size.
Regards,
Bogdan
EDIT:
Ok so now I've done some extra checking and here is my current situation: I've changed the len(triangles) to ADT.byteCount, no solution yet. So I checked all the data I was getting and it's like this: The vertices array contains ~60000 * 3 = 180000 vertices entries of GL_Float type, as does the normals array. Since there are only < 62535 vertices I'm using unsigned short for the triangles. So I have len(triangles) is ~135000. I've also changed the glDrawElements(GL_TRIANGLES, len(self.triangles), GL_UNSIGNED_SHORT, 0) .I've also checked and all the data from the triangles array is between 0 and 62534, as I was thinking maybe some index that is out of range slipped trough. What else could be wrong here ?
Oh and how does glDrawElements(GL_POINTS, ...) work? Does it also need some kind of indices ?
EDIT2
I've updated the code above and as said there, now draw elements draws my GL_POINTS, but I'm not sure where does he gets indices? Or are they not needed in case of GL_POINTS ? And for the GL_TRIANGLES, it works like this , with glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles) commentated, but again what kind of indices does it take here now that the element buffer is binded to 0 ?! And another thing is that glDrawElements will not draw all the points that glDrawArrays does. To better explatin:
glDrawArrays( GL_POINTS, 0, len(self.vertices) );
This draws all my points correctly:
glDrawElements(type, len(self.vertices), GL_UNSIGNED_SHORT, 0)
This seems to visibly draw much fewer points than glDrawArrays. Now the funny thing is that if I pass as size something like 10 * len(self.vertices) to draw elements it will draw all the points(some maybe twice or more ; can I check this? ) but wouldnt it suppose to crash ?
Regards
EDIT3
Some more precise info about the arrays:
vertices - an array of floats,
len(vertices) = 180000
byteCount(vertices) = 720000
triangles - an array of numpy.uint16
len(triangles) = 353439
byteCount(triangles) = 706878
min(triangles) = 0
max(triangles) = 59999 , so they should be pointing to valid vertices
The drawing is done:
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)
UPDATE
Ok just when I tought I got how this should work, I tried to skip the VBO for the elements and went just:
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, ADT.voidDataPointer(self.triangles))
Now not only does this work and draws all my triangles perfectly, but the FPS is better. Shouldn't the VBO be faster? And what could cause the above approach to work but the following to crash:
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, self.bufferTriangles)
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ADT.arrayByteCount(triangles), ADT.voidDataPointer(triangles), GL_STATIC_DRAW_ARB)
glDrawElements(GL_TRIANGLES, len(self.triangles) , GL_UNSIGNED_SHORT, 0)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我没有使用 Python GL 的经验,但我想我发现了一些东西。您在调用
glDrawElements
中使用len(self.triangles)
,因此我认为这会为您提供三角形数组中的索引数。但为什么然后使用len(triangles)
作为glBufferData
中的大小,而不是像其他调用中那样使用ADT.arrayByteCount
。所以你的缓冲区太小了,因为它包含 len(triangles) 字节,尽管三角形包含无符号整数。如果三角形确实包含字节(我对此表示怀疑),则必须在glDrawElements
中使用GL_UNSIGNED_BYTE
。编辑:根据您的编辑,我得到了更多答案。当然,glDrawElements(GL_POINTS, ...) 也需要索引。它只是使用每个索引来绘制一个点,而不是每三个索引绘制一个三角形。只是对于点,您通常不需要
glDrawElements
,因为您无论如何都不会重用顶点,但您仍然需要它的索引。它不会神奇地在幕后变成一个glDrawArrays
调用。请记住,
vertices
数组包含浮点数,而glDrawArrays
绘制顶点,因此您必须绘制len(vertices)/3
顶点。请记住,元素是(单个顶点的)索引,而不是三角形,并且顶点是 3 个浮点(或您在glVertexPointer
中指定的内容),而不仅仅是一个。但是,如果您的 triangles 数组确实包含 3 个索引的元组(因此 len(triangles) 是三角形计数而不是索引计数),则必须绘制3*len(三角形) 元素(索引)。如果您的
vertices
数组包含向量而不仅仅是浮点数,则在 glDrawArrays 调用中绘制len(vertices)
顶点是正确的。因此,很高兴看到他们的声明是确定的。I have no experience with Python GL, but I think I spotted something. You use
len(self.triangles)
in the call toglDrawElements
, so I suppose that gives you the number of indices in the triangles array. But why then usinglen(triangles)
as size inglBufferData
and notADT.arrayByteCount
like in the other calls. So your buffer is just too small, as it containslen(triangles)
bytes, although triangles contains unsigned ints. If triangles really contains bytes (what I doubt) you would have to useGL_UNSIGNED_BYTE
inglDrawElements
.EDIT: According to your edits I got some more answers. Of course
glDrawElements(GL_POINTS, ...)
needs indices, too. It just uses every index to draw a point, instead of every three indices for a triangle. It's just that for points you often don't needglDrawElements
, as you don't reuse vertices anyway, but you still need indices for it. It doesn't magically become aglDrawArrays
call under the hood.And keep in mind, that the
vertices
array contains floats andglDrawArrays
draws vertices, so you have to drawlen(vertices)/3
vertices. Juts remember, an element is an index (of a single vertex), not a triangle and a vertex is 3 floats (or what you specified inglVertexPointer
), not just one.But if your
triangles
array really contains tuples of 3 indices (and thereforelen(triangles)
is the triangle count and not the index count) you would have to draw3*len(triangles)
elements (indices). and if yourvertices
array contains vectors and not just floats, then drawinglen(vertices)
vertices in the glDrawArrays call is correct. It would therefore be nice to see their declarations to be sure.根据我的经验,一旦您开始使用一些更高级的 OpenGL 调用,Python OpenGL 包装器就会出现很多错误。许多调用似乎会无缘无故地导致崩溃,有时如果您用不同调用的等效序列替换它们,有时会起作用...我将编程语言切换为 OpenGL,而不必处理这些问题。
In my experience, the Python OpenGL wrapper is very buggy once you start using some of the more advanced OpenGL calls. Many calls seem to cause a crash for no reason and sometimes work if you replace them with an equivalent sequence of different calls...I switched programming languages for OpenGL instead of having to deal with these issues.
不起作用的原因
起作用和
是因为 PyOpenGL 期望
None
作为 void 指针,而不是 0。使用用 C 编写的 OpenGL 示例时要小心,因为它们使用(void*)0
作为 void 指针,PyOpenGL 无法将其正确解释为指针,而是将 0 视为非 void 值。相反,您应该使用
(另请参阅https://stackoverflow.com/a/14365737/478380)
The reason why
works and
doesn't is because PyOpenGL expects
None
as the void pointer, rather than 0. Be careful when using OpenGL examples written in C, because they use(void*)0
as the void pointer, which isn't interpreted correctly as a pointer by PyOpenGL, which instead treats 0 as a non-void value.Instead, you should use
(See also https://stackoverflow.com/a/14365737/478380)