Join 3,512 readers in helping fund MetaFilter (Hide)


0,140866,141378,143170 ,174450,222784,139386,0,0
September 22, 2013 12:34 PM   Subscribe

Ray tracing is a computer graphics technique that produces realistic images of a three-dimensional scene. In 1987, Paul Heckbert (then at Pixar) announced a contest to produce a ray tracer in the minimum amount of code, as he describes in "A Minimal Ray Tracer." In 2009, Andrew Kensler (then at the University of Utah Scientific Computing and Imaging Institute) created a C++ version that can fit on a business card. Fabien Sanglard explicates this amazing piece of code in "Decyphering the Business Card Raytracer."
posted by grouse (19 comments total) 39 users marked this as a favorite

 
Sorry, the SO instincts are taking over, but the C++ code is C. Also, the fourth link did not work.
posted by Blazecock Pileon at 12:39 PM on September 22, 2013


Sorry, the SO instincts are taking over, but the C++ code is C.

I didn't realize C had operator overloading now.
posted by grouse at 12:43 PM on September 22, 2013 [5 favorites]


Hmm, you're right. Never mind.
posted by Blazecock Pileon at 12:48 PM on September 22, 2013


The vector class (and the operators it overloads) are the only strong C++ flavor.

The use of "struct v" instead of "class v" is clever - class members are private by default and struct members are public by default. So using "struct" costs him one extra character but he then doesn't have to burn 7 characters by specifying "public:".
posted by We had a deal, Kyle at 12:55 PM on September 22, 2013 [8 favorites]


27 seconds? My Ubuntu lappy isn't as fast as I thought.
posted by GallonOfAlan at 1:12 PM on September 22, 2013


As someone who spent the last 3 years in QA, it's beautiful, and clearly a very clever accomplishment. Anyone writing production code like this deserves to be beaten until they're pissing blood.
posted by Suddenly, elf ass at 1:18 PM on September 22, 2013 [23 favorites]


It's stuff like this that means I will never lose the nagging suspicion I have that my boss will one day come in and say something like "Look how good programmers can be at programming! Get out, I want guys like this!".
posted by Jon Mitchell at 1:20 PM on September 22, 2013 [2 favorites]


The business card ray tracer nicely demonstrates the effects of compiler optimizations:
$ for i in 3 2 1 0 s fast ; do make -s CXXFLAGS="-Wall -O$i" card && /usr/bin/time -f "%e sec (-O$i)" ./card >/dev/null && rm -f card ; done 2>&1 | sort -n
8.02 sec (-Ofast)
12.06 sec (-O3)
15.16 sec (-O2)
18.29 sec (-O1)
57.25 sec (-Os)
105.15 sec (-O0)

posted by scatter gather at 1:56 PM on September 22, 2013 [8 favorites]


Another easter-egg:
$ wc -c card.cpp
1337 card.cpp
posted by jepler at 1:57 PM on September 22, 2013 [9 favorites]


The business card ray tracer nicely demonstrates the effects of compiler optimizations

Neat!
$ for i in 3 fast ; do make -s CXXFLAGS="-Wall -O$i" card && /usr/bin/time -f "%e sec (-O$i)" ./card >/dev/null && rm -f card ; done 2>&1 | sort -n
11.46 sec (-Ofast)
18.55 sec (-O3)

$ make -s CXXFLAGS="-Wall -Ofast -fprofile-generate" card && /usr/bin/time -f "%e sec (-Ofast -fprofile-generate)" ./card >/dev/null && rm -f card
26.78 sec (-Ofast -fprofile-generate)

$ make -s CXXFLAGS="-Wall -Ofast -fprofile-use" card && /usr/bin/time -f "%e sec (-Ofast -fprofile-use)" ./card >/dev/null && rm -f card
12.31 sec (-Ofast -fprofile-use)
It looks like profile-guided optimization doesn't help me.
posted by grouse at 2:29 PM on September 22, 2013


Wow. As someone who knows barely enough C to make an arduino twitch, this is way past "impressive" into "intimidating", verging on "obviously witchcraft".

I think I follow the explanation. The weird mix of intricacy and brutal efficiency actually reminds me of looking at some of the weirder biosynthesis pathways; if there was a mind involved, it was a mad genius.
posted by metaBugs at 2:35 PM on September 22, 2013 [2 favorites]


