REYES - RenderMan Under the Hood
Introduction to REYES
Do you know your REYES from your rays? Well, forget rays for the time being, we are here to talk about REYES (Render Everything You’ve Ever Seen) -the algorithm that processes the geometry for the RenderMan renderer. Production rendering is all about processing geometry, lots of geometry. RenderMan, of course, does this exceptionally well. But, in order to harness the power, it is important to understand how RenderMan takes geometry from your scene - be it nurbs, polygons or a subdivision surface - and processes it into shaded micropolygons. This fundamental understanding will help you when we cover aspects such as displacement mapping, baking, shading and debugging.
At the start of the rendering pipeline we put in primitives. These are exported as files from our authoring package in the RenderMan scene format RIB (this translation is hidden when rendering with RenderMan internally in Maya, but the RIB is exposed if rendering with Pro Server). The RIB is parsed and fed into the renderer. Understanding the REYES geometry-processing engine is fundamental to leveraging the power that RenderMan gives you. The processing pipeline has been described many times in varying degrees of complexity (if interested you can read the original Siggraph paper or a nice explanation by Tony Apodaca). To help you understand it at a high level, we will use an analogy of the REYES engine as a big, mechanical monster consuming and digesting your scene.
The first step, in the processing pipeline, is getting the geometry into the mouth of the monster. Geometry leaves the scenes on a banquet platter of RIB which is passed to the mouth of the beast. The mouth is equivalent to the viewing area (viewing frustum) that your camera has of your scene: some things are visible and get processed; and some things are not, so they are culled and discarded. Technically, the viewing frustum is a volume contained by the left, right, top, and bottom of the camera frame, and the near and far clipping plane.
To determine what fits in the viewing frustum, the REYES machine looks at the size of each primitive during a process called bounding. RenderMan checks the bounding box of each primitive, and everything entirely outside of the mouth is trivially discarded, but some primitives are half-way in and half-way out the mouth and must be split into smaller pieces. Once in the mouth, the primitives must be chewed into smaller pieces so they can be swallowed. This iterative process of breaking geometry into smaller, bite-sized sub-primitives (e.g. a nurbs model primitive is split into many small nurbs patch primitives), is known as splitting. The REYES monster chews the primitives until they are small enough to swallow and digest (the size test before swallowing is determined by the current bucket size, more on this later). Essentially all primitives split until they fit completely within a single bucket. Just before being swallowed they are sorted front to back. When a split primitive is swallowed it is subdivided into a cluster of subpixel-sized micropolygons in a process called dicing. These groups of micropolygons are called grids.
These grids are the same size as the small swallowed primitive, only they have been diced into small, pixel-sized micropolygons. The grids of micropolygons move further into the stomach and get shaded together (displacement, surface shading, atmospherics). Once RenderMan is finished shading the grid it is busted into individual micropolygons, each is tested one last time for visibility and then sent to the screen to make pixels (via sampling and filtering steps).
A familiarity with this pipeline is important to understand many of the seemingly arcane attributes (RiAttributes) and options (RiOptions) in RenderMan (found in Studio's Render Settings, and, of course, in the actual RIB). Many of these help to optimize the pipeline described above for memory, performance, and quality. Options and Attributes directly related to the REYES algorithm are:
Shading rate determines how frequently a primitive gets shaded. It is roughly the distance, in pixels, between shading samples. Therefore a shading rate of 1 evaluates the shaders once for every pixel. When setting your shading rate to less than one, 0.5, or even 0.1, you force the dicing step to dice the primitives into smaller and smaller micropolygons, to allow for the increased shading.
A super-low shading rate is often used to eliminate stretching artifacts when there is a significant amount of displacement on an object. Remember the object is diced before it is displaced: so, for a shading rate of 1, before displacement the micropolygons will be subpixel, but after displacement the micropolygons can be stretched to take up more than one pixel. The image below shows a sphere with three levels of displacement. At the two higher levels you can see significant micropolygon stretching.
The Bucket Size setting tells RenderMan how small the primitive needs to be split, before it can be "swallowed" and diced into micropolygons. The larger the bucket size, the larger the resulting grids of micropolygons, all of which must be maintained in memory at once. However given sufficient system memory, larger shading grids are faster to render. Setting the bucket size is always a trade off between available system resources and rendering speed.
- RIB e.g. Option "limits" "bucketsize" [32 32]
Another RiOption that goes hand-in-hand with Bucket Size. The max grid size tells RenderMan how many micropolygons are permitted in a grid. This corresponds to bucket size like this:
- grid size = (bucket * bucket/shadingRate)
- RIB e.g Option "limits" "int gridsize" 
Displacement bounds must be added to any object being displaced. It tells the renderer how much to expand the object's bounding box to account for displacement. This is important to help place the split grids into the correct buckets. If the displacement bound is missing or under-specified, there will be artifacts where the primitive is improperly bucketed. Below you see the split and diced primitive, but without displacement bounds many of the displaced surfaces are placed into the wrong render bucket and mayhem ensues. See the Displacement HowTo for more images of displacement bounding artifacts.
There are a couple of dicing attributes that tell RenderMan what strategy to use when dicing the primitives into micropolygons. This is often important for baking view-independent data, such as occlusion or colorbleeding brick maps. If it is meant to be view-independent, the dicing should be done from something other than the viewing camera. For this reason there is an attribute called rasterorient that can be turned off to give view independent dicing. Examples of other dicing attributes:
- Attribute "dice" "string strategy" ["planarprojection"]
- Attribute "dice" "string referencecamera" ["worldcamera"]
- Attribute "dice" "int rasterorient" 
Before RenderMan shades the micropolygons, it can optimize itself by getting rid of micropolygons that are either hidden by other primitives or facing backwards and not seen (when double-sided is off). There are two attributes that control this, generally used when baking data for camera independent reuse. The attributes are:
- Attribute "cull" "int backfacing" 
- Attribute "cull" "int hidden" [b]
Fundamentals - Point Clouds Brickmaps Baking
Library - How PRMan Works