I am working on a voxel game engine using OpenGL binding for JVM languages (scala is my case) - LWJGL 3 for OpenGL version 4.5 . Currently I'm stuck with chunk rendering (32*32*32 blocks). To render any object I firstly give it an unique ID, treating similar objects, like simple blocks, as one with different transformations, create one VAO data thing in initialization stage and after all preparation is done, I render a whole chunk looping through each block, passing its data to the shader and then calling drawElements with appropriate offset, taken from ID. This way fps drops from 3000(with 3 axis lines and a huge grid object rendered separately) to 1-2. So how should I correctly render a chunk ?
I used https://sites.google.com/site/letsmakeavoxelengine/home/basic-block-rendering tutorial as reference.
Fps dropping code:
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())
}
}
}
}
}
EDIT: for those who are interested, instanced arrays helped me a lot ! Link for a tut: http://learnopengl.com/#!Advanced-OpenGL/Instancing
Answer
The way you are rendering voxels is extremely ineffective. For each cube you have to do calculations, set uniforms, and worst of all bind textures(This is usually a very expensive operation)
Typically effective voxel rendering involves multiple techniques to speed up rendering:
- Rendering in chunks: You split your world into neatly sized proportions, which makes it easier to cull and to generate/regenerate.
- VBO: You render each chunk as a single VBO to utilize least possible API calls.
- You render only the blocks that are "facing air"(Don't render the blocks that are completely surrounded and have no visible faces.) to reduce polygon count. The most commonly used algorithm for this, is called "Marching cubes". wiki
- To further reduce rendered polygons, you could use frustum culling, so you would only render the chunks that are in your viewport.(Thanks to @Kaarez to pointing this out)
- Texture atlas: you don't bind a new texture for each cube, but create a huge texture which contains all of your voxel textures and just change the voxel texture coordinates to match the correct texture's location on the atlas.
The site you referenced also has these and other techniques in its tutorial section, so you should probably reduce your world size so you get a testable FPS and start implementing these optimizations.
Also you should measure performance not in FPS but in frame time: The time needed to rendered a single frame. FPS is not good, because it is not linear.
No comments:
Post a Comment