This book series is great—I built an entire pathtracer in R (rayrender)* following this series. I'd recommend reading these books and then reading Matt Pharr's "Physically Based Rendering", also available for free online (http://www.pbr-book.org). Starting with a pathtracer based on Peter's book and then slowly integrating elements of PBRT (the pathtracer featured in PBR) as needed has been a great pedagogical exercise: RTIOW teaches using a bottom-up approach, while PBR very top-down. If you've written the pathtracer described in RTIOW, you end up with a much fuller understanding of many of the choices made in PBRT.
Also, PBR is getting a fourth edition this year that goes into fully GPU rendering—it's a great time to start learning about pathtracing!
Raytracing is surprisingly simple if you already know trigonometry.
If you don't know Trig... you'll have to brush up on your math. But fundamentally, you project rays, you calculate if those rays hit an object (and if so, which object is "in front", and therefore hit first). And finally, you apply BSDF functions (math formulas created by artists that specify how light bounces off of objects) to calculate where the light bounced to.
That's it. The BSDF is an abstraction: there are many different models: one for metal, one for "specular" ("shiny" objects, like a marble), one for "diffuse" ("soft" objects like an egg), etc. etc. And an "uber BSDF" that kind of provides sliders for many different objects. (Invented by Disney: Roughness, Metalicness, Specularness, Diffuseness). But the BSDF itself is just an abstraction: where did the light bounce to? And different objects have different properties.
------
From there: making a FAST raytracer is the hard part. What kind of data-structures are needed to calculate intersections more quickly? Understanding the hardware (SIMD-cores, maybe even GPUs) to calculate BSDFs in parallel. Etc. etc. Lots of little things to learn and ultimately complicating the thing.
A professional raytracer would also have special code for the different kinds of objects in the scene.
True random path tracing takes too long. Biased techniques and quasi-random paths can make smoother images with less effort. Unbiased techniques shy away from such "theoretically suboptimal" optimizations. Denoising. Multithreaded, multi-computer, multi-GPU support. The feature sets goes on-and-on-and-on.
But if you want to just make "a" raytracer, without any care in the world about how fast (or slow) it is, then you easily can do that in one weekend. So give it a shot if you like.
They brute-force an all-to-all check against all objects. There's no oct-tree partitioning or BVH trees. The only shape supported are spheres of varying sizes (no squares, no triangles, no cubes. Just spheres). There are only three BSDF functions implemented (specular "hard and shiny", diffuse "soft and matte", and glass). This tiny program is inefficient as all heck, but hey... 99 lines of code is pretty spiffy. That's all you really need for a raytracer.
This is what made it such a fun project for me. I don't enjoy really complex math, but I love optimizing and refactoring and making things more modular and achieving new visual effects. So I was able to just drop in a couple of key equations from the internet, and I was off to the races and got many hours of enjoyment from the infinite tail of possible improvements. Plus, you get really satisfying output really early on ("in a weekend"), which is a big motivator for me to keep going.
The way I see it: the linear algebra parts are just optimizations.
You can probably??? write a raytracer using Trig alone: it just means that you'll be using sin/cos on angles instead of "short-cutting" with a dot-product to determine normal vectors.
Hmmm... well... maybe the concept of dot-products and orthogonal vectors will help. So Linear Algebra probably will make things easier if you studied that too... but I still think its kinda optional.
I think of it not exactly, but sort-of, the other way around: using trig without any linear algebra is a shortcut to doing the linear algebra. Or maybe it’s just that the trig stays the same, but it’s possible to hard-code or short-cut your transforms to not include matrices or dot products explicitly... in simple cases. In reality trig and LA aren’t in competition, they’re both necessary in a serious renderer, and they represent different tools and different views on vectors.
Sines and cosines fall out of rotations because of the implicit circle involved, but it’s more general to think of a transform between two basis frames. It just so happens that the lengths of basis vectors of one frame projected to the other are computed using sines and cosines, but the transform and dot products are there whether they’re made explicit or not.
Using only sines and cosines falls apart very quickly, here are a couple of examples:
Compute a surface normal under non-uniform scaling. You need the inverse transpose matrix. I don’t even know how to derive it using trig.
Implement instancing or a character rig in your ray tracer. As soon as you need to compose transforms just to trace a ray, the trig route is off the table (impractical in the extreme), and matrix math is required. An alternative might be geometric algebra - is that in the same boat with linear algebra for you?
the word trigonometry comes from the greek word "trigono" (triangle) and "metro" (count) which quite clearly describe what trigonometry covers: counting triangles (ie sides and angles). Its history dates back to at least the ancient greeks (if not more/other civilizations, I do not know). Linear algebra is a different field of mathematics which focuses on solving systems of linear equations which has a LOT of applications (even in other non applied mathematics). I think it is better if you do not try to think one as part of the other.
trigonometry provides the trigonometric functions, their relationship and a few basic equations. Linear algebra provides vectors/matrices. You need both of them to model 3D objects and vectors interscting with them (aka rays hitting objects)
Can you even get the angle between two three dimensional vectors without using some sort of vector product and venturing into linear algebra territory?
The more I do this, the more I realize that the linear-algebra approach might be easier... but the Trig approach can certainly do what you are asking for.
I mean, I recognize that I'm just "reinventing cross-product" here to get the length of opposite-leg and the direction of adjacent-leg. But it does seem feasible to do all of these things in Trigonometry alone.
A little bit of Linear algebra (cross product creates an orthogonal vector) would definitely make this problem easier.
Ok, hmm I'm still a bit lost though, those identities work on a right triangle, but how do you construct a right triangle between two 3D vectors A, and B?
> Ok, hmm I'm still a bit lost though, those identities work on a right triangle, but how do you construct a right triangle between two 3D vectors A, and B?
(0,0,0), A, and B are the three points of a right-triangle.
Hmm... okay, yeah. I see what you mean. Every time I think about how to do things, its linear algebra time.
The thing is: I've had this feeling before in other maths: the more you study one math subject, the "worse" you get in another. There's surprising things you can do with algebra alone without any calculus... but once you learn calculus, you kinda forget those old algebra tricks.
>(0,0,0), A, and B are the three points of a right-triangle.
Your next line suggests you already know this, but just so it's clear it's not, they are three points in an arbitrary triangle. Each vector has a right triangle between the origin and (two of?) it's axis, but I'm not sure that's helpful.
Yeah, I agree in that it feels like you should be able to do this, but I just know that I've been stumped in the past, and can't figure it out now either.
Apparently, MY trigonometry is awful and needs more practice! Law of sines is basic 'non-right triangles' stuff.
There are three legs: A-0, B-0, and B-A. Hmmm... B-A seems like a vector operation (but its a really easy one, so... probably can be figured out without formally studying linear algebra).. That gives us the "A", "B" and "C" for lengths.
Aaaannnnd I'm stuck again. I have the lengths but I don't have the angles. I only need to find one angle and the other two are solved with law of sines.
Maybe a system of equations at this point (lol, another subject where linear algebra helps). The law of sines gives 3-unknowns, but only 2-equations. So I still need one more constraint before I can math-out a solution.
------
EDIT: Law of sines is fully:
sin(a) / A = sin(b) / B = sin(c) / C = d
Where "d" is the diameter of the circumcircle of the triangle. So the circumcircle's diameter gives us the 3rd equation needed to solve the above system of equations (using non-matrix math).
So yeah, I think its possible. Its just waaayyyy harder to do. I also am handwaving a difficult part. I guess... take the average of 3 points, which would give the center of the circumcircle. Then sqrt(x, y, z) from the center to find the radius, then 2*radius = d.
Wow, that's a lot of work compared to an easy projection matrix...
The average of the 3 points would give you the triangle's centroid, not necessarily its circumcenter. You can still calculate the circumradius from a, b, and c: https://mathworld.wolfram.com/Circumradius.html.
Rarely do artists create the BSDF, generally a general model BSDF is provided by an engineer or scientest, and the parameterization is tweaked by an artist.
> that specify how light bounces off of objects
Technically a BSDF generalizes all light interaction with medium. a BRDF, is the function that describes how light bounces (reflects, the R in BRDF) off of objects, which is sufficent for raytracing solid, non transparent/translucent
objects.
> And an "uber BSDF" that kind of provides sliders for many different objects. (Invented by Disney
The idea of an uber BSDF wasn't "Invented by Disney" Brent Burley did develop Disney's principled BRDF at Disney, but it builds on a long foundation of uber BRDFs at VFX studios. He did popularize, if not invent the idea of a metalness map. It was however already possible to represent metals in existing uber shaders by just using a standard full color specular lobe.
> Understanding the hardware (SIMD-cores, maybe even GPUs)
I'd add understanding memory cache hierarchies, and their affect on performance highest among those.
> He did popularize, if not invent the idea of a metalness map.
I don’t know what the precise differences are, but FWIW we had an uber shader that we called “uber shader”, with a metalness map (a texture driven metallic BRDF lobe, not just full color specular) at PDI in the late 1990’s, written by Sing Choong Foo (who worked on the Lafortune model). I’d guess Pixar and other studios had one too by that time.
Thanks for the info, I knew even my statement might have been a stretch.
I'm not even sure Disney can be fully credited with popularizing it, I think it was becoming important in videogames with packed GBuffers almost concurrently.
The same author also has "Ray Tracing: The Next Week", which introduces a lot of the missing features you mention.
Chapter 3 introduces BVHs, Chapters 4-5 add more surfaces, and Chapter 7 adds cubes. There's a third book ("Ray Tracing: The Rest of Your Life") that tackles more sophisticated approaches like importance sampling.
A great project for learning a new language: You have a chance to use abstractions to implement your own vectors, etc. You have a chance to unit test your vectors and vector math. Performance is important, and you have a chance to utilize multi-threading for positive effect. There's no complicated external dependencies like drivers, etc; it's just basic input and output streams and some math in between. It's not too big and not too small, a few hundred lines is just right for creativity and experimenting without taking months.
Wrote my first ray tracer at 15, wrote a demoscene realtime ray tracing tutorial at 17, am now 37 and have been writing ray tracers professionally for over 10 years. Have also had the pleasure of teaching ray tracing to sort of everyone who'll listen (some went on to do PhDs, one became a lead gfx programmer on GTA 5, the 3rd most recent was 12 years old, ...), it's like a nerdy Jehovah's Witness / Monte Carlo Methodist Church.
It's been pretty great; realtime rendering gets all the hype, but offline rendering is where realtime is headed and this is a super exciting time with all this parallel computation around.
Curious what you mean by this. Just that realtime will start using more and more offline techniques as hardware speeds up? Or are you talking about pre-rendered scenes?
There are techniques you can do with ray tracing that can't (efficiently) be done with rasterisation, and as the amount of hardware ray tracing power increases, it will make increasing sense to ditch the extremely complex rendering pipelines we have now and just do (relatively) simple ray tracing.
Eventually desktop GPU drivers will emulate "old" rasterised games on ray tracing hardware.
Happened to move to New Zealand from South Africa, immediately met up with the Indigo Renderer guy whom I'd spoken to previously through my tutorials on an ancient website, in the FlipCode and cfxWeb days.
The main thing is actually spending a lot of time writing ray tracers... you should write your first 5 ray tracers as quickly as possible :)
This is a really great book! I went through the first one and implemented it in Rust. Was a great learning experience both in ray tracing and in Rust (my first ""real"" project with it).
I really recommend! Much cooler (IMO) then doing another TODO list app. I also recommend going to google to more deeply understand some of the algebra concepts that are used (since they aren't really explained in a deeper level and assume you already know).
> Naive question: how do you draw graphics in Rust? (Or "go"?) Some wrapper on top of OpenGL?
The beauty of raytracers is that you don't need any graphics support at all. The output of a raytracer is a frame. The simplest way is just an array. Compute your color values, save them to array positions.
Of course, you eventually will want to see the result. You can save an image to a file then. One of the simplest formats is PPM. The PPM format requires a header, afterwards you just push RGB values. Which means you don't even need a library (http://rosettacode.org/wiki/Bitmap/Write_a_PPM_file#C)
If your question is not about raytracers, but more general, there's countless ways.
Are you drawing in 2D (and not a game)? You can use the OS's own native functions for this if you are brave. Since Rust can call C libraries, this should work. Draw in memory, then "blit"(i.e. copy it over) to the appropriate buffer. Under Windows, you could use the Win32 API directly if you don't value your time :)
Alternatively, you can use a library that will make your work much simpler. There are many cross platform UI libraries, some of them with Rust bindings. And some Rust-specific ones too Check https://www.areweguiyet.com/
If you don't care about GUI controls and are mostly drawing images (like in games!), there's SDL. That will take care of most of your needs.
If you are doing it in 3D, you could use OpenGL. I've done that with my raytracer back in university. Same principle applied: I would generate a scene in memory, then copy to a texture and display it. Geometry was just two triangles. You could do a similar thing.
There are lots of options for graphics in Rust, so it's really just a choice of which graphics API you'd like to use or learn more about.
(full disclosure: I contribute to some of these crates)
For example, there is an implementation of WebGPU (https://github.com/gfx-rs/wgpu-rs) that can run both inside and outside of the browser that has been growing in popularity lately.
There are also bindings for Vulkan (ash, erupt), D3D12 (d3d12-rs), Metal (metal-rs), cross-platform abstractions at various levels (gfx-hal, wgpu), OpenGL (glow, glium), etc.
It's also possible to simply render graphics to an image (a pretty common approach for Raytracing in One Weekend) or blit pixels onto a window surface.
In this case you just do it from scratch :) I didn't use any external libraries. You generate a PPM image https://en.wikipedia.org/wiki/Netpbm and then open the image from anything that opens a ppm image (open in mac works)
Fantastic idea. On the image format part, PPM is indeed a great way to get off the ground.
If you really want something more portable like PNG then it happens to be quite a fun library to implement. I encourage you to write your own. It’s basically:
- some magic number bytes
- a static header to announce the image is 8bit RGB zlib-compressed unfiltered non-interlaced
- a final set of magic bytes to say “here’s the actual image”
- zlib compressed rows of RGB bytes, with a 0 at the start of each row
So really just a fancy image.ppm.gz. What’s cool is that with 10 more lines of code you can implement animated PNG.
I went through this set of lessons a couple of years ago. It is well worth doing. Interested folks may also want to take a look at Gabriel Gambetta's "Computer Graphics from Scratch" [1].
Both tutorials are excellent, but Gabriel's explanations just gelled a little bit better with my personal learning style.
Oh man. I almost got through this with Python before some planned huge life change took over last year. I just couldn't get the final bit figured out but maybe this is the universe telling me I should attempt debugging it again :). Plus, not 24h ago some Michael Abrash notes were featured here on HN.
For anyone curious, I implemented it at https://github.com/skytreader/praytracing in a way that's Pythonic, more software engineer-y, with experiments on the code outlined in the comments. It also has no dependencies (save mypy) which means it could turn your computer to a radiator for a short while, perfect for winter.
(Also used it to benchmark and experiment with PyPy, but that's another story.)
The worst part about computer graphics is finding an easy way to get the damn image rendered. I applaud the ingenuity of the author to simply output the image to a file and open it like that.
Even basic GDI and OpenGL is monumentally frustrating for beginners who just want to draw a simple rectangle to the screen.
I’m a big fan of code environments that are set up to make graphics easy, and some do exist. Maybe the most popular one is Processing https://processing.org/ Processing defaults to Java, but it also supports python code and 3d GPU graphics.
There are lots of other projects like this too. One in particular that I have loved is NodeBox (nodebox.net/code/index.php/Home.html) - a Mac app where you write super simple code in python 2, and the output can render directly to image or PDF or QuickTime. There’s a similar python 3 project called DrawBot (drawbot.com). These both come from a lineage of forked open source projects over many years, so there are quite a few others.
https://www.lihaoyi.com/workbench-example-app/raytracer.html is a ray tracer I wrote in a few hours; maybe a single afternoon's worth of time. I already knew my way around 3D vector math from my previous work making toy games in school, but even so it's surprising how short and easy the code is.
It's about ~300 lines of Scala and runs in the browser via Scala.js. The page has a link to the source code. Could definitely be sped up or improved in a myriad of ways, but even as is I think the output graphic is pretty impressive given the input code size and programmer effort!
Little by little. It’s okay to get it working without understanding all the parts, and revisit or rewrite sections later. It’s also okay (not just okay but recommended) to let this take longer than a weekend, and dive into any rabbit holes that interest you as you go. It will take longer than a weekend to fully understand all the parts, much longer if you want to do it well enough to be able to write all the parts on your own.
Personally I absolutely love learning math and using it to code up programs that make pictures, learning and applying the trig and linear algebra is pure fun for me, so if it interests you go for it, I say. :)
Becoming fluent with a dot product is maybe the most useful single tidbit of math you could learn...
Yes. Though FWIW I think math classes, graphics classes, and just practicing graphics over the years and crawling into rabbit holes, got me more familiar with all the math needed to do ray tracing than anything required for a CS degree.
Essentially markdown plus ASCII graphics, and it can "resolve" itself into HTML by adding a little JS footer to the end (AFAIK it's also possible to convert to HTML offline, like a conventional static site generator).
It's a nice concept, but I don't know why we don't just have decent native browser support for Markdown. And even if markdeep is necessary, it shouldn't totally fail if JavaScript is unavailable.
Because it would be yet another baked in browser feature which needs to be standardized, with every browser vendor having their own interpretation of the standard, or deciding not to implement it all. Adding new features via Javascript (or WASM) modules instead of bloating browsers with new high-level features is the better long-term strategy IMHO.
They’re both overloaded terms, but with today’s common usage, path tracing is built on top of ray tracing, so path tracing in a sense is ray tracing (but not the other way around). So while it’s true that they’re not exactly the same, if that’s what you meant, your sentence as written can be easily misinterpreted.
Is there a reason to make this distinction here? The site & book are doing ray tracing, so the title is accurate. It mentions path tracing once at the very beginning, because the end goal is a path tracer. I think the goal is achieved, so in this case the book is both ray tracing and path tracing.
> Is there a reason to make this distinction here?
The original method, by Whitted, that looked much better than any previous methods, and yet looks horrible by today's standards, is ray tracing. It had no global illumination.
The method based on solving, explicitly or implicitly, the rendering equation by James Kajiya, and one that has built-in global illumination, is path tracing.
The distinction is not important for buzzword hijacking hacks, marketing gimmickers, and snake oil salesmen.
I could broadly agree with your first two sentences, even though it's not actually true that ray tracing didn't have global illumination before the Rendering Equation came along. Recursive ray tracing, diffuse inter-reflections, distribution ray tracing, all these things existed before the term path tracing came along. Path tracing is more of a unifying formalism than a distinction between global illumination and direct lighting.
I'm completely stumped by your last sentence though, I have no idea where your anger is coming from. But it fails to explain why you want to make the distinction in this thread, since, as I already pointed out "Ray Tracing in one Weekend" is doing both (for example chapters 1-7 are the mechanics of ray tracing, no path tracing concepts are used until chapter 8). Are you referring indirectly to any specific products that have marketing you don't like? Your story can't explain people who use the term "ray tracing" since they're being honest about not necessarily doing path tracing. So are you talking about someone who uses the term "path tracing" when they're not doing global illumination?
What snake oil is being sold here with a free book? Who exactly is a hack? Sure, there's some technical differences between the two terms, but outside of the research community they are used interchangeably.
Also, PBR is getting a fourth edition this year that goes into fully GPU rendering—it's a great time to start learning about pathtracing!
*Rayrender Github: https://www.github.com/tylermorganwall/rayrender
Website/Docs: https://www.rayrender.net