The last month has been pretty worthless, with very few hours put into the project. I finally have the summary code working correctly though, complete with texturing. The results are in Figure 1:
This is the real code, not any kind of mock-up. You can look at a full resolution version here. (1.18 meg) I've actually kicked up the resolution a bit from the last version I showed you. This runs at about 175 fps on my machine, and uses 50 meg of display memory. I will probably continue to fiddle with the resolution issues. And since displays vary so much, I'll need to make the actual resolution an option.
I'm not 100% satisfied with this, but it's good enough to go on with. There have been a few issues with this code.
The terrain is made of quads, and in each quad we have bricks. I average the full brick texture to get a 1 by 1 pixel summary. These are used to make the final texture of the quad.
All the quads are packed into a single OpenGL texture for the chunk, using a "level-oriented" strip packing algorithm -- the first algorithm described here. I still need to play with this a bit (or try the other algorithms) to see if I can get better quad packing, but it will do for now. The results look like Figure 2.
The entire chunk is drawn as a single call with this as the texture, giving pretty good performance. The Figure 1 image is 108 chunks.
In addition to improving the packing of quads, I should also eliminate hidden data off the edges of chunks. Several of those large strips in the first row are the sides of the chunk, which will never be seen.
I also need to create distant single-pixel equivalents to the non-cube shapes like flowers, rail tracks, etc. Right now, it's just using the first color in the texture of the shape to make the summary, which can be very wrong.
The big problem with my texturing is that since a brick is only one pixel, I can't turn on mip-mapping or any kind of averaging in the texturing. That would just ruin the texture for my purposes. Instead, I'm using GL_NEAREST on the texture filtering. This aliases quite a bit as you move. It's no worse than Minecraft, but not as nice as McrView.
Small or thin features are also a problem. The summaries are created with a bounding box in each cell. Although there are never more than 32 by 32 by 32 cells in a chunk, the box in a cell could be as small as a single brick. In Figure 1, you can see the elevated walkways in the distance (look at the black lines near the gray cube, center right of the image.) As these get more distant, they flicker as you move.
I think the only solution for this is some kind of whole-screen anti-aliasing, but I need to read more about AA techniques. I don't intend to do anything on this now.
Texturing individual bricks this way has another problem -- the coordinates of pixels within a quad can round into the next quad in the texture. Since there's no relationship between adjacent texture patterns (they are packed by size), this meant lots of edge artifacts in the output. See Figure 3:
The solution to this was a custom shader. Vertexes include the bounds of the quad, and the fragment shader can clamp each pixel to the bounds of the quad texture.
Brick codes vs. RGB
My first design for this code created the textures in the summaries, based on the full resolution versions of the brick textures. I figured I could then take a high resolution version of the bricks in a quad and scale that down, averaging adjacent pixels.
Unfortunately, this locks the summaries to a particular set of brick textures. For a single user, this wouldn't be a huge problem. After you changed the texture pack, it would just have to recalculate all the summaries (though that's pretty slow on a large world!)
In a multi-user environment, it's more of an issue. The server is going to have the summaries in its database, and send them to the clients as users move around. If the summaries are based on the texture pack, all users must use the same textures.
I didn't really want that, so I changed the code to save brick ids in the summaries, not actual texture pixels. The clients lay out the final textures and create them from the texture pack in use on that client.
The cost of doing this is that adjacent bricks don't blend, increasing the aliasing issues. However, the full texture approach wasn't going to be practical anyway:
I now have a grid of chunks in three dimensions. All chunks are 32 by 32 by 32 cells. Nearest the eye, the code will be using the full bricks as in McrView. All the distant landscape is drawn with summary chunks, where bricks are single pixels in a texture. Cells range from size=2 out to size=1024 bricks across.
There are parameters here:
That's what I have so far. I still need to rebuild the old rendering code with instancing and quads instead of individual faces. Then integrate it all into a single demo.
blog comments powered by Disqus