Home       About Me       Contact       Downloads       Part 43    Part 45   

Part 44: The One That Got Away

December 9, 2011

First, let me thank everyone for the supportive comments last week. It really helps me to think through what I'm doing here and what my goals are.

I think what's happened is that I made a strategic mistake back near the beginning. I was enhancing the single demo up through part 4, but then I decided to work on improving the rendering speed. I went back to a clean slate for that project, and never integrated it into the main demo.

I continued that pattern with the other little pieces I did. It's much quicker to build things without a lot of old code to maintain in the process. The drawback is I didn't have a single game-style demo that continually improved. Instead, I have lots of disconnected pieces. Plus I kept trying for spinoffs like the multi-platform support, the Minecraft viewer, and Crafty as well as SeaOfMemes. That made my efforts very scattered, and has lead to this feeling that I'm not making any progress.

The poll last week ran 60-40 in favor of continued graphics work, but I neglected to add a "don't care" option, which I suspect would have gotten most of the votes. I'm a bit tired of graphics programming at the moment, and annoyed at this feeling of no progress, so I'm going to integrate a bunch of the pieces. I still haven't decided about running a server and making a real game out of it.

Since I don't have anything new to write about this week, let me cover something I skipped over. At the end of Part 42, I mentioned a shader experiment that didn't work out. It was still interesting though, so here's what happened. Perhaps you can suggest some changes.

The Asteroid Belt

I was frustrated over rendering a planet, since it's so detailed, and decided to spend some time on the asteroid belt. As long-time readers know, there's a serious problem with the belt -- you can't see it from within the belt. At a distance, it can look like Saturn's rings, but when you get close, the ring is so thin that it just disappears.

The problem was even more apparent when I rendered individual asteroids. Unless you put in a huge number of them, they just get lost against the stars. You don't even notice when you are moving through the belt, and it's hard to pick out an individual rock. I think this is probably realistic, and I wasn't sure there was a solution.

After rendering fog in Part 37, I had an idea. What if the belt were really dusty, so that there was a fog effect? Then you'd see something as you approached the belt, and see a foggy background when inside it. The asteroids could even cast shadows through the dust, which would make them stand out more. I decided to try and implement a ring-shaped belt of fog around the planet.

Since I was having some success with rendering the planet using a shader instead of a textured sphere, I thought I'd do the same with the belt. I could easily have done a ring with uniform thickness and color, but I got ambitious. I wanted to render a colorful set of rings from any point outside or within the ring. I envisioned moving into the gaps and seeing foggy walls to both sides.

Fig 1: Across the Belt
Fig 2: Identical Chords

It wasn't clear how I was going to do that in the shader though. Figure 1 shows the problem. I have variable densities (even variable colors) and rays from the eye to an object (asteroid, planet, or infinity) could cut through any piece of the ring. In fact, it's worse than this picture, because the actual ring is three dimensional, fading from solid to nothing above and below.

Looking at Figure 1, I realized it's not quite that bad. If the rings are completely symmetric around the center, any chord completely through the disk can be rotated to "normalize" it. Another way to put it is that all chords of the same length are identical -- they cut through the same texture pattern. In Figure 2, chord A could be rotated into position to match B, since they are the same length.

This meant that to render the ring from a distance, all I'd need is a thickness value across each chord length, where chords run from length=0.0 (just tangent to the outside of the ring) to length=1.0 (through the center, completely crossing the disk.) I could precalculate this easily. But what about viewing between points within the ring? (See Figure 3)

Fig 3: Partial Chord
Fig 4: Density along a Chord

Figure 4 shows the density of the ring. For simplicity, I'm just using a uniform thickness here, not the variable density of the ring shown in the other figures. If I sum along the length of the chord, I get the red line -- increasing fog density as you get deeper into the disk, then no increase at the center of the disk (which is open), then increasing density of fog again to the end of the chord.

