## The Brick Map Geometric Primitive

December 2005 (Revised December 2006)

## 1 Introduction

Brick maps were introduced in PRMan 12.0 as a 3D data structure for textures on surfaces and in volumes. PRMan 13.0 extends the use of brick maps to also allow their use as a geometric primitive (on equal footing with the other geometric primitives such as quadrics, polygon meshes, NURBS patches, and subdivision surfaces). Brick maps being a geometric primitive means that they can be rendered (using the Reyes algorithm), ray traced, motion blurred, shaded, displacement mapped, and most of the other things that geometric primitives can do.

The purpose of this application note is to provide examples for the use of brick map geometric primitives using PRMan 13.0 and higher.

## 2 Rendering

A brick map can be rendered as a geometric primitive using the following rib syntax:

```    Geometry "brickmap" "filename" <filename>
```

### 2.1 Surface brick map

For example, assume you have a brick map of surface data. The brick map is called 'irmateapot.bkm' and the color data at the four coarsest levels look like this:

 Level 0 Level 1 Level 2 Level 3

(For reference, appendix A at the end of this document contains the shader and rib file used to generate this brick map.)

In addition to the color data, each voxel in this brick map also contains surface position data, normal data, and opacity data. (All opacity data are 1.)

New for PRMan 13.0.4: We now recommend baking P and N if the brick map is intended for use as a geometric primitive. This gives a higher accuracy of the rendered brick map surface, particularly along the silhouettes. More on this in appendix A.

Here is a short rib file to illustrate the use of the brick map as a geometric primitive.

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_render" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

# Brick map geometric primitive: large textured teapot
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: medium textured teapot (left)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate -2.5 -0.85 -1
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: smallest textured teapot (right)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate 2.5 -0.95 -2
Scale 0.05 0.05 0.05
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd
WorldEnd
FrameEnd
```

Note that the brick map geometric primitive has no surface shader in this example, so the color and opacity will be read from the Cs and Os data in the brick map. (Surface "" can also be used instead of Surface "null".) If the brick map has no Cs data, the Color attribute will be used instead. If the brick map has no Os data, the Opacity attribute will be used instead. (See Section 6 for examples of shading.) Running this rib file gives the following result:

 Rendered brick map geometric primitive

The appropriate level in the brick map is chosen based on the screen size of the brick map geometric primitive and the shading rate. During rendering, the bricks are read on demand at the appropriate size and cached in a brick cache.

Implementation detail: Each brick voxel is rendered as a disk. This can be seen more clearly if the shading rate is increased. The images below are rendered with shading rate 100 and 100000, respectively, and the disks can be seen clearly.

In the left image, some of the disks along the edges are rendered as partially transparent. This is due to lerping of two different levels of detail of the brick map. In the image on the right, all three teapots are rendered using the coarsest level in the brick map (8x8x8 voxels).

If P and N aren't baked among the data, each brick voxel is rendered as a disk centered at the voxel center and facing the camera -- as illustrated below. (This is also how brick maps were rendered in PRMan versions before 13.0.4.) As it can be seen, this inevitably leads to bloated silhouettes.
 Shading rate 100 (no P and N data) Shading rate 100000 (no P and N data)

For long thin geometry such as cylinders there is an additional source of silhouette bloat. This happens because the points along the cylinder are much further apart than along the radial direction. The radius values have to be large enough that the disks cover the space between points along the cylinder. This unfortunately gives point radii that makes the thin geometry bloat a lot. Creating a brick map from this point cloud will give brick voxels that are much larger than the width of the cylinder. The workaround to avoid this is to reduce the ShadingRate when the point cloud is baked and rendered. This will of course lead to larger point cloud and brick map files, but it is unfortunately the only way the brick map geometric primitive can be made to represent very thin geometry correctly.

### 2.2 Volume brick map

For another example, consider a brick map that has been generated from volume data: the data points do not have normals and each brick voxel is semitransparent. (For reference, appendix B at the end of this document contains the shader and rib file used to generate this brick map.) When rendered, all the semitransparent brick voxels are alpha-composited. Here is an example rib file:

```    FrameBegin 0

Format 300 300 1
PixelSamples 4 4
Display "torusvol_render" "it" "rgba"
Projection "perspective" "fov" 15
Translate 0 0 10

