home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 5
/
DATAFILE_PDCD5.iso
/
utilities
/
p
/
povray
/
Docs
/
hfield
< prev
next >
Wrap
Text File
|
1991-09-17
|
7KB
|
107 lines
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