OpenGL:批处理渲染器:转换应该发生在 CPU 还是 GPU 上?
OpenGL: Batch Renderer: Should Transformations Take place on the CPU or GPU?
我正在开发一个2D游戏引擎,以后会支持3D。在当前的开发阶段,我正在处理批处理渲染器。正如你们中的一些人可能知道的那样,当将图形批处理在一起时,统一支持颜色 (RGBA)、纹理坐标、纹理 ID(纹理索引)和模型变换矩阵超出 window,而是通过顶点传递缓冲。现在,我已经实现了将模型的位置、颜色、纹理坐标和纹理 ID 传递到顶点缓冲区。我的顶点缓冲区格式现在看起来像这样:
float* v0 = {x, y, r, g, b, a, u, v, textureID};
float* v1 = {x, y, r, g, b, a, u, v, textureID};
float* v2 = {x, y, r, g, b, a, u, v, textureID};
float* v3 = {x, y, r, g, b, a, u, v, textureID};
我即将使用变换矩阵整合计算对象在世界中的位置 space。这让我问这个问题:
变换矩阵应该乘以CPU或GPU上的模型顶点位置吗?
要记住的一点是,如果我将它传递给顶点缓冲区,我将不得不为每个顶点上传一次变换矩阵(每个精灵 4 次),这对我来说似乎是一种内存浪费。另一方面,在 CPU 上将模型顶点位置乘以变换矩阵似乎与 GPU 的并发能力相比会更慢。
如果我在 GPU 上计算变换,我的顶点缓冲区格式将是这样的:
float* v0 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v1 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v2 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v3 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
这个问题主要是理论上的驱动。因此,将不胜感激理论上和技术上的答案。但作为参考,这里是代码。
Should Transformations Take place on the CPU or GPU?
这真的取决于手头的情况。如果您每帧都重新提交顶点,最好对最适合您的情况进行基准测试。如果您想在不重新提交所有顶点的情况下制作动画,您别无选择,只能在 GPU 上应用它。
无论出于何种原因,如果您决定在 GPU 上应用变换,除了为每个顶点复制矩阵之外,还有更好的方法可以做到这一点。我宁愿将变换矩阵放在 SSBO:
layout(std430, binding=0) buffer Models {
mat4 MV[]; // model-view matrices
};
并在 VAO 的每个顶点中存储一个索引:
struct Vert {
float x, y, r, g, b, a, u, v;
int textureID, model;
};
顶点着色器可以根据索引属性获取完整矩阵:
layout(location = 0) in vec4 in_pos;
layout(location = 1) in int in_model;
void main() {
gl_Position = MV[in_model] * in_pos;
}
您甚至可以将它与其他每个对象的属性结合起来,例如 textureID
。
编辑: 您可以通过实例化和多重绘制实现类似的效果。虽然它可能会更慢。
我不确定您的引擎代码实际是什么样子,但我认为它看起来像任何其他 OpenGL 程序。
如果是这样,根据我的经验,变换矩阵通常应该传递给顶点着色器,并在绘制场景时在 GPU 上应用给定的顶点信息。例如:
//MVP matrix
GLuint MatrixID = glGetUniformLocation(shaderProgID, "MVP");
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0][0]);
但是如果你想找到特定组的所有顶点的世界坐标,在渲染函数之外,你可能需要在 CPU 上进行,否则你将需要使用一些并行的OpenCL 等编程技术在 GPU 上完成工作。
最重要的是,为什么你特别想要绘图程序之外的世界坐标信息?如果您只是想找到模型的世界坐标,您可以简单地为场景中的每个模型设置一个中心坐标,并且只跟踪单个坐标而不是整个网格组。
顶点信息应始终位于模型坐标中并存储在无接触的顶点缓冲区中,除非您想对其应用一些修改。
我正在开发一个2D游戏引擎,以后会支持3D。在当前的开发阶段,我正在处理批处理渲染器。正如你们中的一些人可能知道的那样,当将图形批处理在一起时,统一支持颜色 (RGBA)、纹理坐标、纹理 ID(纹理索引)和模型变换矩阵超出 window,而是通过顶点传递缓冲。现在,我已经实现了将模型的位置、颜色、纹理坐标和纹理 ID 传递到顶点缓冲区。我的顶点缓冲区格式现在看起来像这样:
float* v0 = {x, y, r, g, b, a, u, v, textureID};
float* v1 = {x, y, r, g, b, a, u, v, textureID};
float* v2 = {x, y, r, g, b, a, u, v, textureID};
float* v3 = {x, y, r, g, b, a, u, v, textureID};
我即将使用变换矩阵整合计算对象在世界中的位置 space。这让我问这个问题:
变换矩阵应该乘以CPU或GPU上的模型顶点位置吗?
要记住的一点是,如果我将它传递给顶点缓冲区,我将不得不为每个顶点上传一次变换矩阵(每个精灵 4 次),这对我来说似乎是一种内存浪费。另一方面,在 CPU 上将模型顶点位置乘以变换矩阵似乎与 GPU 的并发能力相比会更慢。
如果我在 GPU 上计算变换,我的顶点缓冲区格式将是这样的:
float* v0 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v1 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v2 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
float* v3 = {x, y, r, g, b, a, u, v, textureID, m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15};
这个问题主要是理论上的驱动。因此,将不胜感激理论上和技术上的答案。但作为参考,这里是代码。
Should Transformations Take place on the CPU or GPU?
这真的取决于手头的情况。如果您每帧都重新提交顶点,最好对最适合您的情况进行基准测试。如果您想在不重新提交所有顶点的情况下制作动画,您别无选择,只能在 GPU 上应用它。
无论出于何种原因,如果您决定在 GPU 上应用变换,除了为每个顶点复制矩阵之外,还有更好的方法可以做到这一点。我宁愿将变换矩阵放在 SSBO:
layout(std430, binding=0) buffer Models {
mat4 MV[]; // model-view matrices
};
并在 VAO 的每个顶点中存储一个索引:
struct Vert {
float x, y, r, g, b, a, u, v;
int textureID, model;
};
顶点着色器可以根据索引属性获取完整矩阵:
layout(location = 0) in vec4 in_pos;
layout(location = 1) in int in_model;
void main() {
gl_Position = MV[in_model] * in_pos;
}
您甚至可以将它与其他每个对象的属性结合起来,例如 textureID
。
编辑: 您可以通过实例化和多重绘制实现类似的效果。虽然它可能会更慢。
我不确定您的引擎代码实际是什么样子,但我认为它看起来像任何其他 OpenGL 程序。
如果是这样,根据我的经验,变换矩阵通常应该传递给顶点着色器,并在绘制场景时在 GPU 上应用给定的顶点信息。例如:
//MVP matrix
GLuint MatrixID = glGetUniformLocation(shaderProgID, "MVP");
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0][0]);
但是如果你想找到特定组的所有顶点的世界坐标,在渲染函数之外,你可能需要在 CPU 上进行,否则你将需要使用一些并行的OpenCL 等编程技术在 GPU 上完成工作。
最重要的是,为什么你特别想要绘图程序之外的世界坐标信息?如果您只是想找到模型的世界坐标,您可以简单地为场景中的每个模型设置一个中心坐标,并且只跟踪单个坐标而不是整个网格组。
顶点信息应始终位于模型坐标中并存储在无接触的顶点缓冲区中,除非您想对其应用一些修改。