WorldBegin
# Brick map geometric primitive: semitransparent torus
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Rotate 25 1 0 0
Rotate 25 0 1 0
Geometry "brickmap" "filename" "torus.bkm"
AttributeEnd
WorldEnd
FrameEnd
```

The colors and opacities are read from the brick map. Here is the rendered image of the volume brick map:

 Rendered brick map geometric primitive (volume data)

## 3 Ray tracing

Brick maps can also be ray traced. The appropriate level in the brick map is chosen based on the ray differential size. For an example of a ray traced brick map, consider the following rib file:

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_raytrace" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

Attribute "visibility" "trace" 1   # make objects visible to rays

# Brick map geometric primitive: large textured teapot
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: medium textured teapot (left)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate -2.5 -0.85 -1
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: smallest textured teapot (right)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate 2.5 -0.95 -2
Scale 0.05 0.05 0.05
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Reflecting ground plane
AttributeBegin
Surface "aachrome"
Translate 0 -1 0
Scale 10 10 10
Polygon "P" [ -1 0 1  1 0 1  1 0 -1  -1 0 -1 ]
AttributeEnd

WorldEnd
FrameEnd
```

Running this rib file generates the following image (with ray-traced reflections at the bottom):

 Directly rendered and ray traced brick map geometric primitive

The previous example showed ray traced reflection of a brick map geometric primitive. Brick map geometric primitives can also cast ray-traced shadows.

As another example, we can add a reflecting ground plane to the rib file for the torus volume brick map. When a ray hits a semitransparent brick voxel it automatically continues to the next voxel, etc. The colors and opacities are composited along the way. This corresponds to ray marching through the volume of the brick map.

 Rendered and ray traced brick map geometric primitive

## 4 Color bleeding

It is simple to compute color bleeding from a brick map geometric primitive. For example:

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_indirectdiffuse" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

# Brick map geometric primitive: large textured teapot
AttributeBegin
Attribute "visibility" "trace" 1
Surface "null"   # no surface shader: use Cs and Os data
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Ground plane with indirectdiffuse computation
AttributeBegin
Surface "indirectsurf" "samples" 256 "maxvariation" 0.01
Translate 0 -1 0
Scale 10 10 10
Polygon "P" [ -1 0 1  1 0 1  1 0 -1  -1 0 -1 ]
AttributeEnd

WorldEnd
FrameEnd
```

The indirectsurf shader simply calls the indirectdiffuse() function and assigns the result to Ci.

The resulting image is shown below. The colors of the teapot-shaped brick map geometric primitive are bleeding onto the ground plane.

 Color bleeding from a brick map geometric primitive

## 5 Motion blur

Brick map geometric primitives can be rendered with transformation motion blur. Here is a rib file illustrating various transformations:

```    FrameBegin 0

Format 400 300 1
PixelSamples 10 10
Display "teapot_rendermb" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

Shutter 0.0 1.0

WorldBegin

Attribute "visibility" "trace" 1   # make objects visible to rays
Attribute "trace" "samplemotion" 1   # ray tracing motion blur

# Brick map geometric primitive: moving large textured teapot
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
MotionBegin [0.0 1.0]
Translate -0.25 0 0
Translate 0.25 0 0
MotionEnd
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: rotating medium textured teapot (left)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate -2.5 -0.85 -1
MotionBegin [0.0 1.0]
Rotate 0  0 1 0
Rotate 45.0  0 1 0
MotionEnd
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: shrinking small textured teapot (right)
AttributeBegin
Surface "null"   # no surface shader: use Cs and Os data
Translate 2.5 -0.95 -2
MotionBegin [0.0 1.0]
Scale 0.15 0.15 0.15
Scale 0.05 0.05 0.05
MotionEnd
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Reflecting ground plane
AttributeBegin
Surface "aachrome" "samples" 64
Translate 0 -1 0
Scale 15 15 15
Polygon "P" [ -1 0 1  1 0 1  1 0 -1  -1 0 -1 ]
AttributeEnd

