home *** CD-ROM | disk | FTP | other *** search
- Persistence of Vision Raytracer
- Height Field Tutorial
- By Doug Muir
- -------------------------------
- This file is intended to provide a reference for how the new height field
- primitive is used, a discussion of how the code works for any programmers
- who would like to modify it, and, unfortunately, provide a discussion of the
- known problems with the height field primitive.
-
- First, I have chosen to implement the height field as simply a collection
- of triangles. This means that there is no real CSG to speak of with the
- height field. The height field was implemented so that large (I mean LARGE)
- numbers of triangles could be used without a 100MB hard drive to hold the
- data file and without 16MB real memory plus 80MB virtual memory to trace
- the image. Using the height field, I have traced >600,000 triangles using
- < 2MB of memory in under 15 minutes (at 320 x 200 on a NeXT machine
- equiped with a 68040). The height field was also image mapped with a
- 640 x 480 x 256 color GIF (otherwise it wouldn't have needed so much memory!)
- For comparison, it took me > 40 minutes, 4MB, and 7MB to hold the data file
- to trace 16,000 triangles (Fractint output) on the same NeXT at 160 x 120.
- Clearly, there are advantages to using the height field.
-
- The syntax for using the height field is as follows:
-
- HEIGHT_FIELD GIF[or POT] "filename" (WATERLEVEL waterlevel) END_HEIGHT_FIELD
-
- Height fields currently only support input files in either gif or the
- Fractint .pot format. The optional "waterlevel" is a float and defaults to 0.
- Higher waterlevels will tell the raytracer not to look for intersections
- below that point. Negative waterlevels will work (I think; I haven't tested
- it, but there's no reason it shouldn't) but may cause some weird results
- (there is really no good reason to use a negative waterlevel).
-
- The height field is mapped to the x-z plane, with its lower left corner sitting
- at the origin. It extends to the image width in the x direction and to the
- image height in the z direction. It is always 256 high in the y direction
- (why? because that was the easiest way for me to do it). After that, you can
- translate it, scale it, and rotate it to your hearts content. (Note: here's
- that problem I spoke of earlier. There seems to be a problem with rotating
- the height field. Parts of the height field seem to be chopped off when
- rotation is done. I am aware of the problem, but don't have a clue what's
- causing it. Any suggestions would be appreciated. -DM) When deciding on
- what waterlevel to use, remember, this applies to the untransformed height
- field. Don't use a negative waterlevel because you scale the height field
- by a negative value in the y direction or because you translated it to
- <0.0 -1000000.0 0.0>. The waterlevel should be used just like the waterlevel
- parameter for 3d projections in Fractint.
-
- Now that the basics are covered, I'll describe what the raytracer does with
- the height field. To find an intersection with the height field, the raytracer
- first checks to see if the ray intersects the box which surrounds the height
- field. Before any transformations, this box's two opposite vertexes are at
- (0,waterlevel,0) and (image_width,256,image_height). If the box is
- intersected, the raytracer figures out where, and then follows the line from
- where the ray enters the box to where it leaves the box, checking each pixel
- it crosses for an intersection. It checks the pixel by dividing it up into
- two triangles. The height vertex of the triangle is determined by the color
- index at the corresponding position in the .gif or .pot file. So if your
- .gif or .pot file has a random color map, your height field is going to look
- pretty chaotic, with tall, thin spikes shooting up all over the place. What
- this means is, not every .gif will make a good height field. If you want to
- get an idea of what your height field will look like, I recommend using
- Fractint's 3d projection features to do a sort of preview. If it doesn't look
- good there, the raytracer isn't going to fix it. For those of you who can't
- use fractint, you'll just have to do the best you can.
-
- The following is for those interested in a more detailed discussion of how
- the ray/height_field intersections are found. First, when I started on
- this, I realized rather quickly that transformations were going to be a bit
- of a problem. I decided to add a field to the height field structure to
- store the transformation matrix, since it just wasn't feasible to transform
- each triangle separately, since I would then have to give up the speed and
- low memory requirements which made the height field idea appealing to me in
- the first place. So in calculating the intersection, the first step is to
- perform on the ray the inverse of the transformations which have been
- specified for the height field. Then, I check for an intersection with this
- ray and the box mentioned earlier. I could have done the check for an
- intersection with the box first, but I realized that if I do the transformation
- first, I can simplify the check for intersection with the box, since I then
- know the box's exact position and orientation. I'm not sure, but my guess
- is that the time saved by doing this simplified calculation makes up for
- the cost of transforming the ray. After that is done, if the box was
- intersected, we find the two points of intersection (if the ray origin is
- inside the box, it is considered one point of intersection), and then set
- up a digital differential analyzer (based on Bresenham's line algorithym) to
- follow the line from one point to the other. At each pixel along the way,
- we check for intersection with the ray. This routine also takes advantage
- of the fact that the ray is transformed, and as such, we know *much* more
- about the triangles that make up the pixel than we would about an arbitrary
- triangle, and again, I have used this knowlege to simplify the intersection
- test. Since we follow the line in the direction of the ray, at the first
- intersection, we stop and return true. If we traverse the entire line
- without an intersection, we return false.
-
- Now the bad news. My feeling is that the aforementioned problem with rotation
- is a result of performing the transformation first in the intersection test.
- I don't know why this would be so, so I didn't change that code (especially
- since so much of the other code relies on that transformation being done).
- I hope that this does not turn out to be the problem, since so many of the
- speed benefits rely on that. If any of you have an opinion, or even better,
- a reference to whether or not this transformation first is valid, please let
- me know. And look at the rest of the code, and see if I go astray anywhere
- which might manifest itself as a problem when rotation is used.
-
- -Doug Muir
- 8/18/1991
-