> [0,140866,141378,143170,174450,222784,139386,0,0].reverse().map(function(n) {
    return n.toString(2).replace('1','*','g').replace('0',' ','g') + '\n';
}).join('\n);

*   *      **** * 
** **  **  *      
* * * *  * ***  * 
*   * **** *    * 
*   * *    *    * 
*   *  **  *    * 
 

posted by potch at 2:41 PM on September 22, 2013 [12 favorites]


grouse: the profiling options don't really help on my system either, although maybe a little with -O2. I've never really used PGO in practice. Maybe it needs repeated runs or a larger program or data set to be useful.
posted by scatter gather at 3:02 PM on September 22, 2013


Yeah, but can he make it juggle?
posted by scruss at 3:15 PM on September 22, 2013 [1 favorite]


MeFi
posted by chortly at 5:56 PM on September 22, 2013 [3 favorites]


The ray tracer on a business card that I created in 1987 was written in C. Read the "A Minimal Ray Tracer" article for some explanation of how it works.

typedef struct{double x,y,z}vec;vec U,black,amb={.02,.02,.02};struct sphere{
vec cen,color;double rad,kd,ks,kt,kl,ir}*s,*best,sph[]={0.,6.,.5,1.,1.,1.,.9,
.05,.2,.85,0.,1.7,-1.,8.,-.5,1.,.5,.2,1.,.7,.3,0.,.05,1.2,1.,8.,-.5,.1,.8,.8,
1.,.3,.7,0.,0.,1.2,3.,-6.,15.,1.,.8,1.,7.,0.,0.,0.,.6,1.5,-3.,-3.,12.,.8,1.,
1.,5.,0.,0.,0.,.5,1.5,};yx;double u,b,tmin,sqrt(),tan();double vdot(A,B)vec A
,B;{return A.x*B.x+A.y*B.y+A.z*B.z;}vec vcomb(a,A,B)double a;vec A,B;{B.x+=a*
A.x;B.y+=a*A.y;B.z+=a*A.z;return B;}vec vunit(A)vec A;{return vcomb(1./sqrt(
vdot(A,A)),A,black);}struct sphere*intersect(P,D)vec P,D;{best=0;tmin=1e30;s=
sph+5;while(s-->sph)b=vdot(D,U=vcomb(-1.,P,s->cen)),u=b*b-vdot(U,U)+s->rad*s
->rad,u=u>0?sqrt(u):1e31,u=b-u>1e-7?b-u:b+u,tmin=u>=1e-7&&uir;d= -vdot(D,N=vunit(vcomb(-1.,P=vcomb(tmin,D,P),s->cen
)));if(d<0>sph)if((e=l
->kl*vdot(N,U=vunit(vcomb(-1.,P,l->cen))))>0&&intersect(P,U)==l)color=vcomb(e
,l->color,color);U=s->color;color.x*=U.x;color.y*=U.y;color.z*=U.z;e=1-eta*
eta*(1-d*d);return vcomb(s->kt,e>0?trace(level,P,vcomb(eta,D,vcomb(eta*d-sqrt
(e),N,black))):black,vcomb(s->ks,trace(level,P,vcomb(2*d,N,D)),vcomb(s->kd,
color,vcomb(s->kl,U,black))));}main(){printf("%d %d\n",32,32);while(yx<32*32)
U.x=yx%32-32/2,U.z=32/2-yx++/32,U.y=32/2/tan(25/114.5915590261),U=vcomb(255.,
trace(3,black,vunit(U)),black),printf("%.0f %.0f %.0f\n",U);}/*minray!*/

posted by paulheckbert at 6:59 PM on September 22, 2013 [25 favorites]


The tutorial for POV-Ray shows how to make a raytracer with a raytracer.
posted by elgilito at 12:47 AM on September 23, 2013


I too lean toward the "beauty is as beauty does" school of thought.

Think that is inscrutable ... imagine it in assembler.
posted by Twang at 6:25 AM on September 23, 2013


10 PRINT "PENUS ";
20 GOTO 10
posted by Cookiebastard at 11:18 AM on September 23, 2013 [1 favorite]


« Older Not only is today, September 22, the Autumnal Equi...  |  Atari cartridge art and artist... Newer »


This thread has been archived and is closed to new comments