Blog Archive About

Psychopath Renderer

a slightly psychotic path tracer

2015 - 07 - 13

Spectral Rendering

Up to this point Psychopath has represented color with RGB values. This is a convenient way to handle color in a renderer because most color input to the renderer is going to be in some kind of RGB format (textures, hand-specified colors, etc.) and most color output the renderer produces is going to be in some kind of RGB format as well (final rendered images). So it makes sense that if your input is RGB, and your output is RGB, probably everything in-between should be RGB as well.

But RGB isn't how real light works. Real light is composed of a spectrum of wavelengths, much the same way that sound is. RGB is a bit like sampling audio at only three frequencies.

Even so, rendering with RGB usually works fine because human color vision is also more-or-less RGB. But there are side effects of spectrums that, although their causes are not visible to the naked eye, their results are. Fluorescent lighting is a good example: the effect it has on colors can't be accurately reproduced in RGB without also tweaking the colors of the materials. Prisms and rainbows are another example.

Admittedly, these examples are largely corner cases that can be faked, and I'm designing Psychopath with animation and VFX in mind—both of which are cases where artistic control is more important than strict accuracy. But nevertheless, being able to render spectral effects is cool! And it might come in handy when trying to match scene lighting for a VFX shot in a fluorescent-lit office building.

2015 - 06 - 03

Basic Shading System

Psychopath now has a basic shading system! Yay!

As you can see in this artistically-awful image, different objects can now have different shaders assigned to them:

Multiple Materials

This was actually fairly easy to get up and running. It just required some design work. I did indeed go with shading being done at intersection time, as described in my last post.

This basic shading system does not use OSL, and doesn't even support texturing of any kind. Each shader just produces a single fixed BSDF. But building this basic version of a shading system helped me work out the machinery behind how shaders get assigned to objects and how they are processed during rendering.

One of the cool features of this shading system is that shaders are assigned hierarchically. Any node in the scene graph (more-or-less) can have a shader assigned to it, and all nodes under such a node will inherit that shader—unless they themselves have been assigned a different shader. This makes it easy to assign a single shader to the entire scene, for example.

2015 - 05 - 26

Designing a Shading System

One of Psychopath's current limitations is that every surface has to have the same shader. This is obviously not workable if it's going to be used as a "real" renderer, so I need to design a shading system that allows different shaders to be assigned to different objects.

Eventually I want to use Open Shading Language for shading in Psychopath, but as a first pass I'm going to do something a little simpler to get my feet wet. I've never written a shading system, after all.

But even so, since Psychopath traces many rays at once there are actually some interesting choices to make here which will carry over to how Psychopath utilizes OSL. There are two broad approaches I can take:

  1. Do the shading when a ray hits something. This would require storing the result of the shading in the intersection structure, which would be a BSDF closure.

  2. Do the shading after all of the ray intersection tests have been completed. This would require storing all information necessary to do the shading in the intersection structure, which would be things such as a reference to the shader and any required differential geometry data.

The biggest benefit of the first approach is that it keeps the architecture a bit simpler. Displacement shaders will have to be evaluated during ray traversal anyway, so the first approach would keep all of the shading code in the same general part of Psychopath.

2014 - 08 - 18

Breadth First Ray Tracing

Phew! I'm almost finished with my "catching up to the present" retrospective architectural posts. I may sprinkle a few more retrospective posts here and there, but this is the last for this series. This post will expand upon my first and second ray reordering posts and talk about breadth first ray tracing, which is what eventually replaced ray reordering in Psychopath.

First, let me say that breadth first ray tracing is really cool. Like, really cool. Much like ray reordering, it accesses scene data coherently for a batch of rays. But unlike ray reordering it has strong guarantees about those access patterns. Specifically, each instance of a scene element is guaranteed to be accessed at most once for an entire batch of rays. And, perhaps surprisingly, breadth first ray tracing is also much simpler to implement than ray reordering.

So how does it work?

The core idea behind breadth first ray tracing is to trace an entire batch of rays together. This is subtly different from ray reordering where, although you also have a batch of rays, you are tracing each ray separately—you just use pausing to switch between them at strategic times. But in breadth first ray tracing you really trace all of the rays together, even down to traversing the acceleration structure of the scene.

2014 - 08 - 02

Drawbacks of Ray Reordering

Now that I've covered the drawbacks I ran into with the geometry cache, I'd like to talk about the issue I ran into with ray reordering.

Ray reordering worked extremely well in pretty much every way. It was actually quite fast, it wasn't too complex or difficult to implement, and I was even able to do a pretty nice QBVH implementation thanks to the awesome paper Stackless Multi-BVH Traversal for CPU, MIC and GPU Ray Tracing by Áfra et al.

But the big drawback of ray reordering is that you have to keep ray state small. You have to be able to pause a ray's traversal, which means storing its current traversal state so that you can resume tracing later. And you are typically tracing thousands or even millions of rays at a time, so that state data can't be large.

With a typical BVH this is relatively straight-forward because it's a tree structure. You never have to store where you came from in the tree because it's implicit. No matter where you are in the BVH, you can just go to the parent node, and then the parent's parent node, and so-on.