Download from
`http://www.nyangau.org/mr/rtmr.zip`

.
You may also need the
Modular Renderer and
Generalised Bitmap Module.

This ray-tracer is based on information given in "Computer Graphics, Principles and Practice", by Foley, vanDam, Feiner and Hughes, and Byte magazine.

This program replaces RT, my previous Ray Tracer. It has a superset of the functionality and uses the Modular Renderer C++ class library to get the actual tracing done.

The input file is an ASCII text file that defines surface colours and shapes etc.. Such text files may include other text files.

Also, bitmaps may be supplied for 2D texture field maps, and
`.tex`

files can be supplied for 3D texture field maps.

The recommended extension for ray-tracer files is `.rt`

, and
the Andys Editor language definition should have a reserved comment start of
"`;...`

" and end of "", since "`;`

"'s are used to
introduce comments.

The shading model is as given on p734 of "Computer Graphics, Principles and Practice", by Foley, vanDam, Feiner and Hughes.

Each surface has ambient, diffuse, specular and transmissive coefficients,
called `ka`

,`kd`

,`ks`

and `kt`

.

It also has a diffuse colour, `od`

and a specular colour
`os`

.
The specular colour is used to compute highlights with, and is usually
white-ish for plastic-like surfaces.

I = Ia * ka * od Ambient + fatt * Ip * kd * od * cos_nl Diffuse (per light) + fatt * Ip * ks * os * pow(cos_rv, phong_power) Specular (per light)

If no surface is hit, then `I = Ib`

, where `Ib`

is a
background colour.

The objects that may be traced are half-planes (planes for short), spheres, arbitrary quadratics and quartics. The solid parts are the regions below :-

Plane: ax+by+cz+d<=0 Biplane: ax+by+cz+d2<=0 and not ax+by+cz+d1<=0 2 2 2 2 Sphere: (x-a) +(y-b) +(z-c) -d <=0 2 2 2 Quadratic: ax +by +cz +dxy+eyz+fzx+gx+hy+iz+j<=0 Quartic: 35 coefficients * various powers of x,y and z <=0

Boolean combinations of the above are allowable too. Clearly planes and spheres are just special cases of the general quadratic case, when certain coefficents are 0. However, such primitives are implemented in the indicated faster ways.

The following types are understood by the tracer :-

value A numeric value string A string value, used for filenames xyz A 3 element vector, used for positions and directions rgb A 3 element colour intensity vector col A colour (rgb, but can vary dependant on position) surf A surface structure shape Any of the traceable shapes

The following functions (rvalues) are provided.
Variables are of the type value unless of the form `a_xxx`

, in
which case they are of type `xxx`

:-

radians = rad(degrees) Converts an angle in degrees to the value in radians a_xyz = xyz(x,y,z) Returns a vector with the 3 specified values a_xyz = trans_x(a_xyz,dx) Returned vector has x component dx bigger. Similar functions trans_y and trans_z also exist a_xyz = trans(a_xyz,a_xyz) Returns sum of 2 vectors. a_xyz = scale_x(a_xyz,factor) Returns a vector, scaled by factor in x direction. Similar functions scale_y and scale_z also exist a_xyz = scale(a_xyz,a_xyz_factor) Returns a vector, scaled by factor a_xyz = rot_x(a_xyz,angle) Returns a vector rotated by angle around x axis Angle is specified in radians Similar functions rot_y and rot_z exist a_xyz = rot0(a_xyz, a_xyz_axis_direction, angle) Returns a vector rotated by angle around axis from the origin in a given direction [feature not in RT, new for RTMR] a_xyz = rot(a_xyz, a_xyz_axis_origin, a_xyz_axis_direction, angle) Returns a vector rotated by angle around axis from the given origin in a given direction [feature not in RT, new for RTMR] a_rgb = rgb(red,green,blue) Makes an RGB colour value out of 3 components eg: set_col shiny_red surf(0.2,0.6,0.2,0.0, rgb(1.0,0.0,0.0), rgb(1.0,1.0,1.0), 200,1)

Note that reserved words `trans_x`

, `trans`

,
`scale_x`

, `scale`

, `rot_x`

and related
`y`

and `z`

forms here operate on vectors and return
vectors.
Later, we will see that the same words also operate on shapes to give
new shapes.
Which is mean't is easily determined from the context.

