1

So I'm going through a ray tracing tutorial in an attempt to stretch my F# legs. Since the tutorial is written in C++, it a rather fun challenge to figure out how to apply the concepts in a functional manner. I'd like to write everything in as functional a manner as possible, because I intend to eventually run the ray tracer in parallel. But that got me to the following situation, the core of which I'm sure shows up in topics other than ray tracing.

We have an Engine object which (among other things) stores a Scene object (a collection of Primitive objects). Currently, the Scene and the Primitives within it are completely immutable.

To try improve the rendering time, we're implementing a regular three-dimensional grid. In order to traverse the grid in an effective manner, the tutorial references this paper for the both the traversal algorithm, and for reducing the number of intersection tests for primitives which lie across grid boundaries. Here's how the latter part works:

  • Each Ray is assigned a unique rayID
  • Each Primitive has a lastRayID field.
  • When a Ray checks for intersection with a Primitive,
    1. If the rayID of the Ray equals the lastRayID of the Primitive, then the intersection test is skipped.
    2. Otherwise, the test is performed, and the rayID of the Ray is stored in the lastRayID field of the Primitive.

This is a neat way of caching the intersection tests, but it's set up for a sequential ray tracer, and wouldn't work at all for even two concurrent rays. So although I could use mutable fields, and therefore be able to implement this algorithm, it wouldn't satify my end goals of an inherently parallelizable ray tracer.

I do have one idea. The problem with storing mutable state with each primitive is that, with respect to the rendering algorithm, the scene is a global entity - each ray could potentially strike any primitive. On the other hand, each ray is completely self contained. So in each ray, I figured I could build up a set of primitives which have already been tested against (this set would be the equivalent to the lastRayID field described above). The problem with this is that each ray has an extremely short life time, and there could potentially be many rays in existence at any given time, so setting up such a data structure in each ray could be costly ((de)allocation time and memory consumption costs could add up quickly)

Does anyone have advice on dealing with these kinds of situations? Even if it's a general idea about transforming shared mutable state into local mutable state or something like that, I'm sure that would help me out a lot. I'd be happy to clarify anything if necessary.

Ken Wayne VanderLinde
  • 18,915
  • 3
  • 47
  • 72

0 Answers0