Opengl 使用我不知道的元素';我不明白

Opengl 使用我不知道的元素';我不明白,opengl,Opengl,我正在研究一个开源项目的源代码,他们使用了一些我不理解的函数元素。作为一名程序员,我对GL API非常陌生,因此如果有人能告诉我这是如何工作的,我将不胜感激 让我们从绘图部分开始。代码如下所示: for (int i = 0; i < numObjs; i++) { glDrawElements(GL_TRIANGLES, vboIndexSize(i), GL_UNSIGNED_INT, (void*)(UPTR)vboIndexOffset(i)); } 有趣的是,它们将所

我正在研究一个开源项目的源代码,他们使用了一些我不理解的函数元素。作为一名程序员,我对GL API非常陌生,因此如果有人能告诉我这是如何工作的,我将不胜感激

让我们从绘图部分开始。代码如下所示:

for (int i = 0; i < numObjs; i++) {

    glDrawElements(GL_TRIANGLES, vboIndexSize(i), GL_UNSIGNED_INT, (void*)(UPTR)vboIndexOffset(i));
}
有趣的是,它们将所有内容都存储在GL_数组_缓冲区中。它们从不将顶点数据存储在GL_数组_缓冲区中,然后使用GL_元素_数组_缓冲区存储索引

但要回到绘制代码,他们首先要做的是声明顶点属性的常规工作。对于每个属性:

glBindBuffer(GL_ARRAY_BUFFER, glBuffer);
glEnableVertexAttribArray(loc);
glVertexAttribPointer(loc, size, type, GL_FALSE, stride, pointer);
这是有道理的,也是标准的。然后是我刚才提到的代码:

for (int i = 0; i < numObjs; i++) {

    glDrawElements(GL_TRIANGLES, vboIndexSize(i), GL_UNSIGNED_INT, (void*)(UPTR)vboIndexOffset(i));
}
它没有崩溃,但我看不到三角形

编辑:

添加似乎对我不起作用的代码(崩溃)

那么在绘画方面:

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);

// see stackoverflow.com/questions/8283714/what-is-the-result-of-null-int/
typedef void (*TFPTR_DrawElements)(GLenum, GLsizei, GLenum, uintptr_t);
TFPTR_DrawElements myGlDrawElements = (TFPTR_DrawElements)glDrawElements;

myGlDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, uintptr_t(4 * 3 * sizeof(float)));
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); // << THIS IS ACTUALLY NOT NECESSARY

// VVVV THIS WILL MAKE IT WORK VVVV

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// see stackoverflow.com/questions/8283714/what-is-the-result-of-null-int/
typedef void (*TFPTR_DrawElements)(GLenum, GLsizei, GLenum, uintptr_t);
TFPTR_DrawElements myGlDrawElements = (TFPTR_DrawElements)glDrawElements;

myGlDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, uintptr_t(4 * 3 * sizeof(float)));
这会使应用程序崩溃


有关解决方案,请参见下面的答案

这是由于OpenGL重新使用了固定函数管道调用。绑定
GL\u数组\u缓冲区时
VBO,对
glvertexattributepointer
的后续调用需要VBO中有一个偏移量(以字节为单位),然后将其转换为
(void*)
GL_数组_缓冲区
绑定在绑定另一个缓冲区之前保持有效,就像
GL_元素_数组_缓冲区
绑定在绑定另一个“索引”缓冲区之前保持有效一样

可以使用一个函数封装缓冲区绑定和属性指针(偏移)状态。
示例中的地址无效。使用:
(void*)n
转换偏移,谢谢您的回答。我认为(在网上做了一些研究之后)

  • 首先,您应该使用
    glGenVertexArray
    。这似乎是OpenGL4.x现在的标准,因此在绘制几何体之前,与其调用GLVERTEXAttributePointer,不如在将数据推送到GPU缓冲区时创建VAO

  • 我(实际上)能够将顶点数据和索引组合在同一个缓冲区(GL_数组_缓冲区)中,然后使用gldrawerelements绘制原语(见下文)。无论如何,标准的方法是将顶点数据推送到GL_数组_缓冲区,并将索引分别推送到GL_元素数组_缓冲区。因此,如果这是标准的方法,那么最好不要太聪明,只使用这些功能

例如:

glGenBuffers(1, &vbo);
// push the data using GL_ARRAY_BUFFER
glGenBuffers(1, &vio);
// push the indices using GL_ELEMENT_ARRAY_BUFFER
...
glGenVertexArrays(1, &vao);
// do calls to glVertexAttribPointer
...
如果我错了,请纠正我,但这似乎是正确的(也是唯一的)方法

编辑: 但是,实际上,只要在调用GLD元素之前先调用glBindBuffer(GLU元素\数组\缓冲区,vbo),就可以将顶点数据和索引一起“打包”到数组\缓冲区中

工作代码(与原岗位代码比较):

那么在绘画方面:

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);

// see stackoverflow.com/questions/8283714/what-is-the-result-of-null-int/
typedef void (*TFPTR_DrawElements)(GLenum, GLsizei, GLenum, uintptr_t);
TFPTR_DrawElements myGlDrawElements = (TFPTR_DrawElements)glDrawElements;

myGlDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, uintptr_t(4 * 3 * sizeof(float)));
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); // << THIS IS ACTUALLY NOT NECESSARY

// VVVV THIS WILL MAKE IT WORK VVVV

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// see stackoverflow.com/questions/8283714/what-is-the-result-of-null-int/
typedef void (*TFPTR_DrawElements)(GLenum, GLsizei, GLenum, uintptr_t);
TFPTR_DrawElements myGlDrawElements = (TFPTR_DrawElements)glDrawElements;

myGlDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, uintptr_t(4 * 3 * sizeof(float)));
glBindVertexArray(vao);

glBindBuffer(GL_数组_缓冲区,vbo);//谢谢你的回答。如何将int转换为void指针?我不明白?你能解释一下吗?另外,我知道我的问题还不清楚,但我想知道的是,使用VBO存储顶点数据和索引是否有效?指针实际上只是一个表示内存地址的整数。除了大小差异(即不同机器上的指针为32或64位),它们之间的转换没有技术问题;这是很危险的,因为你不想意外地去引用一些实际上不是有效内存地址的东西。是的,这很有意义,只是很奇怪。如果这是一个偏移量,那么为什么不传递一个int。如果它需要一个指向变量的指针,那么为什么不传递呢。无论如何,我就是无法让它工作,我仍然想知道使用GL_ARRAY_BUFFER来存储顶点和索引数据是否合适。谢谢。@Wyzard:不幸的是,事情并没有那么简单。从技术上讲,这种将某个整数转换为指针的方法可能会产生未定义的行为。干净的解决方案是将函数签名强制转换为接受uintptr\t作为数据参数的签名。请参见
glpaurements
的签名定义在缓冲区对象出现之前;最初,您将传递一个实际的指针,指向。当引入设备端缓冲区时,通过在地址参数中插入缓冲区偏移量,该函数也得到了扩展以支持它们。我已经在这里进行了深入解释:@datenwold,谢谢,我使用了您的解决方案。不过,我的问题更多的是关于如何将顶点数据和索引打包到数组_缓冲区中以便工作。这导致我的程序现在崩溃。使用OpenGL核心配置文件时需要使用VAOs。将顶点数据和索引放在同一个缓冲区中是可能的,而且效果很好,但我认为这不是很常见的做法。所以对顶点数据和索引使用单独的缓冲区基本上是标准的,绝对没有问题。谢谢。不过我还没能去上班。是的,我同意如果有另一种方法(可能更标准),为什么还要麻烦呢?但我希望它能起作用,以证明它确实有效。我认为将所有数据放在同一个内存位置会很有趣。如果你有一个让它工作的例子,我想看看这个。它一直在为我崩溃。我已经添加了在原始问题中崩溃的代码。实际发现了问题并添加了解决方案。感谢大家的贡献。更好的方法是在设置VAO时调用
glBindBuffer(GL\u ELEMENT\u ARRAY\u BUFFER,…)
,其中包含
glvertexattributepointer()
和其他类似调用。元素数组缓冲区绑定是VAO状态的一部分。如果执行此操作,则在绘图代码中只需要
glBindVertexArray()
glGenBuffers(1, &vbo);
// push the data using GL_ARRAY_BUFFER
glGenBuffers(1, &vio);
// push the indices using GL_ELEMENT_ARRAY_BUFFER
...
glGenVertexArrays(1, &vao);
// do calls to glVertexAttribPointer
...
float vertices[] = {
    0,  1, 0, // Vertex 1 (X, Y)
    2, -1, 0, // Vertex 2 (X, Y)
   -1, -1, 0, // Vertex 3 (X, Y)
    3,  1, 0,
};

U8 *ptr = (U8*)malloc(4 * 3 * sizeof(float) + 6 * sizeof(unsigned int));
memcpy(ptr, vertices, 4 * 3 * sizeof(float));
unsigned int indices[6] = { 0, 1, 2, 0, 3, 1 };
memcpy(ptr + 4 * 3 * sizeof(float), indices, 6 * sizeof(unsigned int));

glGenBuffers(1, &vbo);

glBindBuffer(GL_ARRAY_BUFFER, vbo);

glBufferData(GL_ARRAY_BUFFER, 4 * 3 * sizeof(float) + 6 * sizeof(unsigned int), ptr, GL_STATIC_DRAW);

glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);
glEnableVertexAttribArray(0);

free(ptr);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo); // << THIS IS ACTUALLY NOT NECESSARY

// VVVV THIS WILL MAKE IT WORK VVVV

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo);

// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

// see stackoverflow.com/questions/8283714/what-is-the-result-of-null-int/
typedef void (*TFPTR_DrawElements)(GLenum, GLsizei, GLenum, uintptr_t);
TFPTR_DrawElements myGlDrawElements = (TFPTR_DrawElements)glDrawElements;

myGlDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, uintptr_t(4 * 3 * sizeof(float)));