WorldEnd
FrameEnd
```

Running this rib file generates the following motion-blurred image (with ray-traced reflections at the bottom):

 Motion-blurred brick map geometric primitive

At the present time (PRMan 13.0.4) it is not possible to render brick map geometric primitives with deformation motion blur. It might be implemented in a future release.

In the previous examples, the brick maps "inherited" their color and opacity from the voxel data. However, it is also possible to run shaders on the brick map geometry (just as for other geometric primitives).

### 6.1 Color

Here is an example that runs three different shaders on the three instances of the brick map geometric primitive. The P, N, Cs, and Os values are read from the data in the brick map. (Again, the Color and Opacity attributes are used if those data are not present in the brick map. And voxel centers are used if there are no P and N data in the brick map.)

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_rendershaded1" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

Attribute "visibility" "trace" 1   # make objects visible to rays
Attribute "visibility" "transmission" "opaque"
Attribute "trace" "bias" 0.1   # large bias to avoid self-shadows

LightSource "pointlight_rts" 1 "from" [-10 10 -10] "intensity" 500
LightSource "ambientlight" 2 "intensity" 0.2

# Brick map geometric primitive: large teapot
AttributeBegin
Surface "paintedplastic"
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: medium teapot (left)
AttributeBegin
Surface "matte"
Translate -2.5 -0.85 -1
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: smallest teapot (right)
AttributeBegin
Surface "constant"
Translate 2.5 -0.95 -2
Scale 0.05 0.05 0.05
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Reflecting ground plane
AttributeBegin
Attribute "trace" "bias" 0.0001 # normal bias for the reflection rays
Surface "aachrome"
Translate 0 -1 0
Scale 10 10 10
Polygon "P" [ -1 0 1  1 0 1  1 0 -1  -1 0 -1 ]
AttributeEnd

WorldEnd
FrameEnd
```
The resulting image looks like this:
 Shaded brick map geometric primitive

The light source shader traces shadow rays; the ray-traced shadows are most clearly visible at the base of the spout of the largest teapot. Since the brick voxels are a rather coarse approximation of the true surface, the ray tracing bias has to be rather high to avoid "surface acne" due to false self-intersections. Finding a proper value for the ray tracing bias is a general problem for shading of brick map geometric primitives.

The visual result of running the constant shader (as shown here on the smallest teapot) is the same as when running no shader. However, running the constant shader will take longer than no shader, mostly due to shader set-up overhead.

If (s,t), (u,v), Pref, or other parameters are needed for the surface shader they have to be baked in the brick map data — similar to how P, N, Cs, and Os were baked in this example.

Here is another example of shading of a brick map. In this case the shader does not rely on any baked data in the brick map. The shader computes a checkerboard pattern based on the x- and y-component of the position. (This shader does not properly anti-alias the edges between the two colors, but is chosen here for simplicity.)

```    surface
checkerxy(float frequency = 1)
{
float x, y, cx, cy, c;

x = frequency * xcomp(P);
x = x - floor(x);
y = frequency * ycomp(P);
y = y - floor(y);

cx = (x > 0.5) ? 1 : 0;
cy = (y > 0.5) ? 1 : 0;
if (cx == cy)
Ci = 1; // white
else
Ci = color(1,0,0); // red
Oi = 1;
}
```

Rib file:

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_rendershaded2" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

Surface "checkerxy"

# Brick map geometric primitive: large teapot
AttributeBegin
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: medium teapot (left)
AttributeBegin
Translate -2.5 -0.85 -1
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: smallest teapot (right)
AttributeBegin
Translate 2.5 -0.95 -2
Scale 0.05 0.05 0.05
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

