OpenGL 如何渲染具有多个对象的基于体素的世界场景
OpenGL How to render a voxel based world scene with many objects
我正在使用针对 JVM 语言的 OpenGL 绑定开发体素游戏引擎(scala 是我的例子)- LWJGL 3 for OpenGL version 4.5。目前我坚持使用块渲染(32*32*32 块)。为了渲染任何对象,我首先给它一个唯一的 ID,将类似的对象(如简单的块)视为具有不同转换的对象,在初始化阶段创建一个 VAO 数据事物,在完成所有准备后,我渲染整个块循环遍历每个块,将其数据传递给着色器,然后使用取自 ID 的适当偏移量调用 drawElements。这样 fps 从 3000(3 条轴线和一个巨大的网格对象单独渲染)下降到 1-2。那么我应该如何正确渲染块?
我参考了basic-block-rendering教程。
Fps 下降代码:
def render(shader:Shader): Unit ={
for(x <- 0 until SIZE) {
for (y <- 0 until SIZE) {
for (z <- 0 until SIZE) {
val obj = blocks(x)(y)(z)
if(obj != null){
val M = obj.getTransformationMatrix() * Matrix4F.matrixTRANSLATION(obj.getPosition())
shader.setUniformMat4f("M", M)
shader.setUniformMat4f("MI", M.inverse())
shader.setUniformBool("lightInteraction", obj.lightInteraction)
shader.setUniform1f("smoothness", obj.smoothness)
shader.setUniform3f("matD", obj.matDiffuse)
shader.setUniform3f("matS", obj.matSpecular)
GL13.glActiveTexture(GL13.GL_TEXTURE0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, obj.getTextureID())
Shader.full.setUniform1i("tex", 0)
RenderRegistry.getRenderManager().render(obj.getID, obj.getRenderType())
}
}
}
}
}
您当前的代码正在尝试每帧执行 32768 (32*32*32) 次矩阵乘法、求逆、统一上传和纹理状态更改。通常,这些都是昂贵的操作,并且很可能并非块中的每个对象都需要它们。幸运的是,优化的方法很少。
分组依据texture/material
很可能有些对象共享相同的着色器参数和纹理。绑定这些值然后绘制共享它们的所有对象应该会恢复一些性能。
实例化渲染
我不会过多地讨论这个问题,因为有一个关于该主题的优秀教程 here。假设您所有的体素都使用相同的几何体并且大部分是静态的,您可以将变换矩阵上传到缓冲区并上传顶点对象 一次。这样做的好处部分取决于您的图形驱动程序,但对于大型体素场景,它可能是值得的。
将矩阵运算移至 GPU
这是次要的,但如果您不想为实例化而烦恼,您至少可以节省一些矩阵运算的周期。不要上传 M.inverse()
而是在 GPU 上调用 inverse(M)
。还可以考虑上传体素位置并作为制服单独变换并在 GPU 上执行 transform*position
。
我正在使用针对 JVM 语言的 OpenGL 绑定开发体素游戏引擎(scala 是我的例子)- LWJGL 3 for OpenGL version 4.5。目前我坚持使用块渲染(32*32*32 块)。为了渲染任何对象,我首先给它一个唯一的 ID,将类似的对象(如简单的块)视为具有不同转换的对象,在初始化阶段创建一个 VAO 数据事物,在完成所有准备后,我渲染整个块循环遍历每个块,将其数据传递给着色器,然后使用取自 ID 的适当偏移量调用 drawElements。这样 fps 从 3000(3 条轴线和一个巨大的网格对象单独渲染)下降到 1-2。那么我应该如何正确渲染块?
我参考了basic-block-rendering教程。
Fps 下降代码:
def render(shader:Shader): Unit ={
for(x <- 0 until SIZE) {
for (y <- 0 until SIZE) {
for (z <- 0 until SIZE) {
val obj = blocks(x)(y)(z)
if(obj != null){
val M = obj.getTransformationMatrix() * Matrix4F.matrixTRANSLATION(obj.getPosition())
shader.setUniformMat4f("M", M)
shader.setUniformMat4f("MI", M.inverse())
shader.setUniformBool("lightInteraction", obj.lightInteraction)
shader.setUniform1f("smoothness", obj.smoothness)
shader.setUniform3f("matD", obj.matDiffuse)
shader.setUniform3f("matS", obj.matSpecular)
GL13.glActiveTexture(GL13.GL_TEXTURE0); // Texture unit 0
glBindTexture(GL_TEXTURE_2D, obj.getTextureID())
Shader.full.setUniform1i("tex", 0)
RenderRegistry.getRenderManager().render(obj.getID, obj.getRenderType())
}
}
}
}
}
您当前的代码正在尝试每帧执行 32768 (32*32*32) 次矩阵乘法、求逆、统一上传和纹理状态更改。通常,这些都是昂贵的操作,并且很可能并非块中的每个对象都需要它们。幸运的是,优化的方法很少。
分组依据texture/material
很可能有些对象共享相同的着色器参数和纹理。绑定这些值然后绘制共享它们的所有对象应该会恢复一些性能。
实例化渲染
我不会过多地讨论这个问题,因为有一个关于该主题的优秀教程 here。假设您所有的体素都使用相同的几何体并且大部分是静态的,您可以将变换矩阵上传到缓冲区并上传顶点对象 一次。这样做的好处部分取决于您的图形驱动程序,但对于大型体素场景,它可能是值得的。
将矩阵运算移至 GPU
这是次要的,但如果您不想为实例化而烦恼,您至少可以节省一些矩阵运算的周期。不要上传 M.inverse()
而是在 GPU 上调用 inverse(M)
。还可以考虑上传体素位置并作为制服单独变换并在 GPU 上执行 transform*position
。