[HN Gopher] GPU ray tracing tutorial - 10 articles
       ___________________________________________________________________
        
       GPU ray tracing tutorial - 10 articles
        
       Author : henkie_bk5
       Score  : 133 points
       Date   : 2022-06-15 17:48 UTC (5 hours ago)
        
 (HTM) web link (jacco.ompf2.com)
 (TXT) w3m dump (jacco.ompf2.com)
        
       | dahart wrote:
       | This is awesome! I've been wanting to see what it would look like
       | in OpenCL.
       | 
       | Side note, I hope we graduate away from the terms BLAS & TLAS
       | soon. They make the most sense for a strictly 2-level hierarchy,
       | but the power of ray tracing comes from multi-level, where "top"
       | and "bottom" are ambiguous. This is why OptiX uses IAS (Instance
       | AS) and GAS (Geometry AS) instead.
        
         | jbikker wrote:
         | Well if you have more than 2 levels you can always collapse,
         | which is the preferred solution. Just walk the scenegraph, and
         | flatten the matrices. Now you're left with an array of BLASses,
         | each with their final matrix.
         | 
         | RTX has hardware support for this flattened structure only;
         | OptiX will become significantly slower if you force it to have
         | more levels.
        
           | dahart wrote:
           | Yes, very true. Or at least you can usually collapse. Memory
           | is the issue, and collapsing multilevel doesn't always fit.
           | 
           | There is some cost in the current hardware to multi-level
           | traversal, that's true. But still top & bottom terminology
           | isn't future-proof, doesn't translate to CPU, and we might
           | not be limited to 2-level GPU traversal forever. More to the
           | point perhaps is that "top" and "bottom" aren't words that
           | describe the function. It'd be better to have terms that say
           | what it does rather than where to find it, right?
           | 
           | Full disclosure, I work on OptiX. (But to be clear I
           | literally have no idea if/when we might see multilevel
           | traversal in hardware. I just happen to be in favor of seeing
           | it someday, and if/when it does, TLAS and BLAS will become
           | more awkward or get replaced.)
        
       | henkie_bk5 wrote:
       | The last article of the series on BVH construction, ray tracing
       | and GPU ray tracing (using OpenCL) is now complete: 10 articles
       | cover the basics as well as advanced GPGPU topics.
        
       | jazzyjackson wrote:
       | I got rejected by the server so here's an archive
       | 
       | https://web.archive.org/web/20220615174927/https://jacco.omp...
        
       | skratlo wrote:
       | I find their C++ style particularly appalling.
        
         | jazzyjackson wrote:
         | It's just ANSI style without extra newlines, a matter of taste
         | I suppose.
        
         | alar44 wrote:
         | It really is hideous. I think they skipped newlines to keep
         | line count as low as possible. /Eyeroll
        
       | rollulus wrote:
       | Based on the name of the author, Jacco Bikker, I can blindly
       | recommend this. He has decades of experience and knows how to
       | combine modern computer graphics algorithms from the academic
       | world with real world low level optimizations and a sauce of
       | magic to create state of the art ray tracers.
       | 
       | Disclaimer: my master thesis work was part of his PhD
       | dissertation. As a teenager I read his articles on flipCode and
       | due to sheer coincidence he ended up as my thesis supervisor.
        
       | jeroenhd wrote:
       | Back when I took the author's course on computer graphics in
       | Utrecht (took me a few times to pass it, by no fault of his) I
       | thought it was very strange that the course started out with ray
       | tracing rather than traditional GPU rendering. After all, when
       | you think graphics, you think OpenGL/Vulkan/DirectX, right?
       | 
       | Only after having to implement both types of renderer do you
       | really get an appreciation of how elegant ray tracing really is
       | in comparison. The basic ray tracer from this tutorial clocks in
       | less than 200 lines of C++ excluding the headers! Then there are
       | optimisations like BVHs/BLAS/TLAS which are all so simple to
       | think and reason about compared to the inner workings of a GPU
       | rendering pipeline.
       | 
       | I should find the time to go through this guide again and find
       | out how I can get more performance out of my old ray tracer now
       | that I've grown a few years older and wiser.
       | 
       | This tutorial is more about optimizing a ray tracer than writing
       | one from scratch. If you're looking to learn the basics, I
       | recommend reading through the tutorial the same author wrote
       | eighteen years ago [1]. It covers the more basic concepts of a
       | ray tracer without telling you exactly what to copy paste unless
       | you "cheat" and download the code archive, which is a great way
       | of teaching concepts to programmers in my opinion, as it gives
       | you the opportunity to think for yourself.
       | 
       | With modern C++ you'd probably want to write your code a bit
       | different (VC++ 6 wasn't the best C++ even at its time) and the
       | compute limitations at the time are dwarfed by even your average
       | integrated GPU, but the core concepts haven't changed.
       | 
       | [1]:
       | https://www.flipcode.com/archives/Raytracing_Topics_Techniqu...
       | (I needed to fix the encoding for the page in Firefox to get the
       | math to show up right)
        
         | kragen wrote:
         | I've written several raytracers and rasterizers that are
         | smaller than 200 lines of C++, though quite likely they're
         | worse pedagogically than Jacco's (slashdotted, but available at
         | https://web.archive.org/web/20220615174927/https://jacco.omp...
         | ) tutorial, and they also don't illustrate useful
         | optimizations. Hopefully, what mine lack in cluefulness and
         | performance they make up in breadth, diversity, and brevity:
         | they are written in C, C++, Python, JS, Lua, and Clojure, with
         | output to JPEG files, PPM files, X11, the Linux framebuffer,
         | ASCII art, Unicode Braille art, and the browser <canvas>.
         | 
         | * http://canonical.org/~kragen/sw/aspmisc/my-very-first-
         | raytra... 184 lines of C, including vector arithmetic, input
         | parsing, and PPM output. I'm not sure what you mean by
         | "excluding the headers" -- this one doesn't have any headers of
         | its own (why would a 200-line program have headers of its own?
         | Are you on a Commodore 64 such that the compilation time for
         | 200 lines of code is so high that you need separate
         | compilation?) but it #includes math.h, stdio.h, stdlib.h, and
         | string.h, which total almost 1800 lines of code on my machine
         | and presumably 15x that by the time you count their transitive
         | includes.
         | 
         | * http://canonical.org/~kragen/sw/dev3/circle.clj 39 lines of
         | Clojure, including the model, which is a single sphere; it uses
         | java.awt.image for JPEG output. About half of the code is
         | implementing basic vector math by hand. A minified version is
         | under 1K: http://canonical.org/~kragen/sw/dev3/raytracer1k.clj
         | 
         | * https://gitlab.com/kragen/bubbleos/blob/master/yeso/sdf.lua
         | 51 lines of Lua for an SDF raymarcher including animation, the
         | model itself, and live graphical output. SDFs are cool because
         | it's often easier to write an SDF for some shape than to write
         | code to evaluate the intersection of an arbitrary ray with it.
         | This one runs either in X-Windows, on the Linux framebuffer, or
         | in an unfinished windowing system I wrote called Wercam.
         | 
         | I feel like basic raytracing _is_ a little simpler than basic
         | rasterizing, but I don 't think the difference is hugely
         | dramatic:
         | 
         | * http://canonical.org/~kragen/sw/torus is a basic rasterizer
         | in 261 lines of JS, which is larger than the three raytracers I
         | mentioned above, but about 60% of that is 3-D modeling rather
         | than rendering, and another 5% or so is DOM manipulation. On
         | the other hand, one of the great things about raytracing is
         | that if you want to raytrace a sphere or torus or metaballs or
         | whatever, you don't need to reduce them to a huge pile of
         | triangles; you can just write code to evaluate their surface
         | normals and intersect a ray with them, and you're done.
         | 
         | * http://canonical.org/~kragen/sw/netbook-misc-
         | devel/rotcube.p... The smallest I've been able to get a basic
         | rasterizer down to, 15 lines of Python, just rotating a point
         | cloud, without polygons. You might argue that rotating a point
         | cloud is stupid because it doesn't look very 3-D, but Andy
         | Sloane's donut.c does okay by just applying Lambertian shading
         | to the points in the point cloud:
         | https://www.a1k0n.net/2011/07/20/donut-math.html.
         | 
         | * http://canonical.org/~kragen/sw/dev3/rotcube.cpp in C++
         | rotating an ASCII-art pointcloud is 41 lines; and
         | 
         | * http://canonical.org/~kragen/sw/dev3/braillecube.py with
         | wireframes in Braille Unicode art it's 24 lines of Python, but
         | that's sort of cheating because it imports a Braille Unicode
         | art library I wrote that's another 64 lines of Python.
         | Recording at https://asciinema.org/a/390271.
         | 
         | So I think that the core of either a (polygon!) rasterizer or a
         | raytracer, without optimizations, is only about 20 lines of
         | code if your ecosystem provides you with the stuff around the
         | edges: graphical display (or image file output), model input,
         | linear algebra, color arithmetic. If you have to implement one
         | or more of those four things yourself, it's likely to be as big
         | as the core rasterizer or raytracer code.
         | 
         | For a polygon rasterizer, it's something like:
         | tpoints = [camera_transform @ point for point in points]
         | framebuffer.fill(background)         painter = lambda poly:
         | min(tpoints[i].z for i in poly.v)         for poly in
         | sorted(polys, key=painter)):             normal =
         | tpoints[poly.normal]             if normal.z > 1:  # backface
         | removal, technically an optimization                 continue
         | p2d = [(p.x / p.z, p.y / p.z) for p in [tpoints[i] for i in
         | poly.v]]             lambert = normal.dot(light_direction)
         | color = min(white, max(black, lambert * light_color + ambient))
         | framebuffer.fill_poly(p2d, color)
         | 
         | While a Whitted-style raytracer is more like this:
         | for yy in range(framebuffer.height):             for xx in
         | range(framebuffer.width):                 ray = vec3(xx, yy,
         | 1).normalize()                 hits = [(o, o.intersect(ray))
         | for o in objects]                 hits = [(o, o.p) for o, p in
         | hits if p is not None]                 if hits:
         | o, p = min(((o, p) for o, p in hits),
         | key=lambda t: t[1].z)  # nearest
         | framebuffer[xx, yy] = o.shade(p)                 else:
         | framebuffer[xx, yy] = background
         | 
         | But this presumes you've previously transformed the objects
         | into camera space, it leaves .intersect and .shade to be
         | defined (potentially separately for each object), and it
         | doesn't do the neat recursive ray-tracing thing that gives you
         | those awesome reflections. For a sphere, intersection is about
         | 7 lines of code evaluating the quadratic formula (which you can
         | cut to 3 if you have a quadratic-equation solver in your
         | library), and basic Lambertian shading is about the same as in
         | the rasterizer; your surface normal is (p -
         | sphere.center).normalize().
         | 
         | The core of my Lua SDF raymarcher I linked above is simpler
         | than that. Here I'm using the iteration count as part of the
         | shading function to fake ambient occlusion, which is pretty
         | bogus because it depends on where the camera is in a totally
         | non-physically-based way, but it looks pretty 3-D.
         | local function torus(p, c, r1, r2)            return
         | length2(length2(p[1]-c[1], p[3]-c[3]) - r1, p[2]-c[2]) - r2
         | end              local function render_pixel(x, y, palette)
         | local p, n = {x,y,1}         -- near clipping plane: z=1
         | local q = normalize(p)       -- ray direction
         | for i = 0, 255 do               n = i               local r =
         | scene_signed_distance_function(p)               p = add(p,
         | mul(r, q))                    if p[3] > 10 then return
         | palette(0) end  -- far clipping plane               if r < 0.02
         | then break end            end                 return
         | palette(max(0, min(255, 48 - n -
         | math.floor(p[1]*-16+p[2]*32))))         end
         | 
         | I know what BVHs are, even though I've never implemented them.
         | I'm so clueless that I didn't know what BLAS and TLAS are, but
         | Jacco explains them in part 6 of his series: https://web.archiv
         | e.org/web/20220605013040/https://jacco.omp....
        
         | pengaru wrote:
         | Years ago I read the flipcode article you linked and wrote a
         | raytracer as an introductory exercise to 3D graphics. I even
         | ended up reaching out to Jacco afterwards via linkedin to thank
         | him for the making the tutorial, to my surprise he replied.
         | 
         | It does seem like an excellent way to get introduced to 3D
         | graphics. All the fully-featured triangle rasterizers are a
         | huge pile of complexity. Especially if you're going hardware-
         | accelerated and will also have the burden of navigating
         | existing platform-specific GPU APIs.
         | 
         | There's something to be said for just writing boring CPU-based
         | code in your favorite language and getting to focus on the
         | actual subject being explored.
        
         | jazzyjackson wrote:
         | Looks like a great resource.
         | 
         | I was about to ask how you fixed the page encoding but figured
         | it out for safari, just needed to click View > Text Encoding >
         | ISO Latin 1
        
         | pixelpoet wrote:
         | Lotta Dutch guys in the comments :)
         | 
         | I wrote similar stuff about ray tracing for learning sort of
         | recently here: https://news.ycombinator.com/item?id=30715009
        
       | yohann32 wrote:
       | Fantastic, thanks!
        
       | chunkyks wrote:
       | This feels like an opportunity to link my own, much sillier,
       | raytracer: https://github.com/chunky/sqlraytracer
        
       | patoroco wrote:
       | The file has disappeared :(
        
       | joshenders wrote:
       | 403 from Europe. Cool GDPR policy.
        
         | matttb wrote:
         | It's also 403 from NA, don't think it has anything to do with
         | GDPR. This comment has an archived link:
         | https://news.ycombinator.com/item?id=31759691
        
       ___________________________________________________________________
       (page generated 2022-06-15 23:00 UTC)