kyriazis@rpics (George Kyriazis) (09/07/88)
Since I had too many requests for my ray tracer, I am posting it here. Hope that will help people that can't get mail to me. I finally had the source put so people can read it, but it's not definite that it'll stay where it is. Right now it is on life.pawl.rpi.edu (128.113.10.2) in pub/ray. Can you please comment back on it ? Thanks. So, here it is: # This is a shell archive. Remove anything before this line, # then unpack it by saving it in a file and typing "sh file". # # Wrapped by pawl23.pawl.rpi.edu!kyriazis on Thu Sep 1 22:08:36 EDT 1988 # Contents: README Makefile ray.h vector.h bg.c initialize.c intersect.c # main.c readfile.c shade.c trace.c vector.c echo x - README sed 's/^@//' > "README" <<'@//E*O*F README//' Here is a simple ray tracing program developed here at RPI. It incorporates shadows, reflections and refraction together with one non-directional light source. The light source can diffuse on the surfaces and can also give specular reflections. The illumination model used is by Phong. The only objects supported right now are spheres, but the data structure can be easily expanded to incorporate more objects. Here is a list of the files, and what does each file contain: bg.c: bgcolor() evaluates the background color for a given ray. initialize.c: initialize() does some useful setup. intersect.c: sphere() Intersection routine with a sphere. intersect() Main intersection routine (calls sphere() ). main.c: main() Main body of the program. readfile.c: readfile() Reads in the input data. shade.c: shadow() Calculates the existance of a shadow ray. reflect() Find the reflection vector. refract() Find the refraction vector. shade() Calculate Phong's shading function. trace.c: trace() Trace a single ray. raytrace() Ray trace the whole picture. vector.c: vadd() vector addition. vsub() vector subtraction. vneg() vector negation. svproduct() scalar - vector product. vdot() dot product. vcross() cross product. norm() normalize a vector. ray.h: Include file for every file used in the raytracer. vector.h: Include file for every file using vectors. The ray tracer is written so it can be easily understood (at least that version), and it is fully commented. Nevertheless, probably it won't be understood by a newcomer. The format of the input file is as follows: light nos x y z r [ambient] [diff] [spec] refl r g b refr r g b width index where: light x y z components of the light source. nos num,ber of spheres [ambient] r g b components of ambient [diff] r g b components of diffuse [spec] r g b components of specular refl r g b reflection ratio and color of the reflection refr r g b refraction ratio and color of the refraction width specular width exponent index index of refraction The format of the output file is simple. In the beginning there are 2 integers (that can be read with fread() on a SUN) showing xsize and ysize of the picture. After that follow the pixels in scan-line order. Each pixel uses 3 bytes (one for red, green and blue), totalling 16777216 colors. You can change the format of that file to tailor your needs. It can be done easily by changing the funcion raytrace() in file trace.c Can you please inform me with any bugs that the program might have or any features that you want the upcoming versions to have. This software was written by me, and the subsequent version will probably by produced by other members of the RPI chapter of the ACM. Good luck! George Kyriazis kyriazis@turing.cs.rpi.edu @//E*O*F README// chmod u=rw,g=rw,o=rw README echo x - Makefile sed 's/^@//' > "Makefile" <<'@//E*O*F Makefile//' SRC=vector.c readfile.c main.c trace.c intersect.c initialize.c shade.c bg.c OBJ=vector.o readfile.o main.o trace.o intersect.o initialize.o shade.o bg.o HDR=ray.h CFLAGS=-O2 ray: $(OBJ) cc $(CFLAGS) $(OBJ) -o ray -lm clean: rm -f ray $(OBJ) $(OBJ): $(HDR) @//E*O*F Makefile// chmod u=rw,g=rw,o=rw Makefile echo x - ray.h sed 's/^@//' > "ray.h" <<'@//E*O*F ray.h//' /* * include file for the ray-tracer */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #ifndef NULL #define NULL 0 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE !FALSE #endif #define MAXLEVEL 3 /* maximum recursion level */ struct vector {double x,y,z;}; /* the color structure */ struct color { double r,g,b; }; /* the object structure */ struct obj { struct vector center; double radius; double reflection; /* precentage reflection */ double refraction; /* percentage refraction */ struct color refl_color; /* reflective color */ struct color refr_color; /* refractive color */ struct color ambient; /* ambient color */ struct color diffuse; /* diffuse color */ struct color specular; /* specular color */ double width; /* specular width factor */ double index; /* index of refraction */ }; /* light source */ struct vector light; /* number of spheres */ int noo; /* the array of spheres */ struct obj *obj; /* resolution */ int xres, yres; /* the ray structure */ struct ray { struct vector pos; /* origin */ struct vector dir; /* direction */ struct obj *obj; /* where the ray comes from */ }; /* the intersection structure */ struct intersect { struct obj *obj; /* which object */ double t; /* where in the ray */ struct vector n; /* the normal at that point */ }; /* some statistics variables */ int raycount; /* total number of rays */ int shadowcount; /* total number of shadow rays traced */ int reflectcount; /* total number of reflected rays */ int refractcount; /* total number of refracted rays */ int intersectcount; /* total number of object intersections */ int objtestcount; /* total number of intersection tests */ int rayline; /* rays / line */ int shadowline; /* shadow rays / line */ int reflectline; /* reflected rays / line */ int refractline; /* refracted rays / line */ int intersectline; /* object intersections / line */ int objtestline; /* object intersection tests / line */ @//E*O*F ray.h// chmod u=rw,g=rw,o=rw ray.h echo x - vector.h sed 's/^@//' > "vector.h" <<'@//E*O*F vector.h//' /* * definitions for the vector routines used in other files */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ struct vector vadd(); struct vector vsub(); struct vector vneg(); struct vector svproduct(); double vdot(); struct vector vcross(); struct vector norm(); @//E*O*F vector.h// chmod u=rw,g=rw,o=rw vector.h echo x - bg.c sed 's/^@//' > "bg.c" <<'@//E*O*F bg.c//' /* * the background color. * can be changed to have any kind of background texturing. (hint hint) */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM. */ #include "ray.h" struct color bgcolor(r) struct ray r; { struct color c; /* if it is above the y-axis just give a grey value */ /* if below give a gradually whiter color */ if(r.dir.y > 0) c.r = c.g = c.b = 0.2; else c.r = c.g = c.b = 0.2 - 0.8 * r.dir.y; return c; } @//E*O*F bg.c// chmod u=rw,g=rw,o=rw bg.c echo x - initialize.c sed 's/^@//' > "initialize.c" <<'@//E*O*F initialize.c//' /* * just to initialize some stuff */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "ray.h" initialize() { /* for the moment just initialize the statistics */ raycount = rayline = 0; shadowcount = shadowline = 0; reflectcount = reflectline = 0; refractcount = refractline = 0; intersectcount = intersectline = 0; objtestcount = objtestline = 0; } @//E*O*F initialize.c// chmod u=rw,g=rw,o=rw initialize.c echo x - intersect.c sed 's/^@//' > "intersect.c" <<'@//E*O*F intersect.c//' /* * Intersection routines */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "vector.h" #include "ray.h" #include <math.h> /* * Intersect ray with sphere */ struct intersect sphere(obj, r) struct obj *obj; struct ray r; { struct vector v; struct vector n; /* normal vector */ struct intersect i; double b, c, d; double sol1, sol2; i.obj = NULL; v = vsub( r.pos, obj->center ); b = 2 * vdot( r.dir, v ); c = vdot(v, v) - obj->radius; d = b * b - 4 * c; if( d < 0 ) return i; d = sqrt(d); sol1 = ( -b + d ) / 2; sol2 = ( -b - d ) / 2; if( sol1 <= 0 ) sol1 = sol2; if( sol2 <= 0 ) sol2 = sol1; i.t = (sol1 < sol2) ? sol1 : sol2 ; /* if intersection is behind eye */ if(i.t <= 0) return i; i.obj = obj; /* calculate the normal. It is just the direction of the radius */ n = vsub( vadd( r.pos, svproduct(i.t, r.dir) ), obj->center); i.n = norm(n); return i; } struct intersect intersect(r) struct ray r; { int i; struct intersect inter, intermin; intermin.obj = NULL; for(i = 0; i < noo; i++) { objtestline++; inter = sphere( &obj[i], r); /* update the minimum intersection distance if the new intersection */ /* exists (ray intersects the object), and the intersection distance */ /* is smaller that the one logged and also the object intersected is */ /* not the object that the ray is originating from. */ if( inter.obj && ( !intermin.obj || (inter.t < intermin.t) ) && ( inter.obj != r.obj ) ) intermin = inter; } return intermin; } @//E*O*F intersect.c// chmod u=rw,g=rw,o=rw intersect.c echo x - main.c sed 's/^@//' > "main.c" <<'@//E*O*F main.c//' /* * main subroutine call for the ray-tracer * Use: % ray scenename resolution */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "ray.h" #include <stdio.h> main(argc, argv) int argc; char **argv; { if(argc != 4) { fprintf(stderr,"Usage: %s scenename resolution outfile\n", argv[0]); exit(1); } xres = yres = atoi(argv[2]); readfile(argv[1]); initialize(); raytrace(argv[3]); } @//E*O*F main.c// chmod u=rw,g=rw,o=rw main.c echo x - readfile.c sed 's/^@//' > "readfile.c" <<'@//E*O*F readfile.c//' /* * read the input file of the ray-tracer */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include <stdio.h> #include "ray.h" readfile(fname) char *fname; { FILE *f; int i; f = fopen(fname,"r"); if(f == NULL) { perror("fopen"); exit(1); } /* file format is: * <x y z> light source position * number or spheres * <x y z r [ambient] [diff] [spec] refl r g b refr r g b width index > * for every sphere */ fscanf(f, "%lf %lf %lf", &light.x, &light.y, &light.z); fscanf(f, "%d", &noo); obj = (struct obj *)malloc(noo * sizeof(struct obj) ); if(obj == NULL) { perror("malloc"); exit(1); } for(i = 0; i < noo; i++) { fscanf(f, "%lf %lf %lf %lf", &obj[i].center.x, &obj[i].center.y, &obj[i].center.z, &obj[i].radius ); fscanf(f, "%lf %lf %lf", &obj[i].ambient.r, &obj[i].ambient.g, &obj[i].ambient.b); fscanf(f, "%lf %lf %lf", &obj[i].diffuse.r, &obj[i].diffuse.g, &obj[i].diffuse.b); fscanf(f, "%lf %lf %lf", &obj[i].specular.r, &obj[i].specular.g, &obj[i].specular.b); fscanf(f, "%lf %lf %lf %lf", &obj[i].reflection, &obj[i].refl_color.r, &obj[i].refl_color.g, &obj[i].refl_color.b); fscanf(f, "%lf %lf %lf %lf", &obj[i].refraction, &obj[i].refr_color.r, &obj[i].refr_color.g, &obj[i].refr_color.b); fscanf(f, "%lf %lf", &obj[i].width, &obj[i].index); } } @//E*O*F readfile.c// chmod u=rw,g=rw,o=rw readfile.c echo x - shade.c sed 's/^@//' > "shade.c" <<'@//E*O*F shade.c//' /* * here is the shading function */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "ray.h" #include "vector.h" #include <math.h> struct color trace(); struct intersect intersect(); /* * Shadow check. Check if the ray from the intersection point to the * light, intersection any objects inbetween. If it does, we have a * shadow. * One improvement that can be made is to check the transparencies of all * the intersecting objects and make the shadow have a different intensity * depending on the transparency of the objects */ int shadow(r) struct ray r; { struct intersect i; /* to have a shadow, the light ray must intersect an object */ i = intersect(r); if( i.obj == NULL ) return FALSE; else return (r.obj != i.obj); } /* * calculate the reflection vector. * D. Rogers: "Procedural elements for computer graphics". 5-12. p. 367 */ struct vector reflect(n,v1) struct vector n, v1; { struct vector v2; v2 = vsub( v1, svproduct( 2 * vdot(n, v1), n) ); return v2; } /* * calculate the refracted vector. * D. Rogers: "Procedural elements for computer graphics". 5-12. p. 367 */ struct vector refract(n, v1, index) struct vector n, v1; double index; { double p, t; struct vector v2; v2.x = 0.; v2.y = 0.; v2.z = 0.; p = vdot(n, v1); if(p < 0) { t = 1 - ( 1 - p*p ) / ( index*index ); if( t <= 0 ) return v2; t = -p/index - sqrt(t); } else { index = 1 / index; t = 1 - ( 1 - p*p ) / ( index*index ); if( t <= 0 ) return v2; t = -p/index + sqrt(t); } v2 = vadd( svproduct(1/index, v1), svproduct(t, n) ); return v2; } /* * the actual shading function. Recursively calls trace() */ struct color shade(i, r, n) struct intersect i; struct ray r; int n; /* recursion level */ { struct color col; struct vector pt; struct ray shadow_ray; struct vector ldir; int shad; double ldot; double spec; struct vector rr; /* reflected light direction */ struct ray reflected, refracted; /* refl, refr directions */ struct color c; /* if the recursion level has been exceeded, return peacefully*/ if(n > MAXLEVEL) { col.r = col.g = col.b = 0.; return col; } /* initially get the ambient color */ col = i.obj->ambient; /* first calculate the intersection point */ pt = vadd( r.pos, svproduct(i.t, r.dir) ); /* for the light source first get the vector from it to the intersection */ ldir = norm( vsub(pt, light) ); /* then calc the dot product between the light direction and the normal */ /* negative because the light direction is reverse (comes from the light) */ ldot = -vdot(i.n, ldir); /* and find if that shadow ray is stopped by an object */ shadow_ray.pos = light; shadow_ray.dir = ldir; shadow_ray.obj = i.obj; shad = shadow(shadow_ray); if( (ldot>0) && !shad ) { /* statistics */ shadowline++; /* add some diffuse color. */ col.r += ldot * i.obj->diffuse.r; col.g += ldot * i.obj->diffuse.g; col.b += ldot * i.obj->diffuse.b; /* now calc the specular color */ /* first calculate the reflected light vector */ rr = reflect(i.n, ldir); /* then take the dot of the reflected ray with the viewing direction */ /* that is the specular component. The minus is there for obvious reasons */ spec = -vdot(rr, r.dir); /* remember the specular width factor? We use it here! */ spec = pow(spec, i.obj->width); col.r += spec * i.obj->specular.r; col.g += spec * i.obj->specular.g; col.b += spec * i.obj->specular.b; } /* setup the reflected ray */ reflected.pos = pt; reflected.dir = reflect(i.n, r.dir); reflected.obj = i.obj; /* calculate the reflection */ reflectline++; c = trace(reflected, n+1); col.r += c.r * i.obj->refl_color.r * i.obj->reflection; col.g += c.g * i.obj->refl_color.g * i.obj->reflection; col.b += c.b * i.obj->refl_color.b * i.obj->reflection; /* now setup the refracted ray */ refracted.pos = pt; refracted.dir = refract(i.n, r.dir, i.obj->index); refracted.obj = i.obj; if( (refracted.dir.x == 0.) && (refracted.dir.y == 0.) && (refracted.dir.z == 0.) ) return col; /* and calculate the reflection */ refractline++; c = trace(refracted, n+1); col.r += c.r * i.obj->refr_color.r * i.obj->refraction; col.g += c.g * i.obj->refr_color.g * i.obj->refraction; col.b += c.b * i.obj->refr_color.b * i.obj->refraction; return col; } @//E*O*F shade.c// chmod u=rw,g=rw,o=rw shade.c echo x - trace.c sed 's/^@//' > "trace.c" <<'@//E*O*F trace.c//' /* * This is the actual ray-tracing part */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "ray.h" #include "vector.h" #include <stdio.h> struct intersect intersect(); struct color shade(); struct color bgcolor(); struct color trace(r, n) struct ray r; int n; /* recursion level */ { struct intersect inter; struct color col; double m; /* update stats */ rayline++; /* check for intersection with the object */ inter = intersect(r); /* if no intersection, return some background color */ if( inter.obj == NULL ) return bgcolor(r); /* more stats */ intersectline++; /* else calculate the shading function there */ col = shade(inter, r, n); /* if the color > 1, that means that the components are too big. Normalize. */ m = col.r; if( col.g > m ) m = col.g; if( col.b > m ) m = col.b; if( m > 1 ) { /* overflow condition */ printf("Pixel normalized:%lf %lf %lf\n", col.r, col.g, col.b); col.r /= m; col.g /= m; col.b /= m; } return col; } raytrace(fname) char *fname; { int x, y, x1, y1; double xr, yr, xstep, ystep; struct vector hor, ver, eye_dir; struct color col; struct ray ray; int r, g, b; double m; FILE *f; f = fopen(fname, "w"); if(f == NULL) { perror("fopen"); exit(1); } hor.x = 1.; hor.y = 0.; hor.z = 0.; ver.x = 0.; ver.y = 1.; ver.z = 0.; eye_dir.x = 0.; eye_dir.y = 0.; eye_dir.z = 1.; ray.pos.x = 0.; ray.pos.y = 0.; ray.pos.z = 0.; ray.obj = NULL; /* not coming from an object */ fwrite(&xres, sizeof(int), 1, f); fwrite(&yres, sizeof(int), 1, f); yr = 1.; xstep = 2. / xres; ystep = 2. / yres; for(y = 0; y < yres;y++) { xr = -1.; for(x = 0; x < xres; x++) { ray.dir = vadd( svproduct(xr, hor), svproduct(yr, ver) ); ray.dir = norm( vadd( ray.dir, eye_dir) ); col = trace(ray, 0); /* calc the integer value to be put to the file */ r = col.r * 255; g = col.g * 255; b = col.b * 255; putc(r,f); putc(g,f); putc(b,f); xr += xstep; } yr -= ystep; printf("Finished scanline %d. r:%.2lf/%d sh:%d rl:%d rr:%d int:%d\n", y, (double)rayline / yres, rayline, shadowline, reflectline, refractline, intersectline); /* update statistics */ raycount += rayline; rayline = 0; shadowcount += shadowline; shadowline = 0; reflectcount += reflectline; reflectline = 0; refractcount += refractline; refractline = 0; intersectcount += intersectline; intersectline = 0; objtestcount += objtestline; objtestline = 0; } printf("\nTotal number of rays traced: %d\n", raycount); printf("Total number of not shadowed intersections: %d\n", shadowcount); printf("Total reflected rays traced: %d\n", reflectcount); printf("Total refracted rays traced: %d\n", refractcount); printf("Total object intersections: %d\n", intersectcount); printf("Total objects tested: %d\n", objtestcount); } @//E*O*F trace.c// chmod u=rw,g=rw,o=rw trace.c echo x - vector.c sed 's/^@//' > "vector.c" <<'@//E*O*F vector.c//' /* * vector operations */ /* * (c) 1988 by George Kyriazis * and the RPI - ACM */ #include "ray.h" #include <math.h> struct vector vadd(a,b) struct vector a,b; { struct vector c; c.x=a.x+b.x; c.y=a.y+b.y; c.z=a.z+b.z; return c; } struct vector vsub(a,b) struct vector a,b; { struct vector c; c.x=a.x-b.x; c.y=a.y-b.y; c.z=a.z-b.z; return c; } struct vector vneg(a) struct vector a; { struct vector b; b.x= -a.x; b.y= -a.y; b.z= -a.z; return b; } struct vector svproduct(k,a) double k; struct vector a; { a.x*=k; a.y*=k; a.z*=k; return a; } double vdot(a,b) struct vector a,b; { return (a.x*b.x+a.y*b.y+a.z*b.z); } struct vector vcross(a,b) struct vector a,b; { struct vector c; c.x=a.y*b.z-b.y*a.z; c.y=b.x*a.z-a.x*b.z; c.z=a.x*b.y-b.x*a.y; return c; } struct vector norm(a) struct vector a; { double len; struct vector b; len=sqrt(a.x*a.x+a.y*a.y+a.z*a.z); b.x=a.x/len; b.y=a.y/len; b.z=a.z/len; return b; } @//E*O*F vector.c// chmod u=rw,g=rw,o=rw vector.c echo Inspecting for damage in transit... temp=/tmp/shar$$; dtemp=/tmp/.shar$$ trap "rm -f $temp $dtemp; exit" 0 1 2 3 15 cat > $temp <<\!!! 81 453 2832 README 15 33 265 Makefile 85 339 2018 ray.h 19 46 284 vector.h 27 92 458 bg.c 22 66 381 initialize.c 82 293 1549 intersect.c 32 61 423 main.c 67 175 1366 readfile.c 172 720 4312 shade.c 120 456 2843 trace.c 91 133 969 vector.c 813 2867 17700 total !!! wc README Makefile ray.h vector.h bg.c initialize.c intersect.c main.c readfile.c shade.c trace.c vector.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp if [ -s $dtemp ] then echo "Ouch [diff of wc output]:" ; cat $dtemp else echo "No problems found." fi exit 0 George Kyriazis kyriazis@turing.cs.rpi.edu ------------------------------