如何将模型矩阵包含到VBO中?

时间:2014-07-08 08:47:07

标签: opengl opengl-es webgl vbo vertex-buffer

我想发送缓冲区列表(到GPU /顶点着色器),其中包含有关顶点位置,世界位置,颜色,比例和旋转的信息。

如果我的每个3D对象都有一个矩阵中的变换相关信息,我如何通过VBO将这个矩阵数组(除了其他顶点数据)传递给GPU?

更新 请原谅任何错别字:

// bind & set vertices.
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.vertexAtribPointer(a_Position, 3, gl.FLOAT, false, stride, 0);

// bind & set vertex normals.
gl.bindBuffer(gl.ARRAY_BUFFER,, vertexNormalsBuffer);
gl.vertexAttribPointer(a_Normal, 3, gl.FLOAT, false, stride, 0);

// becaue i cant pass in a model matrix via VBO, im tryng to pass in my world coordinates.
gl.bindBuffer(gl.ARRAY_BUFFER, worldPositionBuffer);

// not sure why i need to do this, but most tutorials i've read says to do this.
gl.bindBuffer(gl.ARRAY_BUFFER, null);

// bind & draw index buffer.
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.drawElements(gl.TRIANGLES, vertexIndexCount, gl.UNSIGNED_SHORT, 0);

请注意,这些缓冲区(vertexBuffervertexNormalsBufferworldPostiionBuffervertexIndexBuffer)是我场景中所有相应3D对象的连接(我正在渲染一个一个通过属性/制服 - 一种简单易懂的天真方法,但1000个对象的速度非常慢。

2 个答案:

答案 0 :(得分:7)

对于在渲染帧时需要经常更改的任何值,将它们作为attribute而不是uniform传递到着色器会更高效。如果您愿意,还可以将值存储在VBO中。请注意,它不是 required 在VBO中存储属性,也可以使用glVertexAttrib[1234]f()glVertexAttrib[1234]fv()指定。

这适用于转换矩阵,就像传入着色器的任何其他值一样。如果它经常变化,你应该把它变成一个属性。在这种情况下唯一的轻微皱纹是我们处理矩阵,属性必须是向量。但这很容易克服。通常作为mat4传入的内容可以由类型vec4的3个值表示,其中这3个向量是矩阵的列向量。它当然是4个向量来表示完全通用的4x4矩阵,但变换矩阵中的第4列不用于任何常见的变换类型(投影矩阵除外)。

如果您希望转换在VBO中,您可以设置另外3个属性,就像您对位置和颜色所做的那样。存储在VBO中的属性值是相应变换矩阵的列向量。

然后在顶点着色器中,通过计算变换属性向量与输入位置的点积来应用变换。代码可能如下所示:

attribute vec4 InPosition;
attribute vec4 XTransform;
attribute vec4 YTransform;
attribute vec4 ZTransform;
main() {
    vec3 eyePosition = vec3(
        dot(XTransform, InPosition),
        dot(YTransform, InPosition),
        dot(ZTransform, InPosition));
    ...
}

还有其他方法可以在完整的OpenGL中解决这个问题,比如使用统一缓冲区对象。但对于WebGL和OpenGL ES 2.0,我认为这是最好的解决方案。

答案 1 :(得分:-1)

你的方法是正确的,并且在某种程度上是不可避免的。如果你有1000个不同 非静态的对象,那么你需要(或者最好)进行1000次绘制调用。但是,如果您的对象是静态的,那么只要它们使用相同的材​​料就可以将它们合并在一起。

合并静态对象很简单。您可以通过乘以模型矩阵来修改顶点位置,以便将顶点转换为世界空间。然后,您可以在一次绘制调用中渲染批处理。

如果您有许多相同对象的实例但具有不同的模型矩阵(即不同的位置,方向或比例),那么您应该使用实例渲染。这将允许您在单个绘图调用中呈现所有实例。

最后,请注意,绘制调用不一定非常昂贵。会发生什么情况是状态更改会延迟,直到您发出绘制调用。例如,请考虑以下事项:

gl.drawElements(gl.TRIANGLES, vertexIndexCount, gl.UNSIGNED_SHORT, 0);
gl.drawElements(gl.TRIANGLES, vertexIndexCount, gl.UNSIGNED_SHORT, 0);

第二次绘制调用对CPU的负担要比第二次少(为自己尝试)。这是因为两个绘制调用之间没有状态变化。如果您只是在绘制调用之间更新模型矩阵统一变量,那么这不应该显着增加成本。通过着色器程序和材料对对象进行排序,可以(并推荐)最小化状态更改。