使用std::vector的VBOs

48

我用C++和OpenGL编写了一个模型加载器。我使用std::vector来存储我的顶点数据,但现在我想将其传递给glBufferData(),然而数据类型差别很大。我想知道是否有一种方法可以在std::vector和文档中记录的const GLvoid *(用于glBufferData())之间进行转换。

顶点类型

typedef struct
{
    float x, y, z;
    float nx, ny, nz;
    float u, v;
}
Vertex;

vector<Vertex> vertices;

glBufferData()调用

glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices, GL_STATIC_DRAW);

我得到了以下(预期的)错误:

error: cannot convert ‘std::vector<Vertex>’ to ‘const GLvoid*’ in argument passing

我如何将向量转换为与glBufferData()兼容的类型?

NB。 我暂时不关心正确的内存分配; vertices.size() * 3 * sizeof(float)很可能会导致段错误,但是我首先想解决类型错误。


@john 编辑了 奶酪般的、羞怯的微笑 - Bojangles
1
主要问题在于您的Vertex结构是否按照OpenGL的期望进行布局。我不清楚这一点,但是假设它是这样的,那么我认为您只需要在调用glBuggerData时将“vertices”替换为“&vectices[0]”。 - john
@john 我删掉了glBuggerData!无论如何,我用与其他示例非常类似的方式创建了我的"Vertex"结构体,因此我假设OpenGL对它很满意。 - Bojangles
@JamWaffles:不要忘记修复Marcelo指出的3 * sizeof(float)问题。你传递的不是每个条目都是3个浮点数的数组,而是每个条目都是一个“Vertex”对象的数组。这比3个浮点数大得多。 - Nicol Bolas
2个回答

62
如果您有一个std::vector<T> v,您可以使用表达式&v[0]获取指向连续数据开始处(这正是OpenGL所需要的)的T*
在您的情况下,这意味着将Vertex*传递给glBufferData函数:
glBufferData(
   GL_ARRAY_BUFFER,
   vertices.size() * sizeof(Vertex),
   &vertices[0],
   GL_STATIC_DRAW
);

或者像这样,它是一样的:

glBufferData(
   GL_ARRAY_BUFFER,
   vertices.size() * sizeof(Vertex),
   &vertices.front(),
   GL_STATIC_DRAW
);

在这里,您可以依赖从Vertex*void const*的隐式转换; 这不应该成为问题。

1
非常感谢解释,Tomalak。正如@Marcelo建议的那样,我将使用&vertices.front(),除非有不应该使用它的充分理由? - Bojangles
@JamWaffles:没有任何理由。 - Lightness Races in Orbit
3
这适用于向量,因为向量数据存储在 C 数组内部。但对于许多其他 STL 容器(例如列表、集合和映射),其中数据不是连续存储的,这种方法将无法使用。 - doron
2
@doron:vector 存储是否使用数组实现完全未指定。重要的是保证元素被连续存储。这基本上是相同的,但在意义上有微妙的差别。 (FYI,除了 std::array 之外,_没有_其他标准容器保证连续的元素存储) - Lightness Races in Orbit
1
@user673679:是的,那应该是调试模式下的范围检查,标准并不要求对&v[0]进行检查,而且在发布版本中你永远不应该看到这种情况。但是如果v.data()“导致崩溃”,那么你的stdlib实现就有严重问题了;它只有在你解引用它时才会崩溃!标准明确指出v.data()是一个有效的(但可能无法解引用的)指针,就像“end”迭代器一样。 - Lightness Races in Orbit
显示剩余5条评论

33
这个应该可以解决问题:
&vertices[0]

有些人喜欢使用&vertices.front(),但这样会更麻烦,而我很懒。

为了更加懒惰,你可以重载glBufferData如下:

template <class T>
inline void glBufferData(GLenum target, const vector<T>& v, GLenum usage) {
    glBufferData(target, v.size() * sizeof(T), &v[0], usage);
}

然后你可以写下以下内容:
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);

同时避免错误(您的结构体大小大于 3 * sizeof(float))。


2
谢谢Marcelo。&vertices.front()非常完美。我之前被困惑了,因为你没有将所有数据传递给函数,只传递了指向第一个元素的指针。现在问题已经解决了。 - Bojangles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接