
Smallpt: Global Illumination in 99 lines of C++ - Ivoah
http://www.kevinbeason.com/smallpt/
======
dahart
I like the walls made of spheres! The real Cornell box is about 2 feet cubed
IIRC... that would make the units of this scene file something close to
centimeters... which would mean the walls are spheres with a diameter of about
1.2 miles.

Last time I saw the box in person, I made sure to leave some fingerprints. :P

> Realistic Ray Tracing, by Peter Shirley > Almost 100% of smallpt is derived
> from this book.

FWIW, Peter Shirley distilled all the fun parts of Realistic Ray Tracing into
an easy quick e-book available for $3, or free with the trial. "Ray Tracing in
One Weekend."

[https://www.amazon.com/gp/product/B01B5AODD8/](https://www.amazon.com/gp/product/B01B5AODD8/)

Blog post about it w/ more code & lots of resources:
[http://in1weekend.blogspot.com/2016/01/ray-tracing-in-one-
we...](http://in1weekend.blogspot.com/2016/01/ray-tracing-in-one-weekend.html)

~~~
generic_user
Thanks for posting the blog. I know Shirley's books but I had not seen the
blog.

~~~
dahart
Surely. :) That blog is actually for his e-books. Here's his real blog:
[http://psgraphics.blogspot.com/](http://psgraphics.blogspot.com/)

------
metaobject
I don't understand the fascination with writing/presenting the code with as
few lines as possible. I'd be just as impressed if they said the code was 120
lines and formatted so it was just a bit more pleasant to read.

~~~
asrp
Here are slides linked from that page with a reformatted version with 202
lines:

[https://drive.google.com/file/d/0B8g97JkuSSBwUENiWTJXeGtTOHF...](https://drive.google.com/file/d/0B8g97JkuSSBwUENiWTJXeGtTOHFmSm51UC01YWtCZw/view)

Here's the source from those slides (I don't know who put it there).

[http://codepad.org/ZWbF9yrY](http://codepad.org/ZWbF9yrY)

I had to remove these two lines to make it compile.

    
    
        double M_PI = 3.14159265358979;
        double M_1_PI = 1.0/M_PI;

~~~
jklehm
I had to modify this line as well: double erand48(unsigned short xsubi[3])
throw() {

Link here: [http://codepad.org/fpp2inpr](http://codepad.org/fpp2inpr)

------
aduffy
Interestingly enough, this code is replicated in Scala Native as an example
for use of the language superset[1].

[1] [https://github.com/scala-native/scala-native-
example/blob/ma...](https://github.com/scala-native/scala-native-
example/blob/master/src/main/scala/smallpt.scala)

------
babuskov

        FILE *f = fopen("image.ppm", "w");         // Write image to PPM file. 
    

Useless comment. Could've used that space for error checking.

------
weerd
So concise. operator% is used as cross product... I love it

~~~
vilya
I've recently had to deal with some code that did this for work (and also used
operator* for dot product) and it made the equations incredibly difficult to
read. Please don't make the mistake of doing this in your own code.

~~~
CyberDildonics
Using operator* for the dot product is straight up wrong, since multiplication
between two vectors is well defined and used all the time.

~~~
vilya
Exactly.

Same with %: since we're talking about a number-like object, there's a clear
and expected meaning for it and that is NOT cross product.

------
willholloway
This a good example of the kind of computational tasks that have me staying on
the top of the line of the latest generation of intel's desktop chips, and
overclock them when I'm running compute intensive stuff.

If I want to try out something new like this and play around with it, I want
it to be snappy.

I am vehemently opposed to the idea that PC's are "fast enough" \-- more
speed, less waiting!

The only compromise I make is avoiding the extreme versions of their chips, as
they can almost $1,000 more. But in the future I might change my mind on that.

Time is the one thing you can't buy more of.

------
ihenriksen
Very cool with small code pieces that does a lot. I was a big fan of the Amiga
Boot sector intro's back in the 80-90s where the programmers had to make
entertaining pieces of digital art where compiled code, music, and graphics
all had to fit inside 1 KB boot sector of a 3.5" floppy disks. Here are some
examples
[https://www.youtube.com/watch?v=GPTkTobvsaw&list=PLDAE2D6D92...](https://www.youtube.com/watch?v=GPTkTobvsaw&list=PLDAE2D6D92098FF88)
. Thanks for sharing! :)

------
reikonomusha
I personally wish the code was just written cleanly and idiomatically. I
wouldn't mind "300 lines of simple, idiomatic C++". What's the use of making
code dense if you're going to reformat it anyway?

(I say this in contrast to something which was explicitly "code golfed", which
this code seems not to be.)

~~~
strainer
I understand production and community code demands a most simple format but
for me this program is just a few braces and vertical breaks short of
perfection.

~~~
btmorex
You must be a fucking genius then because:

Vec w=nl, u=((fabs(w.x)>.1?Vec(0,1):Vec(1))%w).norm(), v=w%u;

is completely unreadable and that's just one line.

~~~
TillE
That really just needs some whitespace.

    
    
        Vec w=nl, 
            u=((fabs(w.x) > .1 ? Vec(0,1) : Vec(1)) % w).norm(),
            v=w%u;
    

Simple, right? I can see how it would look like garbage if you miss the three
separate variables being initialized, but that should be familiar to most
people with C/C++ experience.

~~~
strainer
% being overloaded as vector cross product is a bit mysterious here (until
investigating Vec's definition)

It might be helpful if the instantiations were written fully too, ie.
Vec(0,1,0) : Vec(1,0,0)

------
cloudrunner
A Python version-pySmallPT
[https://github.com/hanton/pySmallPT](https://github.com/hanton/pySmallPT)

~~~
Aardappel
Lobster version:
[https://github.com/aardappel/lobster/blob/master/lobster/sam...](https://github.com/aardappel/lobster/blob/master/lobster/samples/demos/smallpt.lobster)

------
user2994cb
Looks like it would make a nice glsl/WebGL fragment shader (might be even
shorter as the vector operations are predefined). Can't see one listed on the
page, but then it's mostly from a few years back.

~~~
user2994cb
Looking at it a bit more - the double recursion in the radiance function for
refraction would make things a bit awkward in WebGL.

------
deadcast
Great work and thanks for sharing the source! I love when I can see the output
from someone's program, get the source code, compile it myself and then get
the same results! :)

------
lukaszjb
For me it's more like 300 loc.

------
carlsborg
Legend.

------
ifree
cool!

------
gigatexal
I've no words. Very cool.

------
santaclaus
> 99 lines of 72-column (or less) open source C++ code

> #include <math.h>

> #include <stdlib.h>

> #include <stdio.h>

After g++ -E smallpt.cpp, how many lines is it? :0

~~~
archgoon
3325\. I'm not quite sure why you're asking. This isn't about the size of the
library, it's about the lines of code the programmer needs to write to express
an idea.

If you are concerned about the size of the final binary, then as the author
states, it's under 4k with a few optimizations.

~~~
echelon
That's kind of ridiculous. Leveraging something someone else wrote does not
mean you get to disclaim its role.

As an aside, this reminds me of XKCD's `import antigravity`.

~~~
Buge
It's the standard library. It's not a library for graphics. Might as well
credit your OS as well for providing a kernel that runs the program, and your
CPU manufacturer for making a CPU that executes the instructions.

[https://www.youtube.com/watch?v=5_vVGPy4-rc](https://www.youtube.com/watch?v=5_vVGPy4-rc)

~~~
santaclaus
Sure, but where does the standard state that a #include of a standard header
is a more proper atom than any other #include?

~~~
Arelius
For instance, in the C++ draft standard doc n3690. 17.6.1.3 you get

'The facilities of the C standard Library are provided in 26 additional
headers, as shown in Table 15.'

'<cassert> <cinttypes> <csignal> <cstdio> <cwchar> <ccomplex> <ciso646>
<cstdalign> <cstdlib> <cwctype> <cctype> <climits> <cstdarg> <cstring>
<cerrno> <clocale> <cstdbool> <ctgmath> <cfenv> <cmath> <cstddef> <ctime>
<cfloat> <csetjmp> <cstdint> <cuchar>'

and

'Except as noted in Clauses 18 through 30 and Annex D, the contents of each
header c name shall be the same as that of the corresponding header name .h,
as specified in the C standard library (1.2) or the C Unicode TR, as
appropriate, as if by inclusion'

Among other bits. It's made very clear that the standard libraries are valid
'C++'.

Even that aside, here are the used symbols from those libs:

atoi fopen FILE fprintf M_PI fabs sqrt cos sin erand48

Which are all resonable functionality to expect in any programming
environment. The first 4 are only used for I/O which are not exposed in the
language otherwise. And can also be safely removed and "GI" still functions.
It's just nice to be able to see the result.

The rest of the math functions are generally replaceable with varying amounts
of code or by calling platform native instructions.

The only real stand out is erand48 which isn't actually part of the standard,
but is easily replaceable by many other standard pseudo random implementations
something like 'double erand48(){return (double)rand()/(double)MAX_RAND;}' or
some such, or by implementing one of many pseudo random number generators.

In all, it's very fair to consider this 99 lines of C++ all the algorithmic
components of a path tracer are shown in the code. External code is well
within expectations. And there is no reason to consider the usage of stdio.h
as something to be counted, while discounting all the code that goes into
actually compiling and executing the code. Where the line is now is the most
obvious place to draw it and where all sane C++ programmers would.

~~~
ben0x539
> atoi fopen FILE fprintf M_PI fabs sqrt cos sin erand48

They finally standardized M_PI? Sweet, that was always awkward.

