Well, I still have a few bugs in the demo, but it has been 10 days since the last part, and it works well enough. So let's talk about fog.
Fog is caused by water droplets in the air, which both reflect light from the sun and block light from objects you can see. Fog would be useful in the game if we wanted a really foggy area, but for two other reasons as well. First, a small amount of fog or mist washes out distant scenery. Your eyes expect that and so it becomes another visual cue that an object is distant. Second, in a game where we are loading distant scenery as you move, a dense fog at the view distance could hide the loading operation.
We can implement a simple version of fog by taking the distance to a pixel when we draw it, and dividing that by the maximum fog distance. Then we just blend in a fog color in this proportion -- zero fog at the minimum distance, and 100% fog at the maximum. Doing this in the demo gets Figures 1, 2 and 3.
Figure 3 is what it would take to hide all the chunk loading beyond the view distance. As it turns out, even this isn't good enough. I could still see chunks being loaded at the edge of the fog. So I added some logic to fade them in over .75 seconds. Since we are used to things appearing in a fog, this seems to work well. Unfortunately, I don't really want to play in foggy, overcast areas (I'm from San Diego, not Seattle).
Another issue is what to do underground. If you use white fog in those areas, distant dark areas get brighter. See Figures 4 and 5. A comment on the last post said Minecraft just uses black fog color for underground areas. But you would have the same problem above ground in a large dark cave or a building.
Then it occurred to me that I have Minecraft lighting values on all the vertexes, so why not use that as the fog color? Then I'd have bright fog up in the sun, and dark fog down underground or in an unlit building. But using this technique does weird things to shadows, as you can see in Figure 6.
I'm not sure how I want to fix this problem. In the demo, there is no fog underground.
Figure 7 shows what I tried. There's a layer near the ground, and the thickness of fog is computed by how much of a ray from the eye to the landscape point is within the fog bank. Since the fog is between two y values, it's easy to calculate. This produces the landscape in Figure 8.
I'm implementing this algorithm in the vertex shader for the land, but that didn't work for the water or the sky. The water is a single big polygon, so there aren't enough vertexes. The skybox can have sun, moon or stars as well as the blue sky dome.
To fog this, I have a shell painted after all the rest of the sky, and the shader blends fog in the fragment shader, calculating the correct alpha at each point.
That looked pretty good, so I moved the code into McrView. Here's a video, which starts with clear skies so you can see how chunks fade in, and then switches to foggy skies, at 160m distance, and then at 300m distance. In SeaOfMemes, where I won't be drawing the entire landscape as cubes, I think I'll be able to extend the view distance considerably.
On my more limited Linux box, and on the Mac, I was just displaying the simple worlds you get when Minecraft creates a new world. I added a few shapes in creative mode just to make sure they worked. And so it never occurred to me to add up all the display memory being used by the 3D shapes. Its turns out to be a lot -- over 500 meg for the initial view in the TwentyMine world.
One problem I created for myself by using more complex shapes than I needed. In Figure 9 you can see the shape of the rails by looking at the ends. A simple box would have been good enough. Some of the other shapes are also a bit more elaborate than needed. I went back and edited some of them down, but only cut the size by 20%.
Another problem is that SketchUp generates double-sided triangles some of the time. I haven't figured out what causes it to do this, but it seems related to how the object is created, not its final shape. Sometimes I can delete and recreate an edge and it will suddenly realize the shape is solid and drop all the inside faces. Other times, I haven't been able to get it right.
Back in Parts 16 and 17, I explained how I could use the shader language to implement a compressed vertex for my cubes. The idea is that since I only need integers from 0 to 32 for a chunk of cubes, it makes no sense to use floating point numbers. The position (x,y,z) and texture coordinates (tu,tv,texture number), and three lighting values (ambient, sky and torch) take 9 floating point numbers, or 36 bytes. Using 6 bits for each coordinate and compressing the other fields, I fit the vertex in 8 bytes.
That means the initial view in TwentyMine requires only 42 megabytes for the cubes, which will fit in any display out there.
Unfortunately, I couldn't do that for the 3D shapes. They have arbitrary values, not cube edges. So I was using the full 36 bytes per vertex. On top of that, the shapes are more complicated. A section of rail or a flower could have 300 triangles, or nearly 1000 vertexes, compared to the 24 a cube has. In practice, the cube will have even fewer, since I don't draw the hidden sides. With the 3D shapes, there are no hidden sides. So a single curved rail section or chest might take as much display memory as a 100 cubes.
I fussed over this a bit and managed to compress the shape vertexes down to 12 bytes each. That cuts the initial scene down to 162 meg total, which is acceptable. (I'd like this to run on a display with only 256 meg of memory.)
The vertex compression code requires features of the OpenGL 3 shading language. Under OpenGL 2.1, which is what I have on the Mac, you can't send integers to the shader as vertex components, and there are no bit shift operators. I can't implement these compressed vertexes.
That leaves me stuck with the situation that a more modern display, which has a lot of memory, will also support OpenGL 3 or better and I can compress the vertexes, and need less memory. But an older display, which has less memory, will also not have the GL version I need, and so vertexes will take more memory.
If you run the demo and distant scenery flickers in and out, or the performance is terrible, edit the options and cut the view distance down to 80, from the default 160. I haven't decided what to do about this in future versions. I'd like to know what the size of display memory actually is, rather than relying on an option, but from Google searches, there doesn't seem to be a good way to find out on all three platforms.
For Windows, download McrView Part 37 - Windows.
For Linux, download McrView Part 37 - Linux.
For Mac, download McrView Part 37 - Mac.
The UI now allows you to toggle night/day and fog:
You'll need to edit options.xml and point it at the Minecraft world you want to view. The save files are in different directories for each OS:
If the world directory is "New World", set the option line to world="New World". Then run McrView.
For those pitiful few who don't run Minecraft and want to try the demo, I've uploaded a small piece of the TwentyMine world I test on. It's at SampleWorld.zip (32.8 meg). Unzip this into your Minecraft/saves directory, or wherever. If your unzip program creates a directory to contain the unzipped files, make sure you point the options to the TwentyMine directory, not the directory created by Extract/unzip.
Change the world option in options.xml to point to the "TwentyMine" directory (fully qualified if not in the Minecraft dir.)
Source code is at McrView Part 37 - Source.
Thanks to Alan Hauck for the artwork he's added to the project -- a dandelion, a rose, a cactus and tall grass.
I'm going on a short trip with family next week, so Part 38 will be delayed.
Update - What To Do Next?
Since we haven't had a poll in awhile, I'll let you decide what I work on next. Here are the choices:
Here's the poll. I'll check back whenever I have WiFi access on my trip.
blog comments powered by Disqus