Skip to main content
GameDev.net gamedev.net
🔒 Locked

OpenGL Cubes

Started by mavvie Jul 30, 2011 at 1:56 AM 26 replies 4k views
Original Post
mavvie
mavvie
Alright. I have a working game engine with gravity and collision detection with cubes. Great framerate using 1k cubes, i think i have z-buffering working fine. HOWEVER, if i'm creating a game i need more than just 1k cubes....I know that minecraft stores in memory 81x16x16x128=2654208 cubes, but when I run only 1000000 cubes in memory my framerate is unbearable (<1 FPS)...what are other techniques of improving rendering? I already use a z-buffer, back face culling doesn't work because of the way it renders (i should change that?), I use glDrawElements to save CPU ticks, and i cant think of anything else...


How to make it render faster? Checked google can't find much. Help please?
zacaj
zacaj
Youre not going to be able to draw that mane SEPARATE cubes. Minecraft only works because the cubes are aligned on a grid, so when theyre adjacent to each other it can skip the face in between two cubes. Less than 10% of the cubes in minecraft are actually visible at a time, and none of them are simulated using a physics engine
mavvie
mavvie
haha oh so how should i fix this? make an algorithm that will merge the cubes together? any ideas?

EDIT:
All the cubes are axis aligned
zacaj
zacaj
Are they also uniform sized, and on a grid, so theyll touch exactly? Otherwise I doubt that any sort of algorithim will help. Unless you mean something else my merge all the cubes together?
mavvie
mavvie
Yup, i'm thinking of making something similar to minecraft and every cube/block is 10x10x10 aligned to the axes, so yeah should i just merge them for drawing?
mavvie
mavvie
also uh, if you cant render that many cubes how on earth can you find the rendering power to render full maps and buildings and people in FPS's?? like cubes are simple and i'm using vertex arrays and glDrawElements() so really no lighting or texture mapping....but real games how do they render?
zacaj
zacaj
Check out this post, it talks about removing "buried cubes"
http://www.sea-of-memes.com/LetsCode1/LetsCode1.html

Much of the slowdown youre getting is probably coming from call overhead. Although the games draw alot more triangles than you are, they draw them in bigger batches, so theres not as much superfluous data going around. You might want to try to implement geometry instancing, it should help a lot with the frame rate. Are all your cubes textured the same? How many calls are you doing per cube?
Ashaman73
Ashaman73
Here's a quick idea from my terrain engine:
Use static batching, that is you precalculate the triangle of a certain section of your game. I.e. divide your game world into 16x16x16 or 32x32x32 blocks. Then create an simple algorithm to determine all visible cube sides in a block (not visible if a solid cube exists on both sides of a cube side). From each visible cube side construct two triangles and connect them all in a single triangle strip (using degenerated triangles to connect two cube sides).

The second thing to do is to create one vertex buffer objects as cache, this cache will be subdivided into N smaller memory blocks to hold the tringle strip of a game world block. Then use a least recent used cache for updating your VBO cache on demand, so that only the (almost) visible part of your game world will be pre-calculated and cached on-the-fly.

This will leave you only with a handful of render calls, the precalculation of a game world block should be done in a secondary thread ahead of time (not when it is already in view distance).
It's quite easy, good lock
mavvie
mavvie
i saw zacaj's answer first(yesterday) and that post was immensely helpful; i'm now able to run ~27k cubes on my supershitty laptop at 64fps (desired fps). When I upped it to 66k cubes framerate dropped to about 30fps but it will be higher on better graphics cards (and i can actually learn about VBO's when i get home to real graphics cards).

Now rendering has:
Depth buffering
View frustum culling (helps A LOT)
Boolean flags(6) on each cube representing whether each face is covered by another block or not (helps A LOT as well)

To Do:
VBO's
Texture mapping/maps
Slight change to draw code to use glDrawElements() again, as i currently draw each visible face as a quad...
Hidden surface detection

Thanks zacaj!
and ashaman that answer is great but i honestly dont want to divide my world into triangles, but using VBOs sounds great and i will definitely implement them soon.

Also uh, can either of you tell me a good way to not even send a given face to the opengl pipeline if it is covered by something else? Like if i'm on top of a cube it will still draw the bottom(but the bottom isn't visible obviously), is there an easy way to detect hidden surfaces and simply not draw them?
zacaj
zacaj
All I can think of is back face culling:
glEnable(GL_CULL_FACE);
glCullFace(GL_CCW);
I think thats the syntax anyway...

Though you may already be doing that
mavvie
mavvie
ok ive added that code and well i can't really test if it works but when i previously tried backface culling using mode (GL_BACK) or (GL_FRONT) it totally didint work, but GL_CCW works great (no visible problems)

so thanks i think that apart from VBO i've done all i can to make rendering fast

*breathes* time to actually make a game xD
mavvie
mavvie
hrmmm....backface culling is making absolutely no difference to the framerate (the numbers are the same either way)......

I think that i need to find a way to merge all the faces that are drawn together, at least for the non-visible ones? Say i need to turn 10x10x10 1x1x1 cubes into 1 10x10x10 cube...but that's not easy O.o
mavvie
mavvie
wait or should i use glDrawElements() so that instead of redrawing identical vertices it only draws each vertex once?
zacaj
zacaj

wait or should i use glDrawElements() so that instead of redrawing identical vertices it only draws each vertex once?
That might help, shouldnt be too hard to test
What are you doing now to draw them, exactly?
mavvie
mavvie
My current drawing code:


if(type!=NULL_BLOCK)
{
glPushMatrix();
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3,GL_FLOAT,0,vertices);

glColor3f(r,g,b);
glTranslatef(xPos,yPos,zPos);
glScalef(10.0f,10.0f,10.0f);
if(!faceCovered[0])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[0]);
}
if(!faceCovered[1])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[1]);
}
if(!faceCovered[2])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[2]);
}
if(!faceCovered[3])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[3]);
}
if(!faceCovered[4])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[4]);
}
if(!faceCovered[5])
{
glDrawElements(GL_QUADS,4,GL_UNSIGNED_BYTE,face[5]);
}
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
}

Before instead of glDrawElements(face[...]);
I did:

glBegin();
glVertex3f(...);
glVertex3f(...);
glVertex3f(...);
glVertex3f(...);
glEnd();

However, using glDrawElements() doesn't make much of a difference....I think that I'm going to add texture mapping but then i dont know how to fix this.... as it is i could probably make the code more efficient by drawing it all as one face instead of say 10x10x10 faces...

What should I do to make it run better? I have a crappy laptop for graphics (minecraft with minimum settings gets ~30FPS max), so i'll accept 15+ FPS, but i plan on multiplying the number of blocks by about 10.....kinda screwed xD
zacaj
zacaj
1. Remove the enabling and disabling of vertex arrays every cube.
2. Id prescale the vertices, so you dont need to call glScalef. If you later need different sized cubes, you can just scale the x10 vertices to whatever size you need anyway, but you save the need for glScalef on the most used case
3. Id make a separate index array for every combination of visible faces, so you only need to call glDrawElements once per cube, that should speed it up A LOT.
to do it easily, you can use bitshifting:
int cubeType=0;
if(face1visible)
cubeType|=1;
if(face2visible)
cubeType|=3;
if(face3visible)
cubeType|=7;
etc
glDrawElements(indices[cubeType]);
4. Id get to work on VBOs, thatll get you a good speed increase too
5. What version of openGL are you using? You should also be able to make use of Display Lists/Vertex Arrays (different from VBOs)
mavvie
mavvie
1. done
2. done
3. are you sure it would be "easy" to make an array of each possible face?? and also i dont understand how |= would work....
Say faces 1,2,3 are visible.
cubetype|=1; //cubetype in binary is now 1
cubetype|=3; //cubetype in binary is now 11
cubetype|=7; //cubetype in binary is 111
HOWEVER, what if only face 3 is visible
cubetype|=7; //cubeface in binary is 111
this obviously doesn't work.....
help?
4. yeah done VBO's pretty easy, after we get the glDrawElements() sorted out (so there's only 1 call per cube) i'll post code and see if there's improvements
5. whatever version is included with GLEW 1.6 (so maybe 1.6? xD) at least i can DO vbo's now
mavvie
mavvie
ok apparently GLEW 1.6 didn't support VBO's (got linker errors O.o) so now i'm using GLee 5.4 and no linker errors!
zacaj
zacaj
3. WHOA sorry, that should be 1248etc, not 137, so each number, in binary, has only one 1 . Thats what comes from posting at 2AM Was that what was messing you up about the suggestion, or do you still need more explanation?

5. Its not so much what GLee/GLEW supports, its what you WANT(/are able to) target. Do you want to code the entire engine taking advantage of openGL 4.2 features, and only let people with new graphics cards support it, or code it for 1.1, ad have it run on everything, using only very basic functions? I dont know what youre aims are for this game, or even how you want the graphics to look in the end, so I cant really give you any pointers there.. The most important think right now is to find out what version your laptop supports, so you know what to code for. Check http://www.realtech-vr.com/glview/
mavvie
mavvie
Oh alright now i get it
However for building the array I don't get what i should put for the "count" param of glDrawElements(). The array needs to be a fixed size...but the # of faces changes depending on how many faces are visible....
should i fill the array with 0's when it's not used and then find the number of 1's in cubetype and use that as count? this gets a bit tricky

and uh i only really need up to 1.5 features because of VBO's, but who knows maybe later on i'll go up to 3.0 (max of GLee)...for now i'm coding for 1.5.

But the issue is still the count param ^^

Thanks for the continued help

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.