在我的跨平台OpenGL应用程序中,我想使用顶点缓冲区对象进行绘制。 但是我遇到了调用glDrawRangeElements的问题。

glDrawRangeElements(GL_TRIANGLES, start, start + count, count, 
            GL_UNSIGNED_INT,  static_cast<GLvoid *>  (start * sizeof(unsigned int)));

编译器(Mac OS X上的CLang)不喜欢最后一个参数“错误:无法从类型'unsigned long'转换为指针类型'GLvoid *'(又名'void *')”。 OpenGL API将最后一个参数的类型定义为const GLvoid *,并且当此api与顶点数组一起使用时需要一个指针。 但是我知道当使用顶点缓冲区对象而不是指针时,应该将一个表示偏移量的整数值传递给缓冲区数据。 这就是我想要做的事情,因此我必须施展。 如何协调api要求与编译器进行严格的检查?

===============>>#1 票数:6

由于它是如此常用,人们经常使用宏来进行类型转换。 它可以这样定义:

#define BUFFER_OFFSET(i) ((char *)NULL + (i))

这是一种干净且安全的方式来进行转换,因为它不会对具有相同大小的整数和指针类型做出任何假设,而这些假设在64位系统上通常不会。

由于我个人更喜欢C ++样式转换,并且不使用NULL,我会这样定义:

#define BUFFER_OFFSET(idx) (static_cast<char*>(0) + (idx))

===============>>#2 票数:1

像这样:

reinterpret_cast <GLvoid *> (start * sizeof(unsigned int));

===============>>#3 票数: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是“单位大小”的C类型。 因此,关于强制转换的指针算法就像后端地址上的artihmetic一样。

简而言之,尝试使用预期结果做指针魔术是C语言方面的偏移是没有意义的,它只是不起作用。

让我们退一步,记住,我们实际上要做的是:最初的问题是,原始OpenGL顶点数组函数将指针作为其数据参数,但对于顶点缓冲区对象,我们实际上想要指定一个字节基于我们数据的偏移量,这是一个数字。 对于C编译器,该函数采用指针(我们学习的不透明的东西)。 相反,OpenGL定义的是利用编译器的工作方式。 大多数编译器将指针及其整数等价物实现为相同的二进制表示。 所以我们要做的是,它使编译器用我们的数字而不是指针来调用这些函数。

所以在技术上我们唯一需要做的就是告诉编译器“是的,我知道你认为这个变量a是一个整数,你是对的,而且函数glDrawElements只为它的数据参数取一个void* 。但是猜猜:该整数是从void* “产生的,通过将其转换为(void*)然后握住拇指,编译器实际上是如此愚蠢以传递整数值,因为它是函数。

所以这一切都归结为以某种方式绕过旧的函数签名。 转换指针是恕我直言的脏方法。 我做的有点不同:我搞乱了功能签名:

typedef void (*TFPTR_DrawElementsOffset)(GLenum,GLsizei,GLenum,uintptr_t);
TFPTR_DrawElementsOffset myglDrawElementsOffset =
    (TFPTR_DrawElementsOffset)glDrawElements;

现在你可以使用myglDrawElementsOffset而不做任何愚蠢的强制转换,并且offset参数将传递给函数,没有任何危险,编译器可能会搞乱它。 这也是我在程序中使用的方法。

===============>>#4 票数: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)); 

===============>>#5 票数:-2

您可以尝试这样调用它:

// 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教程

  ask by LRaiz translate from so

未解决问题?本站智能推荐: