11 ms·
The joy of building a ray tracer, for fun, in Rust
- Djejdbdbsjs123 5y agoA similar write-up with Julia: https://github.com/claforte/RayTracingWeekend.jl https://github.com/claforte/RayTracingWeekend.jl
- shmerl 5y agoCool! Just to clarify the idea, it's not rendering it on screen (I was first curious how you'd do that from Rust in a simple example), but rather saves image frames.
- fiddlerwoaroof 5y agoWhen I wrote my ray tracer, I set up a simple websocket server to stream pixels to a canvas element.[1] it’s a trivial way to do this without a ton of GUI code. [1]: https://github.com/fiddlerwoaroof/lisp-sandbox/blob/master/raytracing_in_one_weekend/canvas-server.lisp https://github.com/fiddlerwoaroof/lisp-sandbox/blob/master/r...
- shmerl 5y agoInteresting. I'd guess you could do it without GUI, but you still need access to something like WSI / Vulkan to actually get to the screen if you want to do it directly.
- a_e_k 5y agoIn the decade I spent working on RenderMan at Pixar, I learned just how immensely useful it was to have an image viewer running in a separate process talking to the renderer over a socket or pipe. (The Image Tool, or "It" is RenderMan's viewer.) Having it stay up even if you kill the render or it crashes for some reason and being able to flip back and forth to easily compare test renders across recompiles is game changing. If I were to start writing a new renderer, the first thing I'd do is to hook it up to an external image viewer over some protocol. These days, I find myself liking TEV (https://github.com/Tom94/tev https://github.com/Tom94/tev) a lot as a simple open-source image viewer that supports this and most other basic features that I'd want. See the links in the README for Python and Rust implementations of its protocol.
- deleted 5y ago[deleted]
- shmerl 5y agoYou can probably feed something to mpv that way too.
- tekknolagi 5y agoChris Wellons did something similar: https://nullprogram.com/blog/2017/11/03/ https://nullprogram.com/blog/2017/11/03/
- shmerl 5y agoYeah, that's exactly what I thought mpv could do. Good to see a real example.
- corysama 5y ago“Minimal code to put pixels on the screen?” is a question I’ve seen often enough that I’ve made a little gist to link whenever it comes up. https://gist.github.com/CoryBloyd/6725bb78323bb1157ff8d4175d42d789 https://gist.github.com/CoryBloyd/6725bb78323bb1157ff8d4175d... It requires https://www.libsdl.org/ https://www.libsdl.org/ But, making a window and putting some pixels in it, on all the world’s varied platforms, is the premier feature of that lib.
- shmerl 5y agoYeah, I figured you could use SDL. But probably more interesting to do it more directly with Rust and some WSI functions to get the needed surface (though it would be a lot more code).
- signa11 5y agowith multithreaded rendering, is it possible to use SDL ? anecdotally, in my cpu based ray-tracers (i have one in c++ and python so far) , i have found that SDL based rendering causes noticeable slowdowns to become quite useless after the initial novelty wears off.
- corysama 5y agoI've checked the code under the hood of SDL and it does pretty much the best you can do for fast uploads of an image from the CPU to the GPU. My CPU-based ray tracer uses the code I linked. SDL_LockTexture, go wide with threads writing into the locked buffer, SDL_UnlockTexture. If I skip the actual ray tracing, I get 1400 FPS uploading a 1024x1024 image.
- svdree 5y agoMiniFB is also nice for this purpose: https://github.com/emoon/minifb https://github.com/emoon/minifb
- boomlinde 5y agoWhen I want a quick way to get pixels on the screen I just print a PPM image to stdout and pipe my program into `feh -`. There are two PPM formats, both very simple: http://netpbm.sourceforge.net/doc/ppm.html http://netpbm.sourceforge.net/doc/ppm.html For example, the header printf("P6\n%lu %lu\n255\n", width, height); followed by width*height triplets of raw bytes representing the RGB values of each pixel.
- udbhavs 5y agoTangent: Nvidia also wrote a tutorial translating the C++ from the original Ray Tracing in One Weekend to CUDA code that runs on the GPU: https://developer.nvidia.com/blog/accelerated-ray-tracing-cuda/ https://developer.nvidia.com/blog/accelerated-ray-tracing-cu...
- glandium 5y ago> (python does the same FWIW) FWIW, python 3 doesn't do the same anymore: $ python2.7 -c "print(800 / 600)" 1 $ python3 -c "print(800 / 600)" 1.3333333333333333
- lrem 5y agoWhy are you talking about the experimental upcoming version though? ;)
- toxik 5y agoJokes aside, it’s been fifteen years now - Python 2 is really outdated.
- eesmith 5y agoNot only that, but it was clear that 1/2 == 1 was wrong 21 years ago, dating from PEP 238 -- Changing the Division Operator . https://www.python.org/dev/peps/pep-0238/ https://www.python.org/dev/peps/pep-0238/ Python 2.2 added "from __future__ import division".
- signa11 5y agoare there stats for the rendering ? that would be quite useful to compare similar implementations in other languages etc.
- eafkuor 5y agoThat raytracer is impressive. For a gentle introduction to raytracing, I recommend this book: https://www.amazon.com/gp/product/B07Q84TQ91/ https://www.amazon.com/gp/product/B07Q84TQ91/
- acdibble 5y agoThis looks neat. Am I understanding it correctly that it's written in pseudocode?
- the_gastropod 5y ago(I also went through and love this book). It isn't really even written in any code. It has some pseudocode to describe some things, but it primarily describes tests to write. It's a really excellent book to kick the tires of a new language you want to try out.
- deleted 5y ago[deleted]
- throway453sde 5y agoDoes the 3d games today still use ray tracing or some other algorithm?
- wk_end 5y ago3D games have basically never used ray tracing. They're starting to, a little bit. Some old 3D games (think Doom-era) used an algorithm called "ray casting", which is similar to ray tracing but much more primitive and much faster - among other simplifications, instead of one ray per pixel, you only have one ray per column.
- Narishma 5y agoI might be mistaken but I don't think Doom uses ray casting.
- boondaburrah 5y agoI think it did but only in one dimension to determine wall distance and then faked everything else.
- russtrotter 5y agoThe early Doom 1 and 2 being software renderers, used ray casting (386 protected mode FTW to boot!) The later versions though, were part of the first wave of games to leverage GPU cards. I don't know if those newer releases still kept any ability to render purely in software though.
- Narishma 5y agoSee my reply above https://news.ycombinator.com/item?id=29797336 https://news.ycombinator.com/item?id=29797336
- dahart 5y agoDoom’s predecessor Wolfenstein 3D was famous for using ray casting, and the Doom engine inherited ray casting and extended it to handle different floor heights. https://lodev.org/cgtutor/raycasting.html https://lodev.org/cgtutor/raycasting.html
- lloydatkinson 5y agopub trait Hittable { fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord>; } pub trait Scatterable { fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Srgb)>; } I don't know rust but familiar with the Option type. Why would you not create a type that composes a Ray and a Srgb value, something like this? struct RayColour/RayWithSrgb/etc { Ray, Srgb } Seems like it would be a nicer API.
- vbrandl 5y agoOption<(Option<Ray>, Srgb)> can be one of - None - Some((None, Srgb)) - Some((Some(Ray), Srgb)) Only the last one is equivalent to the struct you described
- lloydatkinson 5y agoI'm confused then, you're saying you can render light without a ray?
- vbrandl 5y ago~I'm not sure about the semantics, but the type would allow that, yes.~ Edit: fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, Srgb)> The function takes a `Ray` as input, so you cannot render light without a ray. The return value might be one of the cases listed above. The `None` case might be something like a black hole: a ray hits the object and nothing happens. The `Some((None, Srgb))` case would be a non-reflective surface, it changes the color if hit by a ray but does not reflect the ray further. The `Some((Some(Ray), Srgb))` case would be a reflective surface, that changes the color and reflects the ray. Again not sure about the exact semantics and I didn't read the blog post yet, but that would be my guess.
- schoen 5y agoI suspect the nested option type in this particular implementation has to do with the step-by-step implementation in the guide, as you gradually add various features one at a time. As a result, you might end up with some features in your code that reflect less-general behaviors from earlier chapters, if you don't fully refactor them as you go along. https://raytracing.github.io/books/RayTracingInOneWeekend.html https://raytracing.github.io/books/RayTracingInOneWeekend.ht... I don't think there are actually any cases in which you can usefully do something with the color but without the ray (the reason that the ray output is optional is that the ray might be absorbed rather than reflected, but in this case I think its color becomes meaningless, or becomes the equivalent of (0.0, 0.0, 0.0)). However, someone implementing this based on the tutorial might not think of it that way, because the different features in question were added separately at different times. In the original C++ example in the tutorial, there is a boolean return value indicating whether there is an output ray or not, which I think corresponds to the Some case for the option type here. As there is ultimately only one boolean, not two, and as material implementations are expected to set both the ray and the color when scattering occurs, I think it's correct that you could combine the vector and color return values into a single struct wrapped in a single option type, at least with the implementation strategy that the tutorial is suggesting. I worked through about 90% of the guide in Python and then about 60% in Rust; this article definitely makes me want to pick it up again. Edit: > The `Some((None, Srgb))` case would be a non-reflective surface, it changes the color if hit by a ray but does not reflect the ray further. While this feels like a plausible guess, I don't believe it actually aligns with the strategy suggested in the tutorial. See section 8: https://raytracing.github.io/books/RayTracingInOneWeekend.html#diffusematerials https://raytracing.github.io/books/RayTracingInOneWeekend.ht... Diffuse materials (non-specular reflection) are implemented by having them scatter incident light in a random direction, possibly with some attenuation and some change of color. The change of color, though, is only meaningful when rays are scattered. See also section 9.3 https://raytracing.github.io/books/RayTracingInOneWeekend.html#metal/modelinglightscatterandreflectance https://raytracing.github.io/books/RayTracingInOneWeekend.ht... (talking about how you can either scatter every ray and attenuate its intensity, or scatter a fraction of rays with no attenuation but absorb some rays at random, with the same statistical result on the output)
- dkga 5y agoVery good and encouraging intro. Does anyone know of a good raytracer using Rust that leverages GPUs on Metal or Vulkan?
- Reventlov 5y agoThe earth rendering looks funky, my guess is that there is some projection error because everything is too wide (across the equator). Nice post :)
- jffry 5y agoAntarctica looks too large, so I'm guessing that the implemented uv-mapping and the projection used to create that rectangular Earth texture are not the same hence the distortion p.s. the Earth is also rotating the wrong direction!
- slightknack 5y agoOh, I did something like this a while back as well! Aside from ray tracing, my renderer also supports ray marching, so it can render some cool fractals[1]. Writing path tracers is so much fun, love the write-up! [1]: https://github.com/slightknack/keikan#readme https://github.com/slightknack/keikan#readme
- deweywsu 5y ago
- bestouff 5y agoTo each one its own. After >40 years of programming, and being quite bored, Rust has been a breath of fresh air and made me interested again in coding. (Edit: yes I know I'm feeding a troll)
- frederico02 5y agoNice article. Not everything is an expression in rust as the author mentioned though.
- Diggsey 5y agoWhat's not an expression?
- jamincan 5y agoThere aren't many; basically just variable declarations: https://doc.rust-lang.org/reference/statements.html https://doc.rust-lang.org/reference/statements.html
- tialaramex 5y agoTrue, but almost everything is. The Book's entry on statements is like four paragraphs, while the entire rest of that chapter is about expressions. For example all Rust's loops are expressions. This isn't very useful in many cases, but breaking an ordinary loop can yield a value, so e.g. you can loop checking all the integers until you find one you like, and then return it as the value of the loop. Nice.
- sophacles 5y agoDetail that sometimes bites me: the `loop` looping construct is an expression that can yield a value. `for` and `while` technically are expressions, but they can only ever yield `()` (unit type). Coming from python, i feel doing something like this: let x = for v in iter { if cond(v) { break v; } } else { default_v } would be nice, but this was explicitly rejected by the core team. So for now anyway, only `loop` can break with value.
- tialaramex 5y agoThe thing is, loop gets to break with value because it otherwise doesn't end (so the type of the loop when it doesn't break is never aka ! and that's compatible with any type), but a for loop will always end anyway, so what's the value of the for loop when that happens? In your example code, I see there's an else clause on the for loop which is presumably special syntax so as to provide the value if it doesn't break, but that feels pretty clumsy to me and I'm not sure there are many cases where it's more readable than what I'd do now. If I could see a nice way to do it without such clumsy special syntax, I'd favour this, but with the else syntax (or some equivalent) it feels like it doesn't pull its weight.
- qqumut 5y agoFinally a good book recommendation about Rust. The official Rust book is poorly written and messy.
- kerneloops 5y agoIt's also a undergrad course lab here at SJTU, also in Rust: https://github.com/aik2mlj/raytracer-tutorial https://github.com/aik2mlj/raytracer-tutorial
- rashidujang 5y agoWas pleasantly surprised to learn that the author of this blog post was none other than the CTO of Stripe! Glad to see a CTO who's still hands-on and even willingly blogs about making so-called 'beginner' mistakes with the float/integer behaviour in Rust. Big props.
- dkga 5y agoNice! This made me think that working in tech at Stripe is likely to be more fun and productive than in other comparable places. I believe in experts leading, and I also believe in leaders showing their human face - the author did both :)
- netizen-936824 5y agoThis is something I like seeing as well. Show your mistakes and let the rest of us learn from them. Real development is messy and shit happens, its good to show beginners that this is normal and expected
- friedman23 5y agoRayon really does feel like magic.
- mschaef 5y agoThis article hit home... I took a similar approach and spent bit of time over the summer to familiarize myself with the language by writing a ray tracer. (Super simple, not as fancy as the one in the article.) https://github.com/mschaef/rust-rt https://github.com/mschaef/rust-rt After spending as much time lately as I have in Scala and Python, my immediate reaction to Rust was quite positive. Python has performance issues from 1985 and the build ecosystem seems borderline chaotic. (Made me seriously miss Maven, etc.) Scala seems a lot like C++ - an amazing intellectual accomplishment, a great place to spend all of your time, but not so good as a part time language. (I spend a bunch of my time in Scala mentally expanding out shorthand notation the way I might be mentally macroexpanding in a Lisp.) Rust, in contrast, seems to have struck a nice balance between expressive power and runtime performance. Expressively, it has a lot of what I like about Scala with a syntax that makes more sense to my C-style upbringing. Performance seems to be everything I'd expect from the fully compiled language that it is. (In terms of performance, there's no way Python would've let me get away with some of what I got away with in my Rust ray tracer.) Given that the language gave such a positive initial impression, the questions I still have are more about what it feels like in the large. ie: Working with a significantly sized team jointly on a codebase that might last 1, 5, 10 or more years. (Even then, I'm pretty optimistic.)
- dblbaguette 5y ago
- ArtWomb 5y agoAwesome! Danger here is that once you get started down this path, one becomes really tempted to build a full commercial renderer ;) Next step: add global illumination. Rayon is the key here, and having access to more cpu cores. Ray Tracing And Global Illumination (2003) https://digitalcommons.unf.edu/cgi/viewcontent.cgi?article=1100&context=ojii_volumes https://digitalcommons.unf.edu/cgi/viewcontent.cgi?article=1...
- toxicFork 5y agoI guess this thread is the best place to ask for this: Do you know of good open source real time (preferably gpu based) ray tracing engines out there? That can be used for games? I see a bunch of lists but want to get your expert opinion. E.g. https://awesomeopensource.com/projects/raytracing/real-time https://awesomeopensource.com/projects/raytracing/real-time
- barrenko 5y agoLove me some shiny balls. Great approach to the topic, wonderful blog design as well.
- rowanseymour 5y agoEvery time I read another one of these articles I have to ask myself what kind of a programmer am I if I've never written even a basic ray tracer. Seems like a fun hobby project as you can iterate for eternity. I even initially got into programming thru playing with Povray for school art projects.
- fsloth 5y agoYou can just type in Peter Shirley's Ray Tracing in One Weekend to a language of your choice for an easy start :) https://raytracing.github.io/books/RayTracingInOneWeekend.html https://raytracing.github.io/books/RayTracingInOneWeekend.ht...
- dps 5y agoHi folks, article author here. I just wanted to stop by to say I'm delighted so many folks enjoyed the piece. I really did have a lot of fun :-)
- likeabbas 5y agoGreat article! Is there any chance of bringing Rust into your place of work? :)