A colour is something that is defined to be a given RGB value for a given
point in space.
Since colours can be defined in terms of colours, there is conceptually a
colour 'tree'.
When the root colour is evaluated, it is passed 3 parameters
(known as `p0`

,`p1`

and `p2`

) and these are
the x,y and z of the intersection point on the surface of the shape.

a_col = col(a_rgb) Makes a colour that is the same regardless of position. a_col = col_field2d(bx,by,"fn.bmp") Makes a colour that takes the passed in p0 and p1 (ignoring p2) and uses (p0+bx,p1+by) to index a bitmap specified. On its own, such a colour can only map a bitmap across the x-y plane. With this primitive (and col_remap) bitmaps may be mapped over the surfaces of objects. eg: set_col floor_col col_field2d(0,0,"tiles.bmp") a_col = col_field3d(bx,by,bz,"fn.tex") Makes a colour that takes the passed in p0,p1, and p2 and uses (p0+bx,p1+by,p2+bz) to index the texture-map file. On its own, such a colour can only map a texture-map that is parallel to the x,y and z axes. a_col = col_interp0(a_col_arg) Perform linear interpolation of p0 argument. col_interp1 and col_interp2 also exist. eg: if p0 was 2.5, then evaluates a_col_arg with p0 at 2.0 and also at 3.0 and finds the average colour. eg: set_col floor_col col_interp0( col_interp1( col_field2d(0,0,"tiles.bmp"))) a_col = col_field2di(bx,by,"fn.bmp") equivelent to col_interp0( col_interp1( col_field2d(bx,by,"fn.bmp"))) shorter to type and traces faster too [feature not in RT, new to RTMR] a_col = col_field3di(bx,by,bz,"fn.tex") equivelent to col_interp0( col_interp1( col_interp2( col_field3d(bx,by,bz,"fn.tex")))) shorter to type and traces faster too [feature not in RT, new to RTMR] a_col = col_remap(a_xyz_base, a_xyz_v0, a_xyz_v1, a_xyz_v2,a_col_arg) The (p0,p1,p2) are taken to be cartesian xyz coordinates when this colour is evaluted. New values p0',p1' and p2' are computed where a_xyz_base + p0' * a_xyz_v0 + p1' * a_xyz_v1 + p2' + a_xyz_v2 = (p0,p1,p2). ie: (p0,p1,p2) is transformed to a new coordinate space, based at a_xyz_base in the old coordinate space, with axes given by vectors a_xyz_v0, a_xyz_v1 and a_xyz_v2. The new coordinate is passed to a_col_arg when it is evaluated. When a colour is rotated/translated (when the shape it is used by is rotated/translated, this colour is (ie: these vectors are) is adjusted to move with the shape. Similarly, when a shape is scaled, its colour is scaled too. This will cause the base and basis vectors to be scaled too. a_col = col_cyl(lond,rd,hd,a_col_arg) The (p0,p1,p2) are taken to be cartesian xyz coordinates. The x-y plane can be considered the equator. The z axis can be considered the polar axis. A new p0 is computed as the longitude / lond. A new p1 is computed as the radius / rd. A new p2 is computed as the height (= original p2) / hd. The values are passed down to a_col_arg. This is used to perform cylindrical polar texture maps. eg: set_col x col_remap(xyz(1,2,3),xyz(1,0,0),xyz(0,1,0), col_cyl(rad(1),1,1, col_mat3d(1,0,0, 0,0,1, 0,1,0, col_field2d(0,0,"360x100.bmp")))) a_col = col_sph(lond,latd,rd,a_col_arg) The (p0,p1,p2) are taken to be cartesian xyz coordinates. The x-y plane can be considered the equator. The z axis can be considered the polar axis. A new p0 is computed as the longitude / lond. A new p1 is computed as the latitude / latd. A new p2 is computed as the radius / rd. The values are passed down to a_col_arg. This is used to perform spherical polar texture maps. eg: set_col earth_col col_remap(xyz(0,0,0),xyz(1,0,0),xyz(0,1,0), col_sph(rad(1),rad(1),1, col_field2d(0,90,"360x180.bmp"))) a_col = col_nomove(a_col_arg) When objects of a given colour are translated, rotated or scaled, as well as the objects shape being moved, the objects colour is moved too. Unless this primitive is used. eg: set_col col_dep_on_scene_not_object col_nomove(a_col_arg) a_col = col_mat2d(a00,a01, a10,a11,a_col_arg) 2x2 matrix modification of (p0,p1,p2) and chain on q0 = a00 * p0 + a01 * p1 q1 = a10 * p0 + a11 * p1 Evaluate a_col_arg with q0,q1,p2 This allows you arbitrarily remap p0,p1,p2 in terms of one another. It is useful for mapping textures at obscure angles around shapes, and changing the ordering of p0 and p1 a_col = col_mat3d(a00,a01,a02, a10,a11,a12, a20,a21,a22,a_col_arg) 3x3 matrix modification of (p0,p1,p2) and chain on q0 = a00 * p0 + a01 * p1 + a02 * p2 q1 = a10 * p0 + a11 * p1 + a12 * p2 q2 = a20 * p0 + a21 * p1 + a22 * p2 Evaluate a_col_arg with q0,q1,q2 Similar uses as above. Particularly useful with col_cyl. eg: set_col a_col col_remap(xyz(0,0,0),xyz(1,0,0),xyz(0,1,0), col_cyl(rad(1),1,1, ; here p0=longitude,p1=radius,p2=height col_mat3d(1,0,0, 0,0,1, 0,1,0, ; here p0=longitude,p1=height,p2=radius col_field2d(0,90,"bitmap.bmp"))))

The structures above give the ability to map bitmaps anywhere in space - for example, on the sides of objects. Also the ability to do 3d texture mapping. Also interpolation between pixels in texture maps is optional in any or all of the directions in the texture map. Cylindrical polar and spherical polar mapping may be performed. Colour fields may move with the object when it is moved, or stay fixed against a given reference point (ie: the backdrop).

It is admitted that this is complicated, and a few worked examples are given later.

Every shape has its surfaces defined as the combination of various coefficients and ambient and diffuse colours. Phong power and refractive index also contributes to the overall effect.

a_surf = surf(ka,kd,ks,kt,a_col_od,a_col_os, phong_power,refractive_index) Makes a surface colour structure Supply ambient, diffuse, specular and transmissive coeffs. Supply diffuse and specular colours Supply phong power and refractive index a_shape = plane(a,b,c,d,a_surf) Returns a half-plane solid for ax+by+cz+d<=0 This primitive is traced quicker than a general quadratic a_shape = x_lt(x,a_surf) Returns a shape solid for x <= some x value Similar functions x_gt, y_lt, y_gt, z_lt and z_gt also exist a_shape = biplane(a,b,c,d1,d2,a_surf) Returns a half-plane solid for ax+by+cz+d1>0 and ax+by+cz+d2<=0 This primitive is traced quicker than a general quadratic, and is also traced quicker than the intersection of 2 planes. a_shape = x_in(x1,x2,a_surf) Returns a shape solid for x in x1 to x2. Similar functions y_in and z_in also exist. a_shape = sphere(r,a_surf) Returns a solid sphere, radius r, at the origin This primitive is traced quicker than a general quadratic a_shape = ellipsoid(rx,ry,rz,a_surf) Returns an ellipsoid at the origin with given radii a_shape = x_ell_cyl(ry,rz,a_surf) Returns a elliptical cylinder along the x axis with given radii Similar functions y_ell_cyl, z_ell_cyl also exist a_shape = x_cyl(r,a_surf) Returns a cylinder along the x axis with given radius Similar functions y_cyl, z_cyl also exist a_shape = x_ell_cone(ky,kz,a_surf) Returns a elliptical cone along the x axis with given gradients to the y and z axis Similar functions y_ell_cone, z_ell_cone also exist a_shape = x_cone(k,a_surf) Returns a cone along the x axis with given gradient Similar functions y_cone, z_cone also exist a_shape = quad(a,b,c,d,e,f,g,h,i,j,a_surf) Returns a general quadratic as described above If a to f are 0, a half-plane results If d to i are 0, and a=b=c sphere around the origin results In these two cases, the functions above are traced faster a_shape = x_torus(r,R,a_surf) Returns a circular torus around the x axis with given major and minor radii Similar functions y_torus and z_torus [feature not in RT, new for RTMR] a_shape = quartic(c,c,c,...,a_surf) Returns a general quartic with 35 coefficients as above coefficients specified in same order as with POV-Ray, see http://www.povray.org/documentation/view/3.6.1/298/ [feature not in RT, new for RTMR] a_shape = trans_x(a_shape,x) Returns a copy of the shape, translated by x in the x direction Similar functions trans_y and trans_z also exist a_shape = trans(a_shape,a_xyz) Returns a copy of the shape, translated by a given vector Note: trans(a_shape,xyz(x,y,z)) = trans_x(trans_y(trans_z(a_shape,z),y),x) a_shape = scale_x(a_shape,factor) Returns a copy of the shape, scaled by factor in x direction. (A sphere scaled in this way becomes a general quadratic). Similar functions scale_y and scale_z also exist a_shape = scale(a_shape,a_xyz_factor) Returns a copy of the shape, scaled in x, y and z by amounts. (A sphere scaled unequally becomes a general quadratic). (A sphere scaled equally remains a sphere). a_shape = rot_x(a_shape,angle) Returns a copy of the shape, rotated by angle about the x axis Angle is specified in radians Similar functions rot_y and rot_z exist a_shape = union(a_shape,a_shape) Returns a shape which is the CSG union of the two shapes Similar functions isect, diff, sdiff and extent exist sdiff means symmetric difference (like XOR) extent is explained later a_shape = resurf(a_shape,a_surf) Returns a shape with the same geometry as before, but with different surface characteristics

All the commands that end in '`,a_surf)`

' may have it omitted.
The tracer just surfaces the shape in a grey colour.
This feature is provided to avoid the need to specify surfaces for all the
facets (subshapes) of a shape, if you know you are going to resurface it
later anyway.

The commands supported in the language take a number of rvalues as arguments. These arguments are space seperated.

set_value a_new_value a_value Defines a variable a_new_value to be the value a_value eg: set_value door_width 34.5 set_string a_new_string a_string Defines a variable a_new_string to be the string a_string eg: set_string fn "filename.bmp" [feature not in RT, new in RTMR] set_xyz a_new_xyz a_xyz Defines a vector eg: set_xyz viewpoint xyz(1.0,3.5,10.2) set_rgb a_new_rgb a_rgb Defines a RGB colour vector eg: set_rgb yellow rgb(1,1,0) set_col a_new_col a_col Defines a new colour structure set_surf a_new_surf a_surf Defines a new surface structure eg: set_col shiny_red surf(0.2,0.6,0.2,0.0, col(rgb(1.0,0.0,0.0)), col(rgb(1.0,1.0,1.0)), 200,1) set_shape a_new_shape a_shape Defines a variable a_new_shape to be the shape a_shape a_shape is anything that evalues to a shape eg: set_shape shell diff(sphere(2,a_surf),sphere(1,a_surf))

If you have set a variable using the above, then it can itself be used as a rvalue.

eg: set_shape ball sphere(2.0,greeny_blue) set_shape earth ball

Other commands are as follows :-

set_attenuation af1 af2 Set light intensity with distance coefficents The nature of this equation and its parameter is arbitrary This function is likely to change later Defaults to 1.0 and 0.9 at present The 0.9 means that light intensity fades to 90%, per unit distance travelled. I = af1 * pow(af2,distance) set_attenuation3 af1 af2 af3 as set_attenuation, but with an extra coefficient I = af1 * pow(af2,distance*af3) it is often far easier to linearly vary af3 than to adjust af2 according to a power relationship [feature not in RT, new in RTMR] set_background a_rgb Sets background light intensity Defaults to rgb(0,0,0) at present Used when no object is struck by light ray. set_ambient a_rgb Sets ambient light intensity Defaults to rgb(0,0,0) at present eg: set_ambient shiny_red add_light a_xyz a_rgb Adds a light at specified position of given intensity No lights exist before the first add_light found Lights cannot be removed in present implementation eg: add_light xyz(1,1,1) rgb(1,1,0) render shape eye forward up hangle vangle hpixels vpixels depth rendertype "filename" Renders a view of shape View is from the eye position, looking forward The up vector used to decide which way is up hangle and vangle specify angles of viewing pyramidal volume hpixels and vpixels specify size of output bitmap extra depth to recurse to rendertype is documented in the rendertype and antialiasing section of this document "filename" is the output bitmap filename visdiff a_value Set minimum visible difference value used in anti-aliasing computation. Default is 1.0/255.0. include "filename.rt" Causes the given file to be read in and processed. Ideal for when you put an object in an .rt file and then wish to use it from another .rt file.

We have a bitmap of the surface of the earth. It is 360 x 180 pixels and pixel (180,90) is longitude 0 degrees at the equator.

set_col col1 col_field2d(180,90,"map.bmp")

When `col1`

is evaluated `p0`

and `p1`

are
indexed into the bitmap as x and y to give the colour of the earth at a
longitude of `p0`

and latitude of `p1`

.
A half-plane aligned along the x-y plane drawn in this colour would show
a map of the world.

set_col col2 col_interp0(col_interp1(col1))

When you look close-up at something drawn in colour `col1`

,
you may be able to see the pixel boundarys.
`col2`

does not suffer from this so badly, as linear interpolation
has been used to smooth the pixels in both directions.

set_col col3 col_sph(rad(1),rad(1),1,col2)

When `col3`

is evaluated `p0`

,`p1`

and
`p2`

are mapped from x,y,z coordinates to a longitude, latitude and
radius from the origin.
The longitude is divided by `rad(1)`

to give a `p0`

which is the multiple of 1 degree increments.
Similarly for the latitude for `p1`

.
The radius is divided by 1 (ignored) for `p2`

.
ie: we will chain-on to evaluate `col1`

with `p0`

equal
to the number of degrees east of Greenwich and `p1`

equal to the
number of degrees above the equator.
A sphere at the origin drawn in this colour would display a map of the world
drawn on the sphere, much like a real globe.

set_surf surf1 surf(...,col3...) ; other arguments to surf omitted for clarity set_shape shape1 sphere(1.0,surf2)

This defines a sphere, at the origin, which is drawn in a colour that is a map of the world.

set_shape shape2 trans(shape1,xyz(0.5,0.1,0.2))

This the same as `shape1`

, only the origin of the globe is
at (0.5,0.1,0.2).
The colour of this shape has also moved so that the continents of the world
still sit on the correct parts of the surface of the sphere.

set_surf surf2 surf(...,col_nomove(col3),...) set_shape shape3 sphere(1.0,surf2)

This is an awkward shape.
Although this would render identically to `shape1`

, should you
apply the `trans()`

or `rot_x()`

operator to it, only
the shape, not the 2d bitmap texture would move!

Operators such as `union(shape_a,shape_b)`

conceptually tell the
ray-tracer to intersect a ray with shapes `shape_a`

and
`shape_b`

.
This gives an intersection list which says where the ray enters and leaves
`shape_a`

, and another which says where the ray enters and leaves
`shape_b`

.
By combining these intersection lists we can deduce where the ray enters and
leaves the CSG union of shapes `shape_a`

and `shape_b`

.

In practice the ray tracer can make certain optimisations.
For example if we are tracing `isect(shape_a,shape_b)`

and the
intersection list generated for one of the shapes is empty (does not go
through anything) then the result must also be the empty set.
ie: nothing intersected with something is nothing.

This type of optimisation allows the ray-tracer to avoid calculating ray intersections for certain parts of the shape tree.

The `extent(shape_a,shape_b)`

operator only intersects with
`shape_a`

, if the intersection with `shape_b`

is non
null.
This is particularly useful when `shape_b`

is made to enclose
`shape_a`

, and `shape_a`

is difficult (complicated) to
intersect.

For example, if I had a scene with a dice (made of many primitives) in one
small part of it.
I could use `extent(shape_dice,shape_enclosing_sphere)`

to speed
up the tracing.
The assumption here is that a sphere is much quicker to calculate
intersections for than some complicated CSG shape such as a dice (which may
have 6 half-plane faces, a rounding sphere and 1+2+3+4+5+6 spheres for spots
subtracted etc!).
The other assumption is that most of the time we will not be tracing the part
of the picture with the dice in.

Rendertype is constructed by adding a "mode" value and a "projection" value. Mode controls supersampling and antialiasing. Projection controls the nature of the view (fish-eye etc.).

This ray tracer has 4 modes (or rendertypes) in which it can trace pictures :-

- Mode 0 is a simple point sample trace, which as a result can exhibit severe aliasing.
- Mode 1 is an antialiased trace, which significantly cuts down on aliasing.
Because the Whitted adaptive supersampling technique is used, the penalty
of tracing additional rays is only paid where adjacent pixels differ
notably.
Supersampling can occur to a maximum depth of 4x4.
'Notably' is a little vague, and is specified by the
`visdiff`

command. - Mode 2 is an unconditional 4x4 supersample.
- Mode 3 is unpixelate.
Unpixelate is a scheme by which the trace is done at a coarse resolution
first (one pixel in each 16x16 area), and then additional pixels are traced
until a finer granularity is acheived (one pixel in each 8x8 area), and
this progresses until all the pixels are traced.
If the bitmap being traced is visible as the trace progresses, then this
gives the user a quick peek of the final image
*very*quickly.

[Mode 2 and 3 were not in RT, and are new to RTMR]

Raytracing produces floating point intensities for each ray traced. These must be mapped to byte values to be written into the file. So the range of intensities from 0.0 to 1.0 map to byte values 0 to 255.

Therefore the minimum visible difference is 1.0/255.0.
This is the default `visdiff`

value.

However, if the 2 least significant bits of a byte are not required to be
significant, then the `visdiff`

value can be raised to 1.0/63.0.
If the final output file is to be displayed on VGA hardware, this would be
ideal.

Raising the `visdiff`

value reduces the number of extra rays
that are traced due to adjacent samples being different colours, because
fewer adjacent will exceed the higher `visdiff`

difference value.
One testcard requires 13x the basic cost to trace with the default
`visdiff`

, but only 6x with the higher `visdiff`

value above.

The projection value is one of :-

- 0 = normal
- 10 = Escher tallangle (see 'Above and Below')
- 20 = wideangle
- 30 = fisheye

[Projection values 20 and 30 were not in RT and are new to RTMR].

By adding in 10 to the rendertype described above it is possible to tell the Ray Tracer to trace in the wide (or should I say tall) angle style of projection that M.C.Escher did in his "Above and Below" and "House of Stairs" woodcuts.

I unconditionally admit that my CSG refraction code is a hack.

In a CSG system, a point in space is empty or solid. To model refraction effectively a point in space is empty (a transmissive medium with a refractive index) or solid (non-transmissive medium). This means we should have many classifications of points in space.

It is difficult to define boolean CSG operations on multivalue types. eg: what does it mean if a medium of index 1.5 intersects a medium of index 1.3?

This program implements refraction by making a set of assumptions. Firstly, it is assumed that the refractive index of empty space is 1.0. Certain solid objects can be considered to have other indexes.

All the surfaces that define the boundary of a transmissive shape should have a non-zero transmissive coefficient (ideally the same). This states how much of the light from the other side of the shape is to be allowed through. If the coefficients are not the same, you can produce shapes that look brighter from some directions.

Also the surfaces should all have a refractive index set (ideally the same). This refractive index is used to bend rays into the shape, and out again. If the refractive indexes are not the same, you can produce shapes that have different optical properties when viewed from certain directions. This actually is contradictory to the physics being modelled!

When a ray strikes a transmissive surface the following additional work is
done.
If total external reflection occurs then an additional reflected ray is
spawned (this is unlikely, as it implies the tranmissive solid has a
refractive index <1.0, and most things don't - glass=1.5, water=1.3 etc.).
Otherwise a refracted ray is sent through the solid until it emerges at the
far side.
If total internal reflection occurs at the far side, the ray is
bounced back into the object again.
Eventually the ray is refracted out into empty space.
The emerging rays intensity is of course, scaled by the additional distance
spent going through the solid object, and by `kt`

of each surface
the ray enters the shape by.

There is a limit to the number of internal bounces allowed due to total internal reflection. This limit is around 10 bounces. If this limit is exceeded the refraction computation is aborted.

Due to the way CSG systems merge shapes into one compound shape, certain problems can be produced. For example, if you place a large glass slab onto a smaller red block, you will find that the side faces of the red block, seen from above through the glass block are red, but the top of the red block is not!

Why? Well the the two blocks may actually touch to form a composite CSG object. In the first case, the ray enters the composite object through a glass surface, leaves the solid via a glass surface, and strikes a red side surface. In the other case, the ray enters through a glass surface, but leaves through the bottom red surface of the composite CSG shape. The colour returned is whatever is beneath the red block. ie: in the diagram, there is no surface defined at point X to give the expected view of the red top of the block below the glass block.

ray 1 ray 2 \ | gggggggg\ggggggggg|gggggggggggg g \ | g <--- Glass block gggggggggg\ggg X gggggggg \ r | r \r | r <--- Red block rrrrr|rrrrr | ooooooooo <--- Other shape

The simple workaround to this problem is to ensure that the glass block is just the tiniest amount above the red block, so that the rays leave the glass before hitting the red block.

The code will always write out 24 bit bitmaps. When reading, (for texture maps etc.) 1,4,8 or 24 bit bitmaps can be used.

This program utilises the services of the Bitmap Rosetta Stone, the Generalised Bitmap Module (or GBM for short).

GBM supports the reading and writing of a large number of bitmap file formats in a manner that isolates the calling program (RT) from the format details.

The bitmap bits both read and written are written with value proportional to physical brightness. This means that for OS/2 PM for example, these should be mapped to/from the L* cyclometric colour space. Similarly, when displaying the bitmaps on a specific monitor directly, the pixels should be mapped to a gamma corrected colour space designed to counteract the monitors gamma. In reality these mappings are often skipped, as the results are very similar.

In the absence of a readily available 3D texture map format, I invented one. The 3D texture map file format is inspired by the OS/2 2.0 bitmap formats :-

typedef unsigned char byte; /* 8 bit number */ typedef unsigned long dword; /* Intel ordered 32 bit number */ typedef struct { byte b, g, r, dummy; } PAL; /* Palette entry */ typedef struct { byte i[(width*bits_per_voxel+31)/32*4]; } SCAN; typedef struct { dword 0x1a584554; /* Magic number (little endian) */ dword width, height, depth; /* x,y and z dimensions */ dword bits_per_voxel; /* 1, 4, 8 or 24 only */ PAL pals[(1<<bits_per_voxel)&0x1ff]; SCAN scans[depth][height]; } TEXFILE;

The expression inside the [] in the pals array converts 1 -> 2,
4 -> 16, 8 -> 256 and 24 -> 0, ie: it converts the
`bits_per_voxel`

into number of palette entrys present.

The expression inside the SCAN definition ensures each scanline is padded to the next biggest 32 bit boundary.

Scan lines are stored leftmost pixel to rightmost, and they are stored one after another starting at lowest to highest. Finally complete 2D images are stored one after each other lowest depth first.

Simply run RTMR passing the `.rt`

file as an argument.
eg: to trace a die :-

rtmr die.rt

Versions of the executable called RTMRWIN and RTMRXWIN exist on Windows and UNIX which will show what they are tracing as they trace it to a Window. When used with the unpixelate rendertype, you can get a very early feel for whether the trace will come out right.

The full usage is as follows :-

usage: rtmr {-v var=val} {-s var=val} {script.rt} flags: -v var=val set_value var val -s var=val set_string var val script.rt input raytracer file(s)

The `-v`

switch pre-defines values, in the same way that
`set_value var val`

would within the `.rt`

file.
`-s`

does the same for strings.
This allows you to do things like this (example suitable for
`ksh`

or `bash`

) :-

for angle in 000 010 020 030 040 050 060 070 080 ; do rtmr -v angle=$angle -s fn=scene/scene$angle.tif scene.rt done

[The `-v`

and `-s`

arguments were not present
in RT and are improvements added to RTMR].

I use `die.rt`

as a rough benchmark for relative system
performance.
It traces the same die, at 512x512, using all 4 rendertypes.

Machine | No antialiasing | Whitted | Supersample | Unpixelate |
---|---|---|---|---|

Generic PC
2.4GHz Core 2 Duo 3GB RAM 64 bit Fedora Core 6 Linux | 0s | 1s | 4s | 0s |

Apple iPod Touch
412MHz ARM 128MB RAM MacOSX Mobile | 7s | 15s | 93s | 8s |

I wrote all this code in my own time on my own equiptment. I hereby place all this code into the public domain. Feel free to do whatever you like with it. No copyright / no royalties / no guarantees / no problem. Caveat Emptor.

This documentation is written and maintained by the RTMR author, Andy Key andy.z.key@googlemail.com