My own little voxel project, though I’ve not touched it for a long time, renders blocks by constructing a single mesh for the entire chunk (32 blocks per axis), using a greedy-meshing algorithm, combining any similar blocks next to each other into larger quads with texture coordinates to match. For example, an even plane of stone blocks is rendered as 6 quads, and looks just fine.
My voxels need several things. Texture, vertex color, and maybe normal mapping if I ever get around to that. There are pre-set values for baking the most rudimentary of lighting onto them. For example, if a block’s top face has the color of (1, 1, 1, 1), then the “north” face will be (0.7, 0.7, 0.7, 0.7) to help see the block shapes. That’s probably not going to stay forever, but it helps enormously for now. For now, animated textures are outside the realm of what I need, except for on special blocks, which are their own case. I also haven’t yet done any lighting or shadows except for the basic stuff described above.
I’ve seen talk of using 6 VBO’s, one for each set of faces, IE a “north-face” vbo, etc, and you’d only ever have to draw three of them at once (How are you going to see the top and bottom of a block all at once? That’d be weird). That might be worthwhile, but I’ve yet to implement such a thing, though the greedy meshing I use is a prime candidate since each set of faces is computed in a separate sweep anyway.
Though, since having to regenerate them each frame when checking in what direction the player is looking sounds bad, you’d probably need to generate all 6 VBO’s in advance, and select which ones to render. Whether or not the performance gained from not drawing the entire chunk geometry in one draw call, vs the overhead from drawing 3 VBO’s, is a question I’ve no answer to, though I’m sure there are very smart folks around who could tell you. I’ll probably try and profile each method myself to figure that out, it’s rather intriguing.
It’s even murkier if you’re doing transparency stuff too. I don’t know how it’s done the “recommended” way; I just generate another set of geometry for the transparent stuff, since I’ve not the foggiest idea how to do transparent and opaque blocks in the same set of geometry. It’s probably possible, but not by such a plebian person as myself. My first guess is geometry shading plus some sort of binary value, but I really don’t know.
Using instancing to render the blocks themselves, from what I’m getting here, seems a little odd. Not wrong, never wrong, just not a use-case that I’ve ever considered.
For me, I store 1 short per normal terrain block in a big 1D array, which I access using a hashing function. That means there can be a little over 30 thousand different block types in the world, more if I used unsigned shorts. I think that’d be like 65536 different types? Plenty, even if people ever picked up modding for it. Whether or not that is actually more space efficient, I don’t know; I remember reading that Java uses 32 bits for each value anyway. Plus, the whole having to cast to shorts probably doesn’t help, though there’s probably a more effective method lurking on the internet.
32 x 32 x 32 shorts for tile IDs, 3 integers for the chunk index, and a pointer to the parent “grid” make up a Chunk. If / when I get around to it, each Chunk may contain a list of their own self-contained, so-called “tile entities” I.E. a furnace, and if that chunk ever gets unloaded while working, when it’s reloaded, you can calculate the work it was supposed to have done while away. So, at the very least, a Chunk takes up 66~ ish kilobytes.
That ain’t so bad! Though if I ever have rotate-able blocks, I’ll need to start storing rotation data for them, which I’ll endeavor to have no more than double the size of the chunk. Should be well within the realm of possibility, and 132~ kb / chunk is… well, it isn’t amazing, but it’ll work faster than if I were to pack everything as close together as possible. Hashing and bit manipulation probably costs more than it’s worth at that point.
A Grid contains a unique identifier name, and a HashMap<Vector3i, Chunk> map of chunks. It’s easy to figure out chunks, since I’d just divide and round down any in-world position by 32, and that would give me chunk indices. So if my player is at (20, 20, 20), he’s in chunk (0, 0, 0) and, in-chunk, he’s at (20, 20, 20). It even works for negative indices if you fiddle with the hashing function, something that I thought was taken for granted but apparently is not; if he were at (-1, -1, -1) then a lot of the time the calculation would put him in (0, 0, 0) when he should actually be at chunk (-1, -1, -1), in-chunk (31, 31, 31). Rounding has a lot to do with it, as well as integer division. I’ve not yet figured out the correct hashing function so it always works, but I’m getting there.
Loading and unloading chunks to-disk is easy as checking their distance from where the player is; if the chunk’s too far away, unload it. However, if there are any special blocks that are doing work, save their current progress so you can calculate what needs to happen the next time that chunk is loaded. It might also be an idea to unload chunks that haven’t had any change happen to them for a certain amount of time, but keep the mesh; upon the player re-entering, or upon the modification of such a chunk, record the changes, reload it, apply the changes, and continue as usual.
For example, a giant bomb going off far away would need to make changes, so you could design a slower-paced solver for that bomb blast, have it trudge along so it doesn’t slow down the actual action zone, and when it’s done, unload everything. Or, if the player gets close enough that the job needs to be finished, switch over to a faster solver. Perhaps modify the speed based on how far away the player is.
I digress. And it’ll happen again, probably. ANYWAYS!
The worst-case scenario for meshing means I’m generating 6 quads per block, I.E. a 3D checkerboard of glass and stone where there are the maximum number of visible faces possible, since there’d be two worst-case scenarios, one for opaque blocks, and another for transparent. That’s fine, I’m pretty sure something like that is literally unavoidable for such a situation, and is incredibly unlikely to occur in normal play.
Special blocks, like a furnace or something, would probably be handled with instancing, as they’re a higher-poly model, and I can supply the shader with a series of positions and rotations, probably condensed down into as little information as possible. Or a transformation matrix, that could work. If this is the sort of thing you’re trying to figure out, then:
My matrix math is rough at best, and I really need to work on that. However, I’m sure you can find that information on google somewhere, on how to create a transformation matrix. Keeping a list of “special” blocks and their positions and orientations, and sending that data to be instanced, should be very possible. Each matrix would need to be constructed with one position and one orientation, yeah?
For position, a couple floats would probably be fine, X Y and Z. For rotation, 2 bits per axis, 0 being 0 degrees, with 1 being 90, 2 being 180, and 3 being 270, would probably work, and you can supply that in the form of a byte with 2 dummy bits at the end you can ignore. YMMV on that one, I’ve not done a ton with memory packing like this, though it seems promising or at least worth investigating. Maybe using another float would be a good idea.
Constructing the matrix in the shader might be worthwhile. From what I’ve read about recent technology, the GPU has a ridiculous amount of horsepower at its disposal that the CPU does not, and that may be one of those calculation-heavy tasks that the CPU would struggle with that the GPU would be fine with. I’d recommend testing both methods; in-shader and out.
The big thing I always seem to consider is, “Is it worth the processing to figure out exactly which set of voxel faces what needs rendering?” Each face is 2 quads, it can’t hurt that much to be a little more efficient with CPU to have a few extra triangles for the GPU.
Bear in mind, I’ve not touched the thing for a year and a half. The trends and methodology for rendering voxels might have evolved since then.
However, for some specs, I can render a space of 8x8x8 chunks, which is… 16,777,216 blocks in volume, 256 blocks per edge, and it does rather well. I’ve not even gotten into culling the chunks that are underground, that you wouldn’t even be able to see anyway, so the overhead from their draw calls and 6 quads are included in that too.
Further optimizations may include the whole 6 vbo deal, and when tile entities come around, raycasting or frustum culling to make sure that they’re visible will eliminate the ones that aren’t. You could even frustum cull chunks too.
Another way it just occurred to me, for the 6 vbo deal, is to only have 1 VBO and to put all the data you need into it, and just render a subset of that data for each face that’s visible. Might be worthwhile.
I get rather excited about these things and waffle on a great deal. I’m no authority on the subject, but there’s some stuff I’ve been reading that might help:
https://lwjglgamedev.gitbooks.io/3d-game-development-with-lwjgl/content/
That’s what I’m using to help build from the ground up, since I wanted my own engine! Maybe a bit ambitious, but hey, it’s fun to learn. 8)
http://www.sea-of-memes.com/summary/blog_parts.html
A very interesting blog that has a bit of a disjointed deal to its progress, but still fun and engaging to read!
Plus various articles I found by googling everything under the sun about voxels.
Hope this was at the very least informative, if not actually helpful! ;D