I precalculate a texture, where x is the chord length and y is the sum of density along the chord. That's shown in Figure 5. Notice the smooth grade on most of the chords. They just cut through a uniform block of fog, with density steadily increasing. Only at the right, when the chords start to cross the center, is there anything interesting.

Now, if I want the density of fog between two points "a" and "b" on the chord, I can get the values of those points on the summed density and subtract them. density(edge to b) - density(edge to a) is the density(a to b).

This means I can render the ring from any point to any point by finding the length of the chord cut by the ray from the eye to the target point. Then I find the two intersection points of the eye and the target along the chord.

Since this algorithm works just as well on a sphere as a disk, I just changed my planet shader to use this texture and rendered a sphere with a spherical hole taken out of the center. That's shown in Figure 6.

Fig 5: The "Chord map" texture
Fig 6: A sphere with a hole in it

Resolution Problems

This looked good, and I was very encouraged that I was on the right track. Unfortunately, things started to go wrong when I tried to render my ring. I did that by just cutting a slice through the sphere. I adjusted the start and end points along the chord to be not the eye and target, but the intersections of that line with the top and bottom of the ring. The result is Figure 7.

Fig 7: A sliced sphere
Fig 8: Sliced too thin

Unfortunately, I got some ugly moire patterns. The reason this is happening is the resolution of my lookup table texture. There are two problems. First, if the texture is only 512 by 512, then there are only 512 distinct sums along each chord. The line in Figure 4 is not continuous, but steps at the resolution of the texture.

In Figure 8, you can see the geometry of the situation. Looking down at the disk I'm subtracting two very close points on the chord. There's just not enough resolution in the table to do this, and so you get visible steps that show as moire patterns.

The second problem is that I was using a single byte of texture at each point along the chord. So values are only from 0 to 255, even though we have 512 points of summed density along the chord. Again, there's not enough resolution. I can fix this by using a floating point texture, or using the RGBA values of the texture to encode a more precise number. It doesn't help much though.

Do it as a Disk

The spherical approach clearly was not going to work. The ring is too thin a slice of the sphere. So I redid it as a disk, thinking I'd avoid the problem of short chord sections that way. If the chord section was short only when you were looking at something close, that would be no problem. You only expect a very thin layer of fog when you are close to the target.

Fig 9: A thick disk
Fig 10: Still doesn't work

In Figure 9, you can see what I tried. Chords run across the ring, so they should be long. For vertical distances, I just fall off linearly from the y-value of the ring. The total fog of that line is just the x-length times the average of the fog density at the two endpoints.

Unfortunately, it still doesn't work, as you can see in Figure 10. Again, there's not enough resolution to pick out the features of the ring. I had to remind myself that the texture size limits the resolution here. You can't see features smaller than a cell in the table. In practice, since the stepping produces artifacts, a feature has to be many cells wide. So this might work if I only had a few distinct rings instead of the complex pattern I'm using here.

But there's another problem, shown in the bottom part of Figure 9. When I look down on the ring, my two intersection points will be very close in x value, which means subtracting two close values in the chord map again. Looking straight down, the two points will be on the same chord value, giving zero difference in density. From above, the ring will disappear.

Spherical Fog?

The last thing I tried with this technique was fog on a sphere. This would use the chord table the way it's used in Figure 6, which worked well. And I did get something that sort of worked. Unfortunately, there was still a resolution problem, and visible stepping of fog densities.

More problematic was that I don't know how to handle the angle of the light on fog. The whole technique assumes that the coloring is symmetric around the center. When you take lighting into account, this is no longer true. I would have to add another dimension or do something like the atmospheric scattering algorithms, which test multiple steps along the ray path.

At this point, I got disgusted with the whole approach and published Part 42 without any of this discussion. I also stopped working on shaders and decided to integrate my old GUI code into the demo!

I'm still working on GUI stuff and other integration. I hurt my shoulder again though, so I'm kind of out of it. Hopefully, I'll have another demo for you soon.

Home       About Me       Contact       Downloads       Part 43    Part 45   

blog comments powered by Disqus