by Sandro Maffiodo
smaffer@gmail.com
www.assezeta.com/sandromaffiodo
This is not another Ray Tracer.
This is a classical 3D Software Rasterizer that supports:
The program reads an infinite list of frame descriptors from stdin and generate an infinite sequence of rendered frames to stdout.
$(CC) prog.c -D_= -lm -o prog
The _ symbol is usefull if you want to build the program on Windows. MS Windows need this code to modifiy the open mode of stdout from text to binary:
setmode(fileno(stdout),O_BINARY);
If you want to view the rendering output you can pipe the output to any program that can understand BGRA raw images (like ffmpeg or imagemagick convert), otherwise you can use my simple SDL viewer (oview.c):
$(CC) oview.c -lSDL -o oview
The program require three parameters: WIDTH, HEIGHT and DELAY. The program reads frames from stdin and shows them to a window with DELAY milliseconds between one frame and the next. This tool require libSDL1.X.
If you like to experiment with the program, you can convert your Wavefront OBJ files to a frame descriptor, using my simple converter (oconvert.c):
$(CC) oconvert.c -o oconvert
The program require two parameters: INPUT and OUTPUT. INPUT is the OBJ file and OUTPUT will be a partial frame descriptor with the converted geometry of the input model (without textures). The OBJ file must use triangles (not quads) and must include vertex normals and textures coordinates.
When the program starts you can manually type inputs to the program, but this can take a lot of time. So i've writen some shell scripts that generate some examples, usefull to test the program and see what it does. To use those scripts you must make them executables:
$ chmod +x *.sh
Each script generate an infinite list of:
To view what one of these scripts does alone, you can type
$ ./cube.sh 320 200
By running cube.sh in pipe with the program you will get an infinite list of BGRA frames:
$ ./cube.sh 320 200 | ./prog 320 200
By running the same command in pipe with ffmpeg you will get a movie:
./cube.sh 320 200 | ./prog 320 200 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 320x200 -i - -y -t 5 -b 2M test.mpeg
Otherwise you can pipe the program output to my simple SDL viewer (oview):
./cube.sh 320 200 | ./prog 320 200 | ./oview 320 200 0
$ ./computer.sh 640 480 | ./prog 640 480 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 640x480 -i - -y -t 5 -b 2M computer.mpeg
$ ./cube.sh 320 200 | ./prog 320 200 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 320x200 -i - -y -t 5 -b 2M cube.mpeg
$ ./randomsphere.sh 320 240 | ./prog 320 240 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 320x240 -i - -y -t 5 -b 2M randomsphere.sh
$ ./smaffer.sh 800 600 | ./prog 800 600 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 800x600 -i - -y -t 5 -b 2M smaffer.mpeg
$ ./tribytri.sh 800 600 | ./prog 800 600 | ffmpeg -r 25 -f rawvideo -pix_fmt bgra -s 800x600 -i - -y -t 5 -b 2M tribytri.mpeg
$ ./computer.sh 640 480 | ./prog 640 480 | ./oview 640 480 0
$ ./cube.sh 320 200 | ./prog 320 200 | ./oview 320 200 0
$ ./randomsphere.sh 320 240 | ./prog 320 240 | ./oview 320 240 0
$ ./smaffer.sh 800 600 | ./prog 800 600 | ./oview 800 600 0
$ ./tribytri.sh 800 600 | ./prog 800 600 | ./oview 800 600 0
The program starts reading the output resolution from the command line arguments; then reads a raw BGRA texture image from stdin.
The texture format is very simple:
OTHERS BYTE: a list of size*size BGRA pixels. You can generate the raw BGRA image using imagemagick convert, with something like this:
convert INPUT OUTPUT.bgra
After that, the program continue to reading an infinite list of frame descriptor from stdin. Each frame descriptor is encoded in ASCII and contain a list of float values, comma separated:
a variable length list of float values describing all vertices in the scene. Each vertex is described from 13 values:
X, Y, Z, W, 1, R, G, B, U, V, NX, NY, NZ, 1,
Where
The list of float values must terminate with the "E" character. For each 3 vertices the renderer will generate a single triangle.
This is an example of a descriptor for one coloured triangle:
-1.81066, 0, 0, 0,
0, -2.414213, 0, 0,
0, 0, -1.083333, -1,
0, 0, -8.333333, 0,
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, -10, 1,
10, 10, 1, 1,
0, 0, 1, 1, 1, 0, 0, 0, 0, -1, -1, 1, 1,
0, 0, 1, 1, 1, 0, 1, 1, 0, 1, -1, 1, 1,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
E
This command will generate the same triangle in the file frame.bgra, using a white 2x2 texture:
$ printf "\001\077\077\077\077\077\077\077\077\077\077\077\077\077\077\077\077 -1.81066, 0, 0, 0, 0, -2.414213, 0, 0, 0, 0, -1.083333, -1,0, 0, -8.333333, 0,1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,0, 0, -10, 1, 10, 10, 1, 1, 0, 0, 1, 1,1, 0, 0, 0, 0, -1, -1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, -1, 1, 1, 0, 0, 1, 1, 1, 1, 1,1, 1, 1, 1, 1, 1, E" | ./prog 320 200 > frame.bgra
To convert frame.bgra to JPG you can use this:
convert -size 320x200 -depth 8 frame.bgra frame.jpg
The program does not set any limitations on the rendering output resolution. The maximum output resolution can be
width = INT_MAX
height = INT_MAX
On a 32bit machine this mean (2147483647 x 2147483647) pixels
The maximum texture resolution is limited to
width = 1<<SCHAR_MAX
height = 1<<SCHAR_MAX
This mean that the biggest loadable texture resolution is 9223372036854775807 x 9223372036854775807 pixels. Obviously this resolution can be reached only if you build and run the program on a system that have more than 64bits.
The texture resolution must be square: width and height must be equals.
The 3d scene can totally change in each frame but the texture data remains the same. For example you can pipe a frame descriptor with a spinning cube for 100 frames and then change the frame descriptor in a bouncing ball for the next 100 frames, but the texture used from the two descriptors cannot change. You must use one big texture to all your needs.
You can only change one MODELVIEW matrix for frame. This mean that you can only tranform all triangles of the scene with the given matrix. For example you can rotate and scale the scene (simulating a camera movement) but you cannot rotate a single object of the scene keeping all the others fixed.
The example scripts require bc and the existence of the file /dev/urandom.
The additional tool oview.c requires libSDL1.X.
The build process will generate some warnings (40~ on clang) about: