## Toy raytracer in Python

By: on October 29, 2008

I spent a few days in Wellington recently with my 12-year-old brother. We somehow got to talking about ray tracing, and so we ran through a bit of linear algebra (vectors, normals, and dot products, basically) and built ourselves a raytracer in Python as a lark. We collaborated on the Vector and Point classes, and I filled in the rest of the program. There’s still a bug in the checkerboard code (see the odd horizontal lines in the reflection of the checkerboard in the yellow sphere?) but it’s not too awful for an afternoon’s idle hacking.

The code is contained within raytrace.py, and here’s the output it generates:

Wouldn’t it be nice to have classes like Vector and Point as part of the standard distribution of Python? Even better, though, and perhaps less subject to bike-shed debates, would be the inclusion of a simple, small PNG-writing module like this one as part of standalone base Python!

1. Rui Carmo says:

Pretty neat. Y’know, I’ve done a PNG Canvas library for a few years now:

http://the.taoofmac.com/space/Projects/PNGCanvas

It might be interesting to put all of them together somehow.

2. tonyg says:

Absolutely! I’d love to see a basic Canvas interface grow up, backed by (say) PNG as well as mappable to surfaces offered by the various GUI libraries out there. Sadly, even systems as beginner-friendly as Python still make it very, very difficult to get pixels on screen. A simple Canvas would be a big step in the right direction.

3. TuckerBeck says:

Both things you are looking for are already available.

Points, Vectors – > Numpy’s array objects (support vector math and very fast too )

PNG writer -> Python Imaging Library

Both are ridiculously easy to implement for this sort of thing.

4. tonyg says:

TuckerBeck, readily available yes, but not part of base python.

5. Frank says:

Hi tonyg,

that bug you describe concerning the lines of the checkerboard is probably not a bug, but something one calls the aliasing effect. Aliasing happens when you sample something with a too low frequency. You shouldn’t worry too much about this, however, since you’ve just dived into the matter, as you said. Why not try some fancy other raytracing stuff instead of dealing with the aliasing? 🙂

Keep on,
Frank

6. Peter says:

If you just want to play about with graphics, Nodebox is a great way to do it in python with all of that stuff built in.

7. tonyg says:

Peter: Nodebox looks lovely! It does seem to be OS X specific, though. I’d love for there to be a consistent, cross-platform approach to simple graphics in some reasonable programming language. (See: squeak smalltalk.)

Frank: It doesn’t look like an aliasing effect. It looks more like some kind of sign or rounding problem. Re: slightly fancier raytracing: the low hanging fruit, I’d say, would be CSG… 🙂

8. Victor says:

Hi tonyg, I’m trying to understand how intersection works in your code, but fail till now. Can you please explain what happens in intersectionTime function of Sphere class, and maybe outline how it would look for another primitive like Cone?

9. Paul Crowley says:

Victor: how is your vector math, are you familiar with dot products and the like? It’s hard to explain without diagrams, but if you try to figure out how you would implement intersectionTime then this implementation may become clearer.

10. Paul Crowley says:

Over a year later, I found that bug – you don’t take either the origin of the ray or of the plane into account when computing the intersection distance, only the dot product of their direction vectors. Have fixed in local version, shall I push to hg?

11. Victor says:

Hi Paul,
I didn’t find a bug yet, so it would be nice to see a fix 🙂 I’m ok with vector math, but still I don’t understand why the sphere works and my cone – not 🙂 to be more specific – here’s mine implementation (it renders something, which is definitively not cone :(:

12. Victor says:

oops the code is stripped out from the comment. you can find it here: http://etherpad.com/51Zq0cdWhf

13. Victor says:

I may be even wrong and it works, but I cant position it properly. Anyway – example output:
[URL=http://www.picamatic.com/view/6189859tmp/][IMG]http://www.picamatic.com/show/2009/12/04/07/57/6189859bigthumb.png[/IMG][/URL]

14. tonyg says:

@Victor: intersectionTime is meant (if I remember rightly!) to return how far along the given ray, if at all, an intersection between the ray and the surface of the object happens, in units of multiples of ray.vector (so a value of n returned from intersectionTime indicates that the first intersection happens at a point (ray.point + n * ray.vector)). I haven’t looked closely at your code, but from the presence of the quadratic equation it looks like you’re on the right track 🙂 What does it render currently?

15. Victor says:

@tonyg
actually looks like cone when reflected in the yelow sphere (I didn’t change the scene). I never managed to position the camer properly though 🙂 And my code fails simple tests in 2d, which shall work fine, so I suppose the equation is wrong somehow.

16. pieter says:

Damn! A raytracer under 400 lines of code. Readable code, no less! Shame about the speed, but it goes with the VM territory, I guess.. Does running it with Psyco make a difference?

17. tonyg says:

@pieter: I’m afraid I don’t remember 🙂 but since I left the code for loading psyco in there, I suspect it probably did make a difference. It’d be interesting to see what PyPy does with it.

18. Arek Czechowski says:

Hello Sir – I’ve taken your code and rewrote in coffeescript to compare speed, I hope you don’t mind. Code is here: https://github.com/agend07/coffee_raytracer

Arek

19. tonyg says:

Arek, that’s very cool! Thanks very much for letting us know.