2

I am making a voxel-based game, and for needs of it, i am creating a block rendering engine.

Point is, that i need to generate lots of cubes. Every time i render more than 16x16x16 chunk of theese blocks, my FPS is dropped down hardly, because it renders all 6 faces of all of theese cubes. That's 24 576 quads, and i dont want that.

So, my question is, How to stop rendering vertices(or quads) that are not visible, and therefore increase performance of my game?

Here is class for rendering of a block:

public void renderBlock(int posx, int posy, int posz) {
  try{
    //t.bind();
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);// or even GL_FRONT_AND_BACK */);

    glPushMatrix();

    GL11.glTranslatef((2*posx+0.5f),(2*posy+0.5f),(2*posz+0.5f));             // Move Right 1.5 Units And Into The Screen 6.0
    GL11.glRotatef(rquad,1.0f,1.0f,1.0f);

    glBegin(GL_QUADS);               // Draw A Quad

    GL11.glColor3f(0.5f, 0.4f, 0.4f);             // Set The Color To Green
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Right Of The Quad (Top)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Left Of The Quad (Top)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f, 1f);         // Bottom Left Of The Quad (Top)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f, 1f, 1f);         // Bottom Right Of The Quad (Top)

    //GL11.glColor3f(1.2f,0.5f,0.9f);             // Set The Color To Orange
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f,-1f, 1f);         // Top Right Of The Quad (Bottom)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f(-1f,-1f, 1f);         // Top Left Of The Quad (Bottom)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Left Of The Quad (Bottom)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Right Of The Quad (Bottom)

    //GL11.glColor3f(1.0f,0.0f,0.0f);             // Set The Color To Red
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f, 1f);         // Top Right Of The Quad (Front)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f, 1f, 1f);         // Top Left Of The Quad (Front)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f,-1f, 1f);         // Bottom Left Of The Quad (Front)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f,-1f, 1f);         // Bottom Right Of The Quad (Front)

    //GL11.glColor3f(1f,0.5f,0.0f);             // Set The Color To Yellow
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Left Of The Quad (Back)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Right Of The Quad (Back)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Right Of The Quad (Back)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Left Of The Quad (Back)

    //GL11.glColor3f(0.0f,0.0f,0.3f);             // Set The Color To Blue
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f(-1f, 1f, 1f);         // Top Right Of The Quad (Left)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f(-1f, 1f,-1f);         // Top Left Of The Quad (Left)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f(-1f,-1f,-1f);         // Bottom Left Of The Quad (Left)
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f(-1f,-1f, 1f);         // Bottom Right Of The Quad (Left)

    //GL11.glColor3f(0.5f,0.0f,0.5f);             // Set The Color To Violet
    GL11.glTexCoord2f(0,0);
    GL11.glVertex3f( 1f, 1f,-1f);         // Top Right Of The Quad (Right)
    GL11.glTexCoord2f(1,0);
    GL11.glVertex3f( 1f, 1f, 1f);         // Top Left Of The Quad (Right)
    GL11.glTexCoord2f(1,1);
    GL11.glVertex3f( 1f,-1f, 1f);         // Bottom Left Of The Quad (Right)
    GL11.glTexCoord2f(0,1);
    GL11.glVertex3f( 1f,-1f,-1f);         // Bottom Right Of The Quad (Right)

    //rquad+=0.0001f;
    glEnd();
    glPopMatrix();
  }catch(NullPointerException t){t.printStackTrace(); System.out.println("rendering block failed");}
}

Here is code that renders them:

private void render() {
  GL11.glClear(GL11.GL_COLOR_BUFFER_BIT|GL11.GL_DEPTH_BUFFER_BIT);
  for(int y=0; y<32; y++){
    for(int x=0; x<16; x++){
      for(int z=0; z<16; z++) {
        b.renderBlock(x, y, z);

      }
    }
  }
}
themorfeus
  • 277
  • 1
  • 3
  • 17
  • possible duplicate of [How to do face removal in a unit-cube world a la Minecraft?](http://stackoverflow.com/questions/6319655/how-to-do-face-removal-in-a-unit-cube-world-a-la-minecraft) – Nicol Bolas Apr 06 '12 at 17:25

3 Answers3

4

Your code has a larger performance problem. You shouldn't be using immediate mode OpenGL rendering (glVertexXXX() calls) to draw such a large number of vertices.

When you perform your rendering this way, your code has to make a call to the graphics driver for every vertex, which is slow.

Instead, you should use Vertex Buffer Objects. This will allow you up upload all your geometry directly onto the graphics card, then draw all your cubes in a single Java method call (probably glDrawArrays).

ulmangt
  • 5,343
  • 3
  • 23
  • 36
  • 2
    That is just fixing the smaller problem. The issue here is that he attempts to do voxel rendering in a brute force way, which has no real chance of working. At least not for any input size that matters. – pmr Apr 06 '12 at 16:23
2

I recommend, like ulmangt said, that you use VBO but before that, you need to calculate only the visible faces.

This can be easily done by (just once at the beginning) checking if a face is neighbor to a empty voxel ("air"). If it is, add that quad (face) to the rendering.

Afterwards you just do this check only on the neighbors of changed voxels. Example: When the user removes a cube, check the 6 neighbors of that voxel and add those quads to the rendering. Do the reverse when voxels are added, remove neighbor quads.

So with a 5x5x5 cube made of voxels, instead of 750 quads, you end up with 150.

Other gains can be had by just rendering chunks (a group of voxels) in the view (ignoring the ones to the back of the player) and using a distance limit.

You can go even more crazy by using octrees to only render chunks that you know that could possibly be visible.

Diogo Gomes
  • 2,385
  • 1
  • 19
  • 29
  • Thanks :) Now, do you have an idea of how to determine if voxel touches voxel? – themorfeus Apr 12 '12 at 14:16
  • Well, a chunk voxels will be just a 3d array filled with the type of voxels (I think minecraft used a byte to represent the voxel type in the indev versions, no idea now.). The easiest way that comes to mind it's to just iterate through the whole array and check the neighbors of each voxel and add the visible quads to a list. (TL;DR: Iterate through 3d array of voxels, ignore "air" type voxels, check neighbors for air voxels, add visible quads.) – Diogo Gomes Apr 13 '12 at 01:44
1

A good idea is to NOT use immediate mode to render your blocks, I use display lists because they are the easiest to set up and are VERY fast. Second of all, Even if you still only use immediate mode then use only one glBegin/glEnd call when you're drawing, Use a texture atlas for textures in the future and to your main question which is how to stop rendering invisible faces, It is pretty simple, The way I do it is basically make six methods for each face which returns a boolean. You would simple return if the block type in the direction of that face is an air block, If it is. Then that means it will return true, Therefor render it. And in your draw method add the parameters "boolean backface, boolean topface... etc" and an if statement checking which side to draw.

hope I helped, Good luck!

ABOODYFJ
  • 30
  • 1
  • 9
  • this question is over 2 years old, and i already resolved the issue using display lists. Even though, i already drifted away from this project at all. – themorfeus Aug 27 '14 at 12:01
  • I didn't read the date, Still would be a help for people looking for answers though. – ABOODYFJ Aug 31 '14 at 07:34