This program was written by: David Buck 22C Sonnet Cres. Nepean, Ontario Canada, K2H 8W7 It has been released to the public domain for free distribution. I would like to place the following conditions on the use of this program: 1) that it should not be used as part of any commercial package without my explicit written consent. 2) if you make any neat and interesting pictures, please send them to me. 3) If you make any changes to the source code, please let me know. I'd like to see what you've done. 4) This text file should accompany should accompany the program I can be reached on the following BBS'es Ottawa Online (613) 526-4141 OMX (613) 731-3419 This program is a ray tracer written completely in C. It supports arbitrary quadric surfaces (spheres, ellipsoids, cones, cylinders, planes, etc.), constructive solid geometry, and various shading models (reflection, refraction, marble, wood, etc.) In order to create pictures with this program, you must describe the objects in the world. This description is a text file usually called "Object.data". Normally, such files are difficult to write and to read. In order to make this task easier, the program contains a two-pass parser to read the data file. It allows the user to easily create complex worlds from simple components. Since the parser allows include files, the user may put the object descriptions into different files and combine them all into one final image. This manual is divided into two main sections. The first section describes the command-line parameters for the program. The second section describes the syntax and semantics of the description language. Some sample worlds and their corresponding images are provided on the disk. Section 1 - Command Line Parameters This program is designed to be run from the CLI, although it can be run from the Workbench if desired. From the CLI, the program accepts various parameters: -wxxx width of the picture in pixels -hxxx height of the picture in pixels +f produce an output file (see below) -f don't produce an output file +d display the picture while tracing -d don't display the picture while tracing +p prompt for CR before completing -p finish without prompting for CR -ifilename set the input filename -ofilename set output filename +axxx anti-alias - xxx is a tolerance level -a don't anti-alias +v verbose option - print out the scan line number. -v disable verbose option If the +f option is used, the ray tracer will produce an output file of the picture. This output file describes each pixel with 24 bits (8 bits for red, 8 for green, and 8 for blue). A post processor called "DumpToIFF" can convert this format to hi-res HAM format (320 x 400) making reasonable choices for the colour registers. For compatability, the format of the dump file is the same as the format for the QRT ray tracer. If the +d option is used, then a hi-res HAM image of the picture will be produced while the program performs the ray tracing. This picture is not as good as the one created by "DumpToIFF" because it does not try to make good choices for the colour registers. The +p option makes the program wait for a carriage return before exitting (and closing the graphics screen). This gives you time to look at the final picture before destroying it. If your input file is not "Object.data", then you can use -i to set the filename. The default output filename is "data.dis". If you want a different output file name, use the -o option. The +a option enables adaptive anti-aliasing. The number after the +a option determines the threshold for anti-aliasing. If the colour of a pixel differs from its neighbor (to the left or below) by more than the threshold, then the pixel is subdivided and super-sampled. The samples are jittered to introduce noise and make the pictures look better. If the anti-aliasing threshold is 0.0, then every pixel is supersampled. If the threshold is 1.0, then no anti-aliasing is done. Good values seem to be around 0.2 to 0.4. You may specify the default parameters by creating a file called "trace.defaults" which contains the parameters in the above format. Section 2 - The Object Description Language The Object Description Language allows the user to describe the world in a readable and convenient way. The language delimits comments by the left and right braces ({ and }). Nested comments are allowed, but no sane person uses them, right? The language allows include files to be specified by placing the line: INCLUDE "filename" at any point in the input file. (Include files may include other files). Section 2.1 - The Basic Data Types There are several basic types of data: Float Floats are represented by an optional sign (+ or -), some digits, an optional decimal point, and more digits. It does not support the "e" notation for exponents. The following are valid floats: 1.0 -2.0 -4 +34 Vector Vectors are arrays of three floats. They are bracketed by angle brackets ( < and > ). < 1.0 3.2 -5.4578 > Colour A colour consists of a red component, a green component, and a blue component. All three components are floats in the range 0.0 to 1.0. The syntax for Colours is the word "COLOUR" followed by any or all of the RED, GREEN, or BLUE components in any order. For example: COLOUR RED 1.0 GREEN 1.0 BLUE 1.0 COLOUR BLUE 0.56 COLOUR GREEN 0.45 RED 0.3 For those people who spell "Colour" the American way as "Color", the program also accepts "COLOR" as an equivalent to "COLOUR". COLOUR_MAP For wood and marble texturing, the user may specify arbitrary colours to use for the texture. This is done by a colour map. When the object is being textured. a number between 0.0 and 1.0 is generated which is then used to determine the colour of the point. A Colour map can specify the mapping used to change these numbers into colours. The syntax is as follows: COLOUR_MAP [start_value end_value colour1 colour2] [start_value end_value colour1 colour2] ... END_COLOUR_MAP The value is located in the colour map and the final colour is calculated as a linear interpolated between the two colours in the located range. Section 2.2 - The More Complex Data Types The data types used to describe the objects in the world are a bit more difficult to describe. To make this task easier, the program allows users to describe these types in two ways. The first way is to define it from first principles specifying all of the required parameters. The second way allows the user to define an object as a modification of another object. (The other object is usually defined from first principles but is much simpler). Here's how it works. You can use the term DECLARE to declare a type of object with a certain description. The object is not included in the world but it can be used as a prototype for defining other objects. An example would help. Viewpoint The viewpoint tells the ray tracer the location and orientation of the camera. The viewpoint is described by four vectors - Location, Direction, Up, and Right. Location determines where the camera is located. Direction determines the direction that the camera is pointed. Up determines the "up" direction of the camera. Right determines the direction to the right of the camera. A first principles declaration of a viewpoint would look like this: VIEWPOINT LOCATION < 0.0 0.0 0.0> DIRECTION < 0.0 0.0 1.0> UP < 0.0 1.0 0.0 > RIGHT < 1.0 0.0 0.0> END_VIEWPOINT This format becomes cumbersome, however, because the vectors must be calculated by hand. This is especially difficult when the vectors are not lined up with the X, Y, and Z axes as they are in the above example. To make it easier to define the viewpoint, you can define one viewpoint, then modify the description. For example, VIEWPOINT LOCATION < 0.0 0.0 0.0> DIRECTION < 0.0 0.0 1.0> UP < 0.0 1.0 0.0 > RIGHT < 1.0 0.0 0.0 > TRANSLATE < 5.0 3.0 4.0 > ROTATE < 30.0 60.0 30.0 > END_VIEWPOINT In this example, the viewpoint is created, then translated to another point in space and rotated by 30 degrees about the X axis, 60 degrees about the Y axis, and 30 degrees about the Z axis. Shapes Shapes describe the shape of an object without mentioning any surface characteristics like colour, lighting and reflectivity. The most common shape used by this system is called a Quadric Surface. Quadric Surfaces can produce shapes like spheres, cones, and cylinders. The easiest way to define these shapes is to include the file "BasicShapes.data" into your program and to transform these shapes (using TRANSLATE, ROTATE, and SCALE) into the ones you want. To be complete, however, I will describe the mathematical principles behind quadric surfaces. Those who are not interested in the mathematical details can skip to the next section. A quadric surface is a surface in three dimensions which satisfies the following equation: A y**2 + B y**2 + C z**2 + D xy + E xz + F yz + G x + H y + I z + J = 0 Different values of A,B,C,...J will give different shapes. So, if you take any three dimensional point and use its x, y, and z coordinates in the above equation, the answer will be 0 if the point is on the surface of the object. The answer will be negative if the point is inside the object and positive if the point is outside the object. Here are some examples: X**2 + Y**2 + Z**2 - 1 = 0 Sphere X**2 + Y**2 - 1 = 0 Cylinder along the Z axis X**2 + Y**2 + Z = 0 Cone along the Z axis General quadric surfaces can be defined as follows: QUADRIC < A B C > < D E F > < G H I > J END_QUADRIC Section 2.3 - Quadric surfaces the easy way The file "BasicShapes.data" already includes the definitions of many quadric surfaces. They are centered about the origin (0,0,0) and have a radius of 1. To use them, you can define shapes as follows: INCLUDE "BasicShapes.data" QUADRIC Cylinder_X SCALE < 50.0 50.0 50.0 > ROTATE < 30.0 10.0 45.0 > TRANSLATE < 2.0 5.0 6.0 > END_QUADRIC You may have as many transformation lines (scale, rotate, and translate) as you like in any order. Usually, however, it's easiest to do a scale first, one or more rotations, then finally a translation. Otherwise, the results may not be what you expect. (The transformations always transform the object about the origin. If you have a sphere at the origin and you translate it then rotate it, the rotation will spin the sphere around the origin like planets about the sun). Section 2.4 - Constructive Solid Geometry This ray tracer supports Constructive Solid Geometry in order to make the object definition abilities more powerful. Constructive Solid Geometry allows you to define shapes which are the union, intersection, or difference of other shapes. Unions superimpose the two shapes. This has the same effect as defining two separate objects, but is simpler to create. Intersections define the space where the two surfaces meet. Differences allow you to cut one object out of another. Intersections, Unions, and Differences can consist of several shapes. They are defined as follows: OBJECT INTERSECTION QUADRIC ... END_QUADRIC QUADRIC ... END_QUADRIC QUADRIC ... END_QUADRIC END_INTERSECTION ... END_OBJECT UNION or DIFFERENCE may be used instead of INTERSECTION. The order of the shapes doesn't matter except for the DIFFERENCE shapes. For differences, the last shape is visible and the remaining shapes are cut out of the last. Constructive solid geometry shapes may be translated, rotated, or scaled in the same way as a Quadric surface. The quadric surfaces making up the CSG object may be translated, rotated, and scaled as well. Section 2.5 - Objects There is more to defining an object than just its shape. You must tell the ray tracer about the properties of the surface like colour, reflectivity, refractivity, the index of refraction, and so on. To do this, you must define Objects. A typical object definition looks something like this: OBJECT QUADRIC Sphere TRANSLATE < 40.0 40.0 60.0 > END_QUADRIC AMBIENT 0.3 DIFFUSE 0.7 REFLECTION 0.3 REFRACTION 0.3 IOR 1.05 COLOUR GREEN 1.0 BLUE 1.0 END_OBJECT The following keywords may be used when defining objects: AMBIENT value - Ambient light is light that is scattered everywhere in the room. An object lit only by ambient light will appear to have the same brightness over the entire surface. The default value is 0.3. The value can range from 0.0 to 1.0. DIFFUSE value - Diffuse light is light coming from a light source that is scattered in all directions. An object lit only by diffuse light looks like a rubber ball with a spot light shining on it. The value can range from 0.0 to 1.0. REFLECTION value - By setting the reflection value to be non-zero, you can give the object a mirrored finish. It will reflect all other objects in the room. The value can range from 0.0 to 1.0. REFRACTION value - By setting the refraction value to be non-zero, the object is made transparent. Light will be refracted through the object like a lens. The value can be set between 0.0 and 1.0. IOR value - If the object is refracting light, then the IOR or Index of Refraction should be set. This determines how dense the object is. A value of 1.0 will give no refraction. Water is 1.33, glass is 1.5, and diamond is 2.4. BRILLIANCE value - Objects can be made to appear more metallic by increasing their brilliance. The default value is 1.0. Higher values from 3.0 to about 10.0 can give a more metallic appearance. COLOUR value - The colour of an object can be set by using this option. The value is a colour or a colour constant. For example, COLOUR RED 1.0 BLUE 0.4 or DECLARE Yellow = COLOUR RED 1.0 GREEN 1.0 ... COLOUR Yellow TRANSLATE vector ROTATE vector SCALE vector - Objects can be translated, rotated, and scaled just like surfaces. This feature is included for consistency. LIGHT_SOURCE - If the LIGHT_SOURCE keyword is used in the definition of an object, then the object is included in the list of light sources. It can light objects and produce shadows. (You should also specify the COLOUR of the light source). TEXTURE - The texture feature is an experiment into functionally based modelling. This feature allows you to assign more interesting colouring schemes to objects. For example, you can make some object look like wood or marble. The syntax is as follows: TEXTURE WOOD TURBULENCE 0.2 TRANSLATE < 1.0 2.0 3.0 > ROTATE < 0.0 10.0 40.0 > SCALE < 10.0 10.0 10.0 > END_TEXTURE The transformations are optional. They allow you to transform the texture independent of the object itself. If you are doing animation, then the transformations should be the same as the object transformations so that the texture follows the object. Instead of using WOOD, you may use MARBLE, BOZO, or CHECKER. The WOOD and MARBLE textures are perturbed by a turbulence function. This makes them look more random and irregular than they would normally appear. The amount of turbulence can be changed by the TURBULENCE keyword followed by a number. Values from 0.1 to 0.3 seem to give the best results. The default is 0.3. CHECKER texturing gives a checker-board appearance. This option works best on planes. When using the CHECKER texturing, you must specify two colours immediately following the word CHECKER. These colours are the colours of alternate squares in the checker pattern. For WOOD, MARBLE, and BOZO textures, you may change the colouring scheme by using a colour map. This map allows you to convert numbers from 0.0 to 1.0 (which are generated by the ray tracer) into ranges of colours. For example, the default BOZO colouring can be specified by: TEXTURE BOZO COLOUR_MAP [0.0 0.4 COLOUR White COLOUR White] [0.4 0.6 COLOUR Green COLOUR Green] [0.6 0.8 COLOUR Blue COLOUR Blue] [0.8 1.0 COLOUR Red COLOUR Red] END_COLOUR_MAP END_TEXTURE Another option which can be used in conjunction with the above textures is RIPPLES. This option makes the surface look like ripples of water. The RIPPLES option requires a value to determine how deep the ripples are: TEXTURE WOOD RIPPLES 0.3 TRANSLATE < 1.0 2.0 3.0 > ROTATE < 0.0 10.0 40.0 > SCALE < 10.0 10.0 10.0 > END_TEXTURE (In this case, the WOOD, MARBLE, or BOZO keywords are optional). If a different colouring is specified (WOOD, MARBLE, or BOZO), then the COLOUR parameter is ignored (except for light sources where it gives the light colour). Another option that you may want to experiment with is called WAVES. This works in a similar way to RIPPLES except that it makes waves with different frequencies. The effect is to make waves look more like deep ocean waves. (I haven't done much testing on WAVES, so I can't guarantee that it works). Both WAVES and RIPPLES respond to a texturing option called PHASE. The PHASE option allows you to create animations in which the water seems to move. This is done by making the PHASE increment slowly between frames. The range from 0.0 to 1.0 gives one complete cycle of a wave. Section 2.6 - Composite Objects Often it's useful to combine several objects together to act as a whole. A car, for example, consists of wheels, doors, a roof, etc. A composite object allows you to combine all of these pieces into one object. This has two advantages. It makes it easier to move the object as a whole and it allows you to speed up the ray tracing by defining bounding shapes that contain the objects. (Rays are first tested to see if they intersect the bounding shape. If not, the entire composite object is ignored). Composite objects are defined as follows: COMPOSITE OBJECT ... END_OBJECT OBJECT ... END_OBJECT ... SCALE < 2.0 2.0 2.0 > ROTATE < 30.0 45.0 160.0 > TRANSLATE < 100.0 20.0 40.0 > END_COMPOSITE Composite objects can contain other composite objects as well as regular objects. Composite objects cannot be light sources (although any number of their components can). Section 3 - Showing the final pictures When the ray tracer draws the picture on the screen, it does not make good choices for the colour registers. A post-processor has been provided which scans the picture and chooses the best possible colour registers. It then redisplays the picture. DumpToIFF can optionally save this picture in IFF format. The syntax for the post-processor is: show filename where the filename is the one given in the -o parameter of the ray tracer. If you didn't specify the -o option, then use: show data.dis If you want to save to an IFF file, then put the name of the IFF file after the name of the data file: show data.dis picture This will create a file called "picture" which contains the IFF image. Section 4 - Handy Hints - To see a quick version of your picture, use -w64 -h80 as command line parameters. This displays the picture in a small rectangle so that you can see how it will look. - When translating light sources, translate the OBJECT not the QUADRIC surface. The light source uses the center of the object as the origin of the light. - When animating objects with solid textures, the textures must move with the object. - You can declare constants for most of the data types in the program including floats and vectors. By combining this with INCLUDE files, you can easily separate the parameters for an animation into a separate file. - The amount of ambient light plus diffuse light should be less than or equal to 1.0. The program accepts any value, but may produce strange results. - When using ripples, don't make the ripples too deep or you may get strange results. - Wood textures usually look better when they are scaled to diffent values in x, y, and z, and rotated to a different angle. - You can sort of dither a colour by placing a floating point number into the texture record: TEXTURE 0.05 END_TEXTURE This adds a small random value to the intensity of the diffuse light on the object. Don't make the number too big or you may get strange results. - You can compensate for non-square aspect ratios on the monitors by making the RIGHT vector in the VIEWPOINT longer or shorter. A good value for the Amiga is about 1.333. - By making the DIRECTION vector in the VIEWPOINT longer, you can achieve the effect of a zoom lens. Section 5 - Concluding remarks I'm sure that there are bugs in the code somewhere, but I've done my best to remove all the bugs I could find. I also think that the object description language needs to be re-worked. Its current syntax is a bit cumbersome. The system could also use a good graphical interface :-). I would like to thank Rick Mallett from Carleton University for his help in testing this program, for his comments and suggestions, and for creating the data file for ROMAN.DATA - awesome!