在我的跨平台OpenGL应用程序中,我想使用顶点缓冲区对象进行绘制。但是我遇到了调用glDrawRangeElements的问题。
glDrawRangeElements(GL_TRIANGLES, start, start + count, count,
GL_UNSIGNED_INT, static_cast<GLvoid *> (start * sizeof(unsigned int)));
编译器(Mac OS X上的CLang)不喜欢最后一个参数&#34;错误:无法转换类型&#39; unsigned long&#39;指针类型&#39; GLvoid *&#39; (又名&#39; void *&#39;)&#34;。 OpenGL API将最后一个参数的类型定义为const GLvoid *,并且当此api与顶点数组一起使用时需要一个指针。但是我知道当使用顶点缓冲区对象而不是指针时,应该将一个表示偏移量的整数值传递给缓冲区数据。这就是我想要做的事情,因此我必须施展。如何协调api要求与编译器进行严格的检查?
答案 0 :(得分:7)
由于它是如此常用,人们经常使用宏来进行类型转换。它可以这样定义:
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
这是一种干净且安全的方式来进行转换,因为它不会对具有相同大小的整数和指针类型做出任何假设,这在64位系统上通常不会。
由于我个人更喜欢C ++样式转换,并且不使用NULL,我会这样定义:
#define BUFFER_OFFSET(idx) (static_cast<char*>(0) + (idx))
答案 1 :(得分:1)
这是我的回答https://stackoverflow.com/a/8284829/524368的几乎逐字副本,稍作修改,以使其与此问题相符。
C定义(和C ++跟随它),指针可以转换为整数,即类型uintptr_t
,如果以这种方式获得的整数,将其转换回它来自的原始指针类型,产生原始指针。
然后有指针算术,这意味着如果我有两个指针指向所以同一个对象我可以区分它们,产生一个整数(类型为ptrdiff_t
),并添加了整数或减去任何一个原始指针,将产生另一个。它还定义了,通过向指针添加1,可以产生指向索引对象的下一个元素的指针。另外,同一对象的两个uintptr_t
除以sizeof(type pointed to)
指针的差值必须等于它们自己被减去的指针。最后但并非最不重要的是,uintptr_t
值可能是任何值。它们也可以是不透明的手柄。它们不需要是地址(尽管大多数实现都是这样做的,因为它是有意义的。)
现在我们可以看看臭名昭着的空指针。 C定义从类型uintptr_u
值0转换为指针的指针作为无效指针。 请注意,源代码中的此值始终为0 。在后端,在编译的程序中,用于实际将其表示到机器的二进制值可能完全不同!通常它不是,但它可能是。 C ++是相同的,但是C ++不允许隐式转换而不是C,因此必须明确地将0转换为void*
。另外,因为空指针不引用对象,因此没有解除引用的大小指针算法未定义为空指针。引用无对象的空指针也意味着,没有定义可以将它明智地转换为类型指针。
所以如果这一切都是未定义的,为什么这个宏毕竟会起作用?因为大多数实现(意味着编译器)都非常容易上当,而编译器编码器在最高程度上是懒惰的。大多数实现中指针的整数值只是指针本身在后端的值。所以空指针实际上是0.虽然没有检查空指针上的指针算法,但是如果指针得到某种类型的指定,大多数编译器都会默默地接受它,即使它没有意义。 char
是&#34;单位大小&#34;如果你想这样说的C类型。因此,关于强制转换的指针算法就像后端地址上的artihmetic一样。
总而言之,尝试做指针魔术并将预期结果作为C语言方面的偏移是没有意义的,它只是不会那样工作。
让我们退一步,记住,我们实际上要做的事情:最初的问题是,原始OpenGL顶点数组函数将指针作为其数据参数,但对于Vertex缓冲对象我们实际上想要在数据中指定基于字节的偏移量,这是一个数字。对于C编译器,该函数采用指针(我们学习的不透明的东西)。相反,OpenGL定义的是利用编译器的工作方式。大多数编译器将指针及其整数等价物实现为相同的二进制表示。所以我们必须做的是,它使编译器使用我们的数字而不是指针来调用这些函数。
从技术上讲,我们唯一需要做的就是告诉编译器&#34;是的,我知道你认为这个变量a
是一个整数,你是对的,那个函数glDrawElements
它只需void*
个数据参数。但是猜猜看:这个整数是从void*
&#34;产生的,通过将其转换为(void*)
然后握住拇指,编译器实际上是如此愚蠢以传递整数值,因为它是功能。
所以这一切都归结为以某种方式绕过旧的函数签名。转换指针是恕我直言的脏方法。我做的有点不同:我搞乱了功能签名:
typedef void (*TFPTR_DrawElementsOffset)(GLenum,GLsizei,GLenum,uintptr_t);
TFPTR_DrawElementsOffset myglDrawElementsOffset =
(TFPTR_DrawElementsOffset)glDrawElements;
现在你可以使用myglDrawElementsOffset
而不做任何愚蠢的转换,并且offset参数将被传递给函数,没有任何危险,编译器可能会搞乱它。这也是我在程序中使用的方法。
答案 2 :(得分:0)
像这样:
reinterpret_cast <GLvoid *> (start * sizeof(unsigned int));
答案 3 :(得分:0)
当我使用古代c风格的演员时,我使用CLang和c ++ 11进行编译。
glDrawRangeElements(GL_TRIANGLES, start, start + count, count, GL_UNSIGNED_INT,
(GLvoid *) (start * sizeof(unsigned int)));
我喜欢较少但被编译器接受的替代品
glDrawRangeElements(GL_TRIANGLES, start, start + count, count, GL_UNSIGNED_INT,
reinterpret_cast<GLvoid *>(static_cast<uintptr_t>(start * sizeof(unsigned int))));
glDrawRangeElements(GL_TRIANGLES, start, start + count, count, GL_UNSIGNED_INT,
(char *)(0) + start * sizeof(unsigned int));
答案 4 :(得分:-1)
您可以尝试这样称呼它:
// The first to last vertex is 0 to 3
// 6 indices will be used to render the 2 triangles. This make our quad.
// The last parameter is the start address in the IBO => zero
glDrawRangeElements(GL_TRIANGLES, 0, 3, 6, GL_UNSIGNED_SHORT, NULL);
请查看OpenGL tutorial。