创建异构顶点数据数组的可移植方法

Portable way to create heterogenous vertex data array

本文关键字:可移植 方法 数组 数据 异构 顶点 创建      更新时间:2023-10-16

在图形编程中,使用顶点格式是很常见的。 例如,此处对此进行了描述。

但是,我正在寻找一种方法来完成不调用未定义行为的方法 (我主要寻找C++信息,但 C 也可以(。

常见的方法是这样的:首先,将顶点格式声明为结构。

struct Vertex {
float x;
float y;
uint16_t someData;
float etc;
};

然后,您创建一个数组,填写它们,并将它们发送到您的图形 API(例如:OpenGL(。

Vertex myVerts[100];
myVerts[0].x = 42.0;
// etc.
// when done, send the data along:
graphicsApi_CreateVertexBuffer(&myVerts[0], ...);

(旁白:我跳过了你告诉 API 格式是什么的部分;我们只是假设它知道(。

但是,图形 API 不了解您的结构。它只需要内存中的一系列值,如下所示:

|<---  first vertex   -->||<---  second vertex  -->| ...
[float][float][u16][float][float][float][u16][float] ...

由于打包和对齐的问题,无法保证myVerts将在内存中以这种方式布局。

当然,大量的代码都是以这种方式编写的,尽管它不是可移植的,但它可以工作。

但是有没有任何便携式方法可以做到这一点,也不是

1. Inefficient
2. Awkward to write

这基本上是一个序列化问题。另请参阅:将缓冲区解释为结构的正确、可移植的方法

我知道的主要符合标准的方式是将您的内存分配为char[]. 然后,您只需完全按照您希望的布局方式填写所有字节。

但是要从上面的struct Vertex表示转换为char[]表示,需要一个额外的副本(以及一个缓慢的逐字节副本(。所以这是低效的。

或者,您可以直接将数据写入char[]表示形式,但这非常尴尬。说verts[5].x = 3.0f比寻址到字节数组、将浮点数写入 4 个字节等要好得多。

有没有一种好的便携式方法来做到这一点?

但是,图形 API 不了解您的结构。它只需要内存中的一系列值,如下所示:

|<---  first vertex   -->||<---  second vertex  -->| ...
[float][float][u16][float][float][float][u16][float] ...

这不是真的。图形 API 具有有关结构的知识,因为您告诉它有关结构的信息。你甚至已经知道这一点:

(旁白:我跳过了你告诉 API 格式是什么的部分;我们只是假设它知道(。

当您告诉图形 API 每个字段在结构中的位置时,应使用sizeofoffsetof,而不是猜测结构的布局。然后,即使编译器插入填充,您的代码也将正常工作。例如(在OpenGL中(:

struct Vertex {
float position[2];
uint16_t someData;
float etc;
};
glVertexAttribPointer(position_index, 2, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*)offsetof(struct Vertex, position));
glVertexAttribIPointer(someData_index, 1, GL_SHORT, sizeof(struct Vertex), (void*)offsetof(struct Vertex, someData));
glVertexAttribPointer(etc_index, 1, GL_FLOAT, GL_FALSE, sizeof(struct Vertex), (void*)offsetof(struct Vertex, etc));

glVertexAttribPointer(position_index, 2, GL_FLOAT, GL_FALSE, 14, (void*)0);
glVertexAttribIPointer(someData_index, 1, GL_SHORT, 14, (void*)8);
glVertexAttribPointer(etc_index, 1, GL_FLOAT, GL_FALSE, 14, (void*)10);

当然,如果您将磁盘中的顶点读取为已知格式的字节 blob(可能与编译器的结构布局不同(,则可以使用硬编码布局来解释这些字节。如果要将顶点视为结构数组,请使用编译器为您决定的布局。