我想发送缓冲区列表(到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);
请注意,这些缓冲区(vertexBuffer
,vertexNormalsBuffer
,worldPostiionBuffer
,vertexIndexBuffer
)是我场景中所有相应3D对象的连接(我正在渲染一个一个通过属性/制服 - 一种简单易懂的天真方法,但1000个对象的速度非常慢。
答案 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的负担要比第二次少(为自己尝试)。这是因为两个绘制调用之间没有状态变化。如果您只是在绘制调用之间更新模型矩阵统一变量,那么这不应该显着增加成本。通过着色器程序和材料对对象进行排序,可以(并推荐)最小化状态更改。