WorldEnd
FrameEnd
```

The resulting image:

 Shaded brick map geometric primitive: checkerboard shader

Finally, here is an ambient occlusion example. The shader is replaced with a shader that computes ambient occlusion using the occlusion() function. Again, ray tracing bias is an issue; it must be set large enough to avoid false self-occlusion. Beware that computing ray-traced ambient occlusion and indirect diffuse illumination on brick map geometric primitives can be rather slow since no information is shared between shading points (maxvariation is ignored).

 Shaded brick map geometric primitive: ambient occlusion

### 6.2 Opacity

As an example of transparency, we can change the surface shader in the previous example to make checkered transparency:

```    surface
checkerxyopacity(float frequency = 1)
{
float x, y, cx, cy, c;

x = frequency * xcomp(P);
x = x - floor(x);
y = frequency * ycomp(P);
y = y - floor(y);

cx = (x > 0.5) ? 1 : 0;
cy = (y > 0.5) ? 1 : 0;
if (cx == cy) {
Ci = 0; // black
Oi = 0; // transparent
} else {
Ci = Cs; // color from baked data (or Color attribute)
Oi = 1; // opaque
}
}
```

The result is an image of a very fragmented teapot:

 Shaded brick map geometric primitive with checkered transparency

This example shows that fully transparent and fully opaque voxels are rendered correctly. However, semitransparent voxels can end up being rendered such that they look too opaque. This is because each surface point can be stored in up to eight voxels, i.e. surface data are often stored two voxels deep. When those voxels are rendered, they are alpha-composited using their (baked or assigned) opacities. The end result is too much opacity.

### 6.3 Displacement

It is often advantageous to run the displacement shader when the brick map geometry is created (as a point cloud). But it is also possible to run a displacement shader on the brick map geometric primitive, as shown in the following example.

Displacement shader that displaces the surface along the x-axis (with displacement magnitude depending on sine of y):

```    displacement
xydisp (float scale = 1, freq = 1)
{
point Pobj = transform("object", P); // position in object space
float y = ycomp(Pobj);

float disp = scale * sin(freq * y); // displacement magnitude
P += disp * vector(1,0,0); // displace along the x-axis

N = calculatenormal(P); // compute new normal
}
```

Here is a rib file that uses the displacement shader:

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4
Display "teapot_renderdisplaced" "it" "rgba"
Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

Attribute "displacementbound" "sphere" 0.1

# Brick map geometric primitive: large teapot
AttributeBegin
Displacement "xydisp" "scale" 0.1 "freq" 10
Surface "null"
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: medium teapot (left)
AttributeBegin
Displacement "xydisp" "scale" 0.05 "freq" 10
Surface "null"
Translate -2.5 -0.85 -1
Scale 0.15 0.15 0.15
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

# Brick map geometric primitive: smallest teapot (right)
AttributeBegin
Displacement "xydisp" "scale" 0.02 "freq" 10
Surface "null"
Translate 2.5 -0.95 -2
Scale 0.05 0.05 0.05
Geometry "brickmap" "filename" "irmateapot.bkm"
AttributeEnd

WorldEnd
FrameEnd
```

The resulting image of three displacement-mapped brick map geometric primitives looks like this:

 Brick map geometric primitive with displacement shader

There are a couple of limitations on the use of displacement-mapping on brick map geometric primitives: 1) If the displacement shader gives discontinuous (non-smooth) displacements, the result can be "torn" geometry, i.e. a surface with holes. 2) Ray tracing of brick maps with displacement shading is not implemented (the displacement will be ignored for ray tracing).

### 6.4 A note on transformations

Baked data of type point, vector, and normal are transformed to world space at baking time. Likewise, data of those types are transformed to the current camera space at lookup and shading time. These transformations are consistent with the transformations of vertex variables on other geometric primitives, and ensures that e.g. shading normals are correct. (If the transformation is not desired, it is a common trick to assign these data types to e.g. colors or three floats since those are not transformed.)

## 7 Levels of detail

A fundamental problem with PRMan (and similar renderers) is that it cannot simplify complex geometry. For this reason, level-of-detail representation was introduced in the RenderMan specification. But the existing explicit level-of-detail method requires that the objects are modeled multiple times (with different amounts of detail). Here we will show that brick maps can sometimes provide a more convenient automatic level-of-detail representation.

Imagine a collection of complex objects that are only a few pixels large in most frames, for example, an armada of spaceships that have been individually modeled with full details, including all rivets and bolts. Each spaceship is seen up close in a few frames of a shot, so the details are required sometimes, but, in each shot, most of the spaceships are distant and only cover a few pixels. Now imagine that there is no time to model simpler versions of the spaceships due to production pressures and deadlines. If the fully detailed spaceships are used for rendering, their entire definitions will be read in (for example NURBS patch knot values or subdivision base meshes), converted to geometry, shaded, and rendered. Since there are so many spaceships and they are all specified in full detail, PRMan will soon run out of memory.

In contrast, brick maps have no overhead: no NURBS knots, no subdivision base mesh or the like. If a brick map geometric primitive only covers a few pixels, only the top level bricks will be read in. PRMan automatically chooses the appropriate level in the brick map (based on size on screen or ray differential).

If we get too close to a brick map (closer than the detail level the data were originally generated at), the finest brick map level is insufficient. The needed data are "missing", and the disks that represent the finest brick voxels become visible. The solution to this problem is to use the original geometric primitive in that case. For any single frame, there will be only a few objects that require the full, original object definition.

The following is a useful RIB file idiom for switching between the original geometry and the brick map representation:

```    AttributeBegin
Detail [xmin xmax ymin ymax zmin zmax]   # "ruler" for detail calculations

# Brick map for low detail
DetailRange [0 0 detail1 detail2]
Geometry "brickmap" ...

# Original geometry for high detail
DetailRange [detail1 detail2 1e38 1e38]
...
AttributeEnd
```

