Committed on the Free edition of March Hare Software CVSNT Server. Upgrade to CVS Suite for more features and support: http://march-hare.com/cvsnt/ git-svn-id: http://moon:8086/svn/vhdl/trunk@790 cc03376c-175c-47c8-b038-4cd826a8556b
472 lines
10 KiB
C
472 lines
10 KiB
C
/*
|
|
* IOCCC Raytracer by Anders Gavare.
|
|
*
|
|
* The raytracer was one of the winners of the 17th IOCCC.
|
|
*
|
|
* NOTE: This is the UNOBFUSCATED version of the raytracer I used
|
|
* during development, not the actual entry sent to the contest.
|
|
*
|
|
*
|
|
* How to build:
|
|
* cc r3.c -o r3 (plus optimization flags)
|
|
*
|
|
* How to run:
|
|
* ./r3 > ray.ppm
|
|
* or
|
|
* ./r3 | xv -
|
|
*/
|
|
#include "libsys.h"
|
|
UINT32 *pP;
|
|
|
|
xsize = 800;
|
|
ysize = 600;
|
|
An = 1;
|
|
|
|
camera_X = 0;
|
|
camera_Y = -10;
|
|
camera_Z = -7;
|
|
|
|
scale = 1296;
|
|
RootOfScale = 36;
|
|
maxcolor = 255;
|
|
|
|
MaxDepth=9;
|
|
_=1<<15;
|
|
|
|
/*
|
|
double sphere_x[44] = {
|
|
-15,-15,-15,-15, // I
|
|
-8,-8,-10,-6,-10,-6, // O
|
|
-1,-1, 1,1, // C
|
|
6, 6, 8,8, // C
|
|
13,13,15,15, // C
|
|
-11,-11,-11,-11,-9,-9,-7,-7, // r
|
|
0,0,-2,-2,-2,2,2,2, // a
|
|
7,7,9,9,11,11, // y
|
|
};
|
|
double sphere_z[44] = {
|
|
3,0,-3,-6, // I
|
|
-6, 3,0,0,-3,-3, // O
|
|
0,-3,-6, 3, // C
|
|
0,-3,-6, 3, // C
|
|
0,-3,-6, 3, // C
|
|
-11,-13,-15,-17,-11,-15,-13,-17, // r
|
|
-11,-15,-13,-15,-17,-13,-15,-17, // a
|
|
-11,-13,-15,-17,-11,-13, // y
|
|
};
|
|
*/
|
|
|
|
/* Common return variable: */
|
|
return_var;
|
|
return_var2;
|
|
return_var3;
|
|
return_var4;
|
|
|
|
|
|
get_sphere_coordinates(b)
|
|
{
|
|
return_var = "1111886:6:??AAFFHHMMOO55557799@@>>>BBBGGIIKK"[b]-64;
|
|
return_var3 = "C@=::C@@==@=:C@=:C@=:C531/513/5131/31/531/53"[b]-64;
|
|
|
|
/* return_var = sphere_x[b]; */
|
|
return_var2 = b<22? 9 : 0;
|
|
/* return_var3 = sphere_z[b]; */
|
|
return_var4 = 2;
|
|
}
|
|
|
|
|
|
/* sqroot(x) returns the square root of x in return_var */
|
|
sqroot_sub(x, mask, guess)
|
|
{
|
|
mask?
|
|
(
|
|
guess ^= mask,
|
|
guess*guess > x ?
|
|
( guess ^= mask )
|
|
:
|
|
0,
|
|
sqroot_sub(x, mask/2, guess)
|
|
)
|
|
:
|
|
( return_var = guess );
|
|
}
|
|
|
|
|
|
sqroot(x)
|
|
{
|
|
sqroot_sub(x, _, 0);
|
|
}
|
|
|
|
|
|
/*
|
|
* hit_sphere():
|
|
*
|
|
* returns where on a sphere we hit
|
|
* Returns distance in hit_sphere_q. (Negative return
|
|
* value if we didn't hit anything.)
|
|
*/
|
|
hit_sphere_q;
|
|
hit_sphere(objnr, x, y, z,
|
|
dx, dy, dz,
|
|
a, b)
|
|
{
|
|
get_sphere_coordinates(objnr);
|
|
x -= return_var*scale;
|
|
y -= return_var2*scale;
|
|
z -= return_var3*scale;
|
|
|
|
/*
|
|
* Solve the following equation:
|
|
*
|
|
* q^2 (dx^2+dy^2+dz^2) + q * 2(x*dx+y*dy+z*dz)
|
|
* + x^2+y^2+z^2 - r^2 = 0
|
|
*
|
|
* We assume that |dx,dy,dz| is 1.
|
|
*
|
|
* q^2 + a q + b = 0
|
|
*
|
|
* The solution is (of course)
|
|
*
|
|
* q = -a/2 +- sqrt(a^2/4 - b)
|
|
*/
|
|
|
|
b = x*x/scale+y*y/scale+z*z/scale-return_var4*return_var4*scale;
|
|
|
|
/* a = 2*(...), and then divide by -2 */
|
|
a = -x*dx/scale-y*dy/scale-z*dz/scale;
|
|
|
|
hit_sphere_q =
|
|
(
|
|
(b = a*a/scale - b) >= 0 ?
|
|
(
|
|
/* b = scale*sqrt((double)b/(double)scale), */
|
|
/* sqroot(b*scale), */
|
|
|
|
/* sqroot_sub(b, _, 0),
|
|
b = return_var * RootOfScale,
|
|
*/
|
|
sqroot_sub(b*scale, _, 0),
|
|
b = return_var,
|
|
|
|
/*
|
|
* Return the lowest q (a+b or a-b) which is more
|
|
* than 0. Return negative if neither
|
|
* a+b or a-b is more than 0.
|
|
*/
|
|
|
|
a + (a>b? -b : b)
|
|
)
|
|
:
|
|
-1.0
|
|
);
|
|
}
|
|
|
|
|
|
/*
|
|
* find_closest():
|
|
*
|
|
* find_closest() scans objects and returns an index
|
|
* to the object which was closest (has the lowest 'q').
|
|
* x,y,z,dx,dy,dz are scaled.
|
|
* find_closest_i is the index number of the found object,
|
|
* -1 if none was found. find_closest_q is the distance
|
|
* (scaled).
|
|
*/
|
|
find_closest_i; /* index ("sphere number") */
|
|
find_closest_q; /* distance (scaled) */
|
|
find_closest(objnr, x, y, z,
|
|
dx, dy, dz, notindex)
|
|
{
|
|
/* Initialize find_closest_i on first call: */
|
|
find_closest_i =
|
|
!objnr ? -1 : find_closest_i;
|
|
|
|
objnr < 44 ?
|
|
(
|
|
hit_sphere(objnr, x,y,z, dx,dy,dz, 0,0),
|
|
(hit_sphere_q > 0 && objnr!=notindex &&
|
|
(hit_sphere_q<find_closest_q || find_closest_i<0)) ?
|
|
(
|
|
find_closest_q = hit_sphere_q,
|
|
find_closest_i = objnr
|
|
) : 0,
|
|
find_closest(objnr+1, x,y,z,dx,dy,dz, notindex)
|
|
)
|
|
: 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* trace_ray():
|
|
*
|
|
* Traces one ray. orig_xyz and dir_xyz are scaled.
|
|
* Return value in trace_ray_R, G, and B.
|
|
*/
|
|
trace_ray_R;
|
|
trace_ray_G;
|
|
trace_ray_B;
|
|
nX;
|
|
nY;
|
|
nZ; /* tmp normal, and tmp
|
|
sphere_light calculation */
|
|
trace_ray(orig_x, orig_y, orig_z,
|
|
dir_x, dir_y, dir_z,
|
|
depth, notindex,
|
|
tmpcol, closest_i_saved)
|
|
{
|
|
/*
|
|
* "Scan" through the list of all objects
|
|
* to see which one is closest:
|
|
*/
|
|
|
|
find_closest(0, orig_x, orig_y, orig_z,
|
|
dir_x, dir_y, dir_z, notindex);
|
|
|
|
depth>0 && find_closest_i >= 0?
|
|
(
|
|
/* find_closest_i and _q are the object
|
|
number and distance of the object we
|
|
hit. */
|
|
|
|
orig_x += dir_x*find_closest_q/scale,
|
|
orig_y += dir_y*find_closest_q/scale,
|
|
orig_z += dir_z*find_closest_q/scale,
|
|
|
|
/*
|
|
* Calculate color (diffuse light):
|
|
* (Note: use nZ as a temp variable while
|
|
* calculating nY)
|
|
*/
|
|
get_sphere_coordinates(find_closest_i),
|
|
nX = orig_x - return_var*scale,
|
|
nY = orig_y - return_var2*scale,
|
|
nZ = orig_z - return_var3*scale,
|
|
|
|
tmpcol = (-2*nX -2*nY + nZ) / 3, /* sqrt(9), */
|
|
/* Set return_var to the length of the normal
|
|
vector (in this case the sphere radius) */
|
|
/* return_var = sqrt(nX*nX + nY*nY +nZ*nZ), */
|
|
sqroot(nX*nX + nY*nY +nZ*nZ),
|
|
// return_var = return_var4*scale,
|
|
/* divide by return_var to get the color */
|
|
// tmpcol = return_var!=0? tmpcol*scale/return_var : 0,
|
|
tmpcol /= return_var4,
|
|
|
|
/* color is now -1..1 */
|
|
tmpcol *= tmpcol, /* square the color 1..-1 => 1..1 */
|
|
tmpcol *= 200,
|
|
tmpcol /= (scale*scale),
|
|
/* tmpcol += 5, */
|
|
|
|
closest_i_saved = find_closest_i,
|
|
|
|
/* Mirror: out = in - 2*(-normal)*cos v
|
|
where v is the angle between in and
|
|
-normal */
|
|
|
|
/* -normal: (normalized to len=1.0) */
|
|
/* return_var = sqrt(nX*nX + nY*nY + nZ*nZ), */
|
|
return_var!=0?
|
|
(
|
|
nX = -nX * scale / return_var,
|
|
nY = -nY * scale / return_var,
|
|
nZ = -nZ * scale / return_var
|
|
) : 0,
|
|
|
|
/* use return_var as a temp variable,
|
|
calculate cosinus between the vectors */
|
|
return_var = (dir_x*nX + dir_y*nY + dir_z*nZ)/scale,
|
|
/*
|
|
dir_x -= 2 * nX * return_var / scale,
|
|
dir_y -= 2 * nY * return_var / scale,
|
|
dir_z -= 2 * nZ * return_var / scale,
|
|
*/
|
|
dir_x -= nX * return_var / (scale/2),
|
|
dir_y -= nY * return_var / (scale/2),
|
|
dir_z -= nZ * return_var / (scale/2),
|
|
|
|
trace_ray(orig_x, orig_y, orig_z,
|
|
dir_x, dir_y, dir_z,
|
|
depth-1, find_closest_i, 0,0),
|
|
|
|
trace_ray_R /= 2,
|
|
trace_ray_G /= 2,
|
|
trace_ray_B /= 2,
|
|
/*
|
|
closest_i_saved &= 7,
|
|
!closest_i_saved? (closest_i_saved++) : 0,
|
|
*/
|
|
/*
|
|
closest_i_saved = closest_i_saved<4? closest_i_saved+1 : 7,
|
|
*/
|
|
/*
|
|
tvinga gråskalor: closest_i_saved = 7,
|
|
*/
|
|
closest_i_saved = closest_i_saved<22? 7 :
|
|
(
|
|
closest_i_saved<30 ? 1 :
|
|
(
|
|
closest_i_saved<38 ? 2 :
|
|
(
|
|
closest_i_saved<44 ? 4 :
|
|
(
|
|
closest_i_saved == 44 ? 6 :
|
|
3
|
|
)
|
|
)
|
|
)
|
|
),
|
|
|
|
trace_ray_R += closest_i_saved & 1 ? tmpcol : 0,
|
|
trace_ray_G += closest_i_saved & 2 ? tmpcol : 0,
|
|
trace_ray_B += closest_i_saved & 4 ? tmpcol : 0
|
|
)
|
|
:
|
|
(
|
|
/* If we didn't hit anything, set the color anyway: */
|
|
|
|
depth==MaxDepth? /* True if this is a ray originating
|
|
from the camera */
|
|
(
|
|
orig_z += 2,
|
|
dir_z = orig_z > 0? orig_z / 8 : orig_z / 20
|
|
)
|
|
: 0,
|
|
|
|
/*
|
|
* Colors according to Horizon_1 in gimp:
|
|
* At top of sky: 13,92,146 (light blue)
|
|
* At bottom sky: 255,255,255 (white)
|
|
* Top of ground: 213,168,111 (light brown)
|
|
* Bottom of ground: 103, 55, 26 (brown)
|
|
*/
|
|
|
|
dir_z > 0?
|
|
(
|
|
trace_ray_B = dir_z * dir_z / scale,
|
|
trace_ray_R = 255 - 250 * trace_ray_B / scale,
|
|
trace_ray_G = 255 - 150 * trace_ray_B / scale,
|
|
trace_ray_B = 255 - 100 * trace_ray_B / scale
|
|
)
|
|
:
|
|
(
|
|
trace_ray_B = dir_z * dir_z / scale,
|
|
trace_ray_B < scale/5?
|
|
(
|
|
trace_ray_R = 255 - 210 * trace_ray_B / scale,
|
|
trace_ray_G = 255 - 435 * trace_ray_B / scale,
|
|
trace_ray_B = 255 - 720 * trace_ray_B / scale
|
|
)
|
|
:
|
|
(
|
|
trace_ray_B -= scale/5,
|
|
trace_ray_R = 213 - 110 * trace_ray_B / scale,
|
|
trace_ray_G = 168 - 113 * trace_ray_B / scale,
|
|
trace_ray_B = 111 - 85 * trace_ray_B / scale
|
|
)
|
|
),
|
|
|
|
depth!=MaxDepth?
|
|
(
|
|
trace_ray_R /= 2,
|
|
trace_ray_G /= 2,
|
|
trace_ray_B /= 2
|
|
)
|
|
: 0
|
|
);
|
|
|
|
trace_ray_R = trace_ray_R<0? 0 : trace_ray_R>maxcolor? maxcolor: trace_ray_R;
|
|
trace_ray_G = trace_ray_G<0? 0 : trace_ray_G>maxcolor? maxcolor : trace_ray_G;
|
|
trace_ray_B = trace_ray_B<0? 0 : trace_ray_B>maxcolor? maxcolor : trace_ray_B;
|
|
}
|
|
|
|
|
|
/*
|
|
* do_pixels_in_line():
|
|
*
|
|
* the horizontal "for loop"
|
|
*/
|
|
R; G; B;
|
|
dpil_helper(x,y, a,b)
|
|
{
|
|
trace_ray(
|
|
scale*camera_X + scale*40*(An*x+a)/xsize/An - scale*20,
|
|
scale*camera_Y,
|
|
scale*camera_Z - scale*30*(An*y+b)/ysize/An + scale*15,
|
|
0, scale, 0, MaxDepth, -1, 0,0);
|
|
|
|
R += trace_ray_R;
|
|
G += trace_ray_G;
|
|
B += trace_ray_B;
|
|
|
|
++a<An?
|
|
dpil_helper(x,y,a,b)
|
|
: (
|
|
++b<An?
|
|
dpil_helper(x,y,0,b)
|
|
: 0
|
|
);
|
|
}
|
|
do_pixels_in_line(x, y)
|
|
{
|
|
R = G = B = 0;
|
|
dpil_helper(x,y,0,0);
|
|
|
|
x < xsize ?
|
|
(
|
|
/* output the pixel to stdout, */
|
|
|
|
#ifndef JMIPS_VGA
|
|
printf("%c%c%c", R/An/An, G/An/An, B/An/An),
|
|
#else
|
|
pP[x+xsize*y] = (R/An/An & 0xFF) | ((G/An/An & 0xFF) << 8) | ((B/An/An & 0xFF) << 16),
|
|
#endif
|
|
/* and then do the next pixel in this line: */
|
|
do_pixels_in_line(x+1, y)
|
|
) : 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* do_line():
|
|
*
|
|
* the vertical "for loop"
|
|
*/
|
|
do_line(y)
|
|
{
|
|
/* Do all lines: */
|
|
do_pixels_in_line(0, --y? do_line(y),y:y);
|
|
}
|
|
|
|
main()
|
|
{
|
|
#ifdef JMIPS_VGA
|
|
volatile UINT32 *pVGA_ctrl = (UINT32*)SYS_VGA_CTRL;
|
|
volatile UINT32 *pVGA_front = (UINT32*)SYS_VGA_FB_FRONT;
|
|
volatile UINT32 *pVGA_back = (UINT32*)SYS_VGA_FB_BACK;
|
|
UINT64 *pPixelBuf;
|
|
|
|
xsize = Screen_get_resx();
|
|
ysize = Screen_get_resy();
|
|
|
|
pPixelBuf = (UINT64*)malloc(xsize*ysize*sizeof(UINT32));
|
|
pP = (UINT32*)pPixelBuf;
|
|
printf("pPixelBuf : %8.8X\n", (UINT32)pPixelBuf);
|
|
|
|
*pVGA_front = (UINT32)pPixelBuf;
|
|
*pVGA_back = (UINT32)pPixelBuf;
|
|
*pVGA_ctrl |= SYS_VGA_BIT_MSTEN;
|
|
|
|
memset(pPixelBuf, 0, xsize*ysize*sizeof(UINT32));
|
|
#else
|
|
|
|
/* Output PPM file header ... */
|
|
printf("P6\n%i %i\n255\n", xsize, ysize);
|
|
|
|
#endif
|
|
|
|
/* and do all lines: */
|
|
do_line(ysize);
|
|
}
|
|
|