C++ “好奇”;无效*”;某些GL函数的参数

C++ “好奇”;无效*”;某些GL函数的参数,c++,pointers,opengl,casting,C++,Pointers,Opengl,Casting,对于OpenGL中的某些函数,必须指定一个字节偏移量,例如在glVertexAttribPointer()中指定步幅。一开始我会猜到它是一个像整数一样的正常数值。但经过检查,我意识到它需要铸造到void*(更具体地说是GLvoid*)。我的问题是:void*的预期含义是什么?为什么必须将其用于字节偏移量?它被称为void指针,它可以指向任何类型,就像char*一样-但它略有不同。首先你必须记住,指针只是一个数字,一个指向正确位置的地址,仅此而已。要使用由void指针指向的数据,必须显式地将其转换

对于OpenGL中的某些函数,必须指定一个字节偏移量,例如在
glVertexAttribPointer()
中指定步幅。一开始我会猜到它是一个像整数一样的正常数值。但经过检查,我意识到它需要铸造到
void*
(更具体地说是
GLvoid*
)。我的问题是:
void*
的预期含义是什么?为什么必须将其用于字节偏移量?

它被称为void指针,它可以指向任何类型,就像char*一样-但它略有不同。首先你必须记住,指针只是一个数字,一个指向正确位置的地址,仅此而已。要使用由void指针指向的数据,必须显式地将其转换为正确的类型,不能直接取消引用它们。它们允许您跳过类型检查,应该避免

我看到的另一个区别是意图的清晰。当您看到char*指针时,您无法确定它是否指向char/char数组。它可以是其他东西,它是高度情境化的:我看到一些案例,当它令人困惑时,有时是显而易见的;另一方面,空洞指针的意图是明确的:它可以是任何东西,并且必须考虑应用程序的上下文来查找类型。 另一件事是指针算法,在空指针的情况下,它不是由标准定义的

我猜OpenGL中的void指针是用来使API更通用的,给驱动程序更多的控制(通过设置,它可以更容易地将类型从float更改为double)。但这只是推测,需要确认。

glVertexAttribPointer()是顶点缓冲区对象之前的一个旧函数

在VBO之前,顶点数据将存储在客户端数组中,在绘制之前,需要将指向数据的指针传递给OpenGL

当VBO出现时,他们通过允许指针用于传递整数偏移量来重新调整此函数的用途

e、 g。
void*offset=(void*)offset of(vertexStructName,vertexMemberName)

一些OpenGL函数,例如采用依赖于上下文的
GLvoid*
参数。在旧的GL、顶点前缓冲区上,程序员会将整数索引数组直接传递给
GLDraweElements
,如下所示:

const GLuint indexes[] = { ... };
glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, indexes);
这被称为即时模式绘图

当引入顶点和索引缓冲区时,OpenGL体系结构委员会决定它们应该重用现有的接口,从而为最后一个无效的指针参数
GLDraweElements
GLVertexAttributePointer
和一些其他类似的函数赋予了新的上下文相关的含义

使用索引缓冲区时,渲染数据已经在GPU上,因此void pointer param是缓冲区中的偏移量。例如:要呈现的第一个索引。导致了
glpaurements
的新用法:

size_t firstIndex = ...
size_t indexDataSize = sizeof(GLuint);
glDrawElements(GL_TRIANGLES, numIndexes, GL_UNSIGNED_INT, reinterpret_cast<const GLvoid *>(firstIndex * indexDataSize));
const GLvoid*指针​参数是从顶点开始到给定元素的偏移量(以字节为单位)。同样,之所以保持这种状态,是因为该函数存在于顶点/索引缓冲区之前,并被重新调整用途以使用它们,而在即时模式中,您将传递一个顶点数组作为“指针”参数

因此,在过去,
glvertexattributepointer
的用法类似于:

const vec3 positions[] = { ... };
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, positions);
在现代德国劳埃德船级社中,您将使用:

struct Vert {
    vec3 position;
    vec3 normal;
};

size_t offset;

offset = offsetof(Vert, position);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));

offset = offsetof(Vert, normal);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));
struct Vert{
vec3位置;
vec3正常;
};
尺寸偏差;
偏移量=偏移量(垂直,位置);
glvertexattributepointer(0,3,GL_FLOAT,GL_FALSE,reinterpret_cast(偏移));
偏移量=偏移量(垂直,正常);
glvertexattributepointer(1,3,GL_FLOAT,GL_FALSE,reinterpret_cast(偏移));

Um,stride是一个
GLsizei
,它是一个
int
。另一个问题是为什么它需要是一个指针。void*与char*有何不同-应该是我的问题。我在-1中解释了血淋淋的细节:[unsigned]char*实际上被允许别名指向任何类型T的指针。是的,这是真的,我错过了。但是当你使用char时,你不知道它是用来指向char或者其他东西的。有了void,这个意图是明确的。我将在空闲时间编辑我的答案。强制转换为
void*
将完全删除该类型。您需要确切地知道允许您回溯到什么,否则您可能会违反别名规则。在这方面,转换为
void*
与转换为
char*
具有相同的“缺点”。如果你不想违反别名规则,你需要知道你来自什么类型,如果你想转换回一个允许的类型。是的,我从来没有说过别的。我会重复我自己的话:使用char*有解释的空间,指针下可以是char或其他东西,您不必强制进行转换。在void*场景中,情况有所不同-您必须强制转换才能使用数据。
struct Vert {
    vec3 position;
    vec3 normal;
};

size_t offset;

offset = offsetof(Vert, position);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));

offset = offsetof(Vert, normal);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, reinterpret_cast<const GLvoid *>(offset));