Here detail1 and detail2 determine at which size the brick map fades out and the original geometry fades in.

## 8 Brick cache

The brick cache is shared between brick map geometric primitives and brick map textures. This is natural since the bricks are the same underlying data structure.

The default size of the brick cache is 10 MB pr. thread (corresponding to e.g. 758 bricks if the brick voxel data consist of 6 floats). The brick cache size can be changed with the option:

```    Option "limits" "brickmemory" 10240
```
The memory size is specified in kilobytes, so the example above is 10 MB (same as the default size).

The brick cache size can also be specified in the rendermn.ini file. The syntax is as follows:

```    /prman/brickmemory		10240
```

As usual, the option overrides the rendermn.ini setting if both are specified.

More details and advice regarding brick cache sizes are described in Section 5.4 of PRMan application note "Baking 3D Textures: Point Clouds and Brick Maps"

## 9 Known limitations

• Constructive Solid Geometry (CSG): the brick map geometic primitive cannot be used for constructive solid geometry.
• Backface culling: "sides 1" can only only be used for backface culling of brick map geometric primitives if the brick map contains normal data N.
• Deformation motion blur is not implemented.
• If the displacement shader gives discontinuous (non-smooth) displacements, the result can be "torn" geometry i.e. a surface with holes.
• Ray tracing of brick maps with displacement shading is not implemented: the displacement will be ignored. (However, displacement of directly visible brick maps works just fine -- as shown in section 5.3.)
• Semitransparent brick maps can look too opaque. (This is explained in Section 5.2.)
• Ray tracing bias: due to the relatively coarse voxelization of the surface geometry represented by a brick map, rays leaving a brick map geometric primitive can incorrectly hit the same brick map. The only way to avoid this is to set the ray tracing bias value rather high.
• When executing a shader on a brick map geometric primitive, the filtering s and t widths are currently 0. This leads to aliased texture and shadow map lookup results.

## 10 Frequently asked questions

Q: How do I create a point cloud file?

A: Point cloud files can be generated in several ways: a) By rendering a scene with shaders that call the bake3d() function. b) By some stand-alone program that writes data points using the point cloud API described in PRMan application note #39 "Baking 3D Textures: Point Clouds and Brick Maps".

Q: How do I create a brick map?

A: Brick maps are created from point cloud files using the stand-alone program 'brickmake'.

Q: Can I go the other way, i.e. create a point cloud from a brick map?

A: Yes. If you assign a surface shader to the brick map primitive, and that shader calls bake3d(), you can generate a point cloud with one point pr. brick voxel. You can adjust the number and density of the generated points by changing the shading rate and/or image resolution.

Q: The point cloud API is great. Is there a similar API for brick maps?

A: No, not at the present time.

Q: When I render my brick map, it only renders the top level brick -- no matter how I set the screen resolution and shading rate. What's up with that?

A: This can happen if some of the points in the point cloud (that was used to generate the brick map) have very large radius values. Try to eliminate the excessively large radii.

The brick map data structure is described in PRMan application note #39 "Baking 3D Textures: Point Clouds and Brick Maps" and also in the following publication:

• Per H. Christensen and Dana Batali. An Irradiance Atlas for Global Illumination in Complex Production Scenes. Rendering Techniques '04 (Proceedings of the Eurographics Symposium on Rendering 2004), pp. 133-141.

## Appendix A: Generating the textured teapot brick map

For easy reference, here is the shader and rib file that were used to generate the textured teapot brick map.

New for PRMan 13.0.4: We now recommend baking the positions P along with the normals N and other data for brick maps that are to be used as geometric primitives. These extra data enable the rendered brick map to better approximate the original surface, and also reduce trace bias issues when ray tracing the brick map geometric primitives.

Shader for baking surface position, normal, color and opacity:

```    surface
bakePNCsOs(string filename = "", displaychannels = "", texturename = "";
float interpolate = 1)
{
color irrad, tex = 1, illumsurfcolor;
normal Nn = normalize(N);

// Compute direct illumination (ambient and diffuse)
irrad = ambient() + diffuse(Nn);

// Multiply by surface color
if (texturename != "")
tex = texture(texturename);
illumsurfcolor = irrad * tex * Cs;

// Store in point cloud file
bake3d(filename, displaychannels, P, Nn, "interpolate", interpolate,
"P", P, "N", Nn, "Cs", illumsurfcolor, "Os", Os);

Ci = illumsurfcolor * Os;
Oi = Os;
}
```

Rib file:

```    FrameBegin 0

Format 400 300 1
PixelSamples 4 4

Display "teapot_bake" "it" "rgba"
DisplayChannel "point P"
DisplayChannel "normal N"
DisplayChannel "color Cs"
DisplayChannel "color Os"

Projection "perspective" "fov" 25
Translate 0 0 12

WorldBegin

LightSource "ambientlight" 1 "intensity" 1

Attribute "cull" "hidden" 0   # don't cull hidden surfaces
Attribute "cull" "backfacing" 0   # don't cull backfacing surfaces
Attribute "dice" "rasterorient" 0   # view-independent dicing !

# Teapot
AttributeBegin
Surface "bakePNCsOs" "filename" "irmateapot.ptc"
"displaychannels" "P,N,Cs,Os" "texturename" "irma.tex"
Translate 0 -1 0
Rotate -90  1 0 0
Geometry "teapot"   # standard "Utah" Bezier patch teapot
AttributeEnd

WorldEnd
FrameEnd
```

Running this rib file generates the point cloud file 'irmateapot.ptc'. (The point cloud file can be inspected using the interactive viewing program ptviewer, as shown in the image below.)

 Point cloud for textured teapot

A brick map is then generated using the brickmake program:

```    brickmake irmateapot.ptc irmateapot.bkm
```

The brick map can be inspected using the interactive viewing program brickviewer -- as shown in the first figure at the beginning of this application note.

## Appendix B: Generating the torus volume brick map

For easy reference, here is the shader and rib file that were used to generate the torus volume brick map.

Shader for baking volume color and opacity:

```    volume
baketorusvol(string filename = "", displaychannel = "";
point center = (0,0,0);
float a = 0.75; // major radius
float b = 0.25; // minor radius
float opacity = 0.05; // opacity at each point
float frequency = 3, depth = 1, steplength = 0.1)
{
color cs, os = opacity;
point Pfront, Pcurrent, Pshad;
normal N0 = 0;
vector In = normalize(I);
vector dx = steplength * In;
vector diff;
float d, x, z;
uniform float steps = depth / steplength, step = 0;

Pfront = P - depth * In;
Pcurrent = Pfront;

// March along ray through the volume
while (step < steps) {

d = length(Pshad - center);

// Select points inside torus
if ((d*d - a*a - b*b) * (d*d - a*a - b*b) < 4 * a*a * (b*b - z*z)) {
// Compute marble texture
float sum = 0, freq = frequency, i;
for (i = 0; i < 6; i = i + 1) {
sum = sum + 1/freq * abs(0.5 - noise(4 * freq * Pshad));
freq = 2 * freq;
}
cs = 3 * sum + color(0.2, 0, 0); // brighten and add reddish tint

// Write the marble texture data point to a point cloud file
bake3d(filename, displaychannel, Pcurrent, N0, "Cs", cs, "Os", os);
}

// Advance one step
step += 1;
Pcurrent += dx;
}

Ci = cs;
Oi = os;
}
```

Rib file:

```    FrameBegin 0

Format 300 300 1
PixelSamples 4 4
ShadingRate 4   # coarse shading gives sparse baked points

Display "torusvol_bake" "it" "rgba"
DisplayChannel "color Cs"
DisplayChannel "color Os"

Projection "orthographic"
Translate 0 0 10

WorldBegin
# Atmosphere shader bakes point cloud of volume texture
Atmosphere "baketorusvol"
"filename" "torus.ptc" "displaychannel" "Cs,Os" "opacity" 0.02
"frequency" 1 "depth" 0.5 "steplength" 0.02

# Back wall -- necessary to execute the atmosphere shader
Surface "constant"
Translate 0 0 0.25
Polygon "P" [-1 -1 0  1 -1 0  1 1 0  -1 1 0]
WorldEnd
FrameEnd
```

Running this rib file generates the point cloud file 'torus.ptc'. Two ptviewer views of the point cloud are shown below:

 Point cloud for torus volume: front view Point cloud for torus volume: off-axis view

A brick map is then generated using the brickmake program:

```    brickmake torus.ptc torus.bkm
```
In this example, the color is varying (modulated by a procedural marble texture) while the opacity is constant. A more correct model of an inhomogeneous volume would have constant color but varying opacity.
 Pixar Animation Studios (510) 752-3000 (voice)  (510) 752-3151 (fax) Copyright © 1996- Pixar. All rights reserved. RenderMan® is a registered trademark of Pixar.