Appendix D of the RenderMan Interface Specification, V3.1 [1] describes conventions for structuring RIB output. These conventions are supplied to facilitate the extraction of high-level information from the RIB file. By obeying these conventions modeling programs allow downstream RIB editors to access high-level entities within a scene. Modelers can benefit from the power of the RenderMan Interface by seamlessly integrating into sophisticated rendering production environments while third parties can enhance the power of a rendering production environment without writing a complete modeler. The purpose of this application note is to step through the process of creating a RIB file that conforms to the structuring conventions of Appendix D. The reader should refer to Appendix D (a copy of which appears here as Attachment 1) for complete details on these conventions. Examples are provided below in order to illustrate how a variety of modeling data structures map onto these conventions.
RIB is fast becoming a standard means of interchange between modeling and rendering systems. Due to the computational expense of high-quality rendering, network rendering servers and rendering service bureaus are beginning to appear. Designers will ask their modelers to create a RIB file which they can then send to their service bureau. In this scenario, the RIB file becomes separated from the modeling software. When the RIB file reaches the rendering station, it's possible that last minute alterations to the scene might have to be made. As stated in Appendix D:
In a distributed environment many decisions relating to the final appearance of rendered frames may need to be deferred until the selection of a particular renderer can be made.
If enough information is not present in the RIB file, a render management system cannot provide access to the components of the scene and the designer must either accept the existing scene as it is or go back to the drawing board to regenerate the RIB file. These unfortunate alternatives can be avoided by putting into the RIB file as much structural information as possible. Although RIB was not intended to be used as a full-function modeling format, its block structuring and stacking mechanisms go a long way toward enabling this.
The following abbreviated list should give you an idea of the kinds of functionality needed in a rendering production environment. This list should drive the discussion concerning what kind of information a modeler should build into the RIB file. As in Appendix D, we will use the term Render Manager to identify a program which in some way manages the rendering process. One or more Render Managers will make up a rendering production environment.
A rendering production environment should allow a designer to:
In order for a Render Manager to perform tasks such as these, incoming RIB files must be predictable and logically structured. The problem of organizing a RIB file falls directly onto the shoulders of the modeler developer and the data structures within his program. The good news is that the modeler can then off-load certain rendering-related tasks downstream. Simply stated, a well-structured RIB file identifies the boundaries of those entities which are, in the mind of the user, logically distinct. Additionally, information pertaining to the resource requirements of the rendering job should be flagged.
Only a few conceptual elements must be provided in the RIB file in order to enable a Render Manager to do its job. Most important in a modeled scene are what Appendix D calls user entities. The kinds of last minute changes that designers require often involve individual objects in the scene. For example, in order for a designer to assign new wallpaper to the interior walls of a model, the data for the walls must be easily identified in the RIB file. Two commonly employed organizational principles are Geometry and Material Composition.
Geometric Grouping. A user entity based on the geometric principle might be the collection of all primitives (polygons, patches, etc.) that make up a piece of furniture in a house. Because we can gather all of, say, a chair's primitives, it is possible to move and re-orient the chair in space.
Material Grouping. A user entity organized on the principle of material composition might consist of all the primitives representing the parts of a chair that are made of fabric. Another user entity might be all the primitives in the same chair representing the wood. When the designer identifies objects based on this principle, he is usually interested in changing the appearance of the entity. It should be possible to try out many different combinations of woods and fabrics in some render management system. Individual primitives can obviously be members of both the fabric grouping and the chair grouping. Moreover, we can imagine unrelated geometric entities organized into the same material grouping. Here, the designer may wish to change the fabric on all the chairs and sofas in the living room.
A render job controller regulates parameters that specify how a RIB file is rendered. In order to distribute a series of frames around a rendering network, we must be able to identify and extract from the RIB file all the information pertaining to a given frame. It should also be possible to easily identify the RIB requests which control rendering resolution, quality, quantization and exposure. Non-standard shaders, textures and rendering capabilities should be clearly flagged.
The strategy promoted by Appendix D is quite simple. It suggests that the block structuring mechanisms of the RenderMan Interface are sufficient to encapsulate the geometric and material groupings discussed above. Just as in modern block-structured programming languages, clarity can be enhanced by localizing the scope of requests and variable definitions. Global state should be used sparingly and changes to the global state should only affect known entities. In the RenderMan Interface five kinds of blocks provide these benefits of structured programming and allow the developer to build the logical structure of a modeling database into the RIB file.
The global block is the outermost block in the RenderMan Interface. It is bounded by calls to RiBegin and RiEnd. Because a RIB file is bounded by the file boundaries, no specific RIB requests are required to encapsulate this block. Global attributes, options and variable declarations should be located at this level. Because any valid RenderMan requests will affect the default behavior throughout, it is advised to place requests that do not change at this level. For example, if a modeler puts many frames into a single RIB file, it should establish frame resolution, aspect ratio, quantization and global variable declarations in the global block. The alternative would be to specify these things within each frame of the animation. By placing these global declarations in the global block, a Render Manager can effectively change the resolution of the entire animation without parsing through the complete RIB file. The reader is referred to Appendix D for a more detailed discussion on guidelines and restrictions for global attributes and options.
The frame block identifies the individual frames of an animation. All of the information required to render a given frame should either be present in the graphics state (set previously in the global block) or be located within the frame block itself. By using frame blocks, the modeler enables the Render Manager to pull individual frames out of the file and render them independently. Appendix D specifies that any frame-specific global options, attributes and declarations should be set immediately following the FrameBegin request. Thus, a Render Manager need only find a frame block and search the first several RenderMan requests to determine frame block settings. There should be no difficulty in changing any of these settings as the scope of their influence is well defined.
The world block is provided to encapsulate all of the geometric elements of a particular frame. Any transformations applied externally during the current frame are frozen and associated with the camera. RIB requests that affect every element in a scene should directly follow the WorldBegin request and precede any geometric requests. Typically, global defaults for the graphic state are set here. Global light source requests should generally follow the WorldBegin request and precede any geometry declarations.
The attribute block is the fundamental block for encapsulating user entities. The geometry and attributes for any object a user can identify should be located in a block of this kind. For example, if a chair is made up of 500 polygons, all of these polygons and any attributes (color, opacity, surface, etc.) which are linked to the chair should be found within this block. If we remove the block from the world or relocate the block in the RIB file, no undesirable effects can occur since any changes within the block are undone at its end. A Render Manager will interpret calls to RiAttributeBegin and RiAttributeEnd as entity delimiters. By tagging an attribute block with an identifier attribute, the modeler establishes an organizational principle for an entity.
Within an attribute block, the structure is simple. All attribute settings should follow immediately after the RiAttributeBegin request. Geometric transformations are considered attributes in the RenderMan Interface and should also precede any geometry. Depending on the internal architecture of the modeling software, user entities may be described around a local origin. In this case, a modeling transformation commonly transforms the entity from object space to world space. If this is not the case, the modeler will probably be working entirely in world space and no modeling transform will be present.
After setting all of the attributes for the entity, the geometry should immediately follow. Appendix D identifies two special identifier attributes to allow the modeler to tag a user entity with a name. These calls are invoked as follows:
RiAttribute("identifier","name", (RtPointer) &objectname,RI_NULL); RiAttribute("identifier","shadinggroup", (RtPointer) &shadinggroupname,RI_NULL);
Because the modeler tags all entity blocks with an identifier, a Render Manager can differentiate between collections of geometry that are organized according to a geometric relationship (like all the primitives of the chair) and geometry organized by a material relationship (like all the primitives made of fabric). To allow for hierarchically defined models, attribute blocks may be arbitrarily nested.
Because geometric transformations are considered attributes by the RenderMan Interface, the transform block is a weaker form of the attribute block. Since transform blocks do not protect the global state from changes to appearance attributes, they cannot be used to identify user entities. They still play a useful function in hierarchical models.
The following examples are provided to help the developer create properly structured RIB files. We present the examples in shorthand notation for the sake of brevity and clarity and omit the definitions of data structures. Internal elements of these undefined data structures are identified with standard C structure notation. Since internal data structures are undefined, these examples show logical structure, not syntax.
Following is an example of well-structured RenderMan calls for a database of architectural elements whose locations are known and fixed. Here, we assume that the elements are made up of hundreds of polygons whose vertices lie in world space. Since the elements lack individual object space coordinates, a render management system may not be able to move the elements through the scene but should be able to change their appearances. Since the positions of the elements are fixed and have no local object coordinate system, we output the entities as individual shading groups. A render manager should interpret this file as a single user entity (the building) made up of several shading groups (the walls, roof, doors, etc.).
#include <ri.h> RtPoint CameraFrom = {100.0,100.0, 100.0}, CameraTo = {400.0, 600.0, 400.0}; extern char *ShadersList; main() { RtMatrix cameraXform; extern struct InternalDatabase Scene; int i, j; /* * The following call opens the global block */ RiBegin(RI_NULL); /* * The next call is required * and identifies conformance to Appendix D */ RiArchiveRecord("structure", "RenderMan RIB-Structure 1.1"); /* * The following calls identify resource requirements */ RiArchiveRecord("structure", "Scene Big Building"); RiArchiveRecord("structure", "Frames 1"); RiArchiveRecord("structure", "Shaders %s", ShadersList); RiArchiveRecord("structure", "CapabilitiesNeeded ShadingLanguage"); RiArchiveRecord("structure", "For Andy Warhol"); /* * Global setting for resolution, quantization might go here; * often it's better to leave them out for the Render Manager * or output device to specify */ RiFrameAspectRatio(4.0/3.0); /* * The following is considered Frame Information by Appendix D */ RiArchiveRecord("structure", "CameraOrientation %f %f %f %f %f %f", CameraFrom[0], CameraFrom[1], CameraFrom[2], CameraTo[0], CameraTo[1], CameraTo[2]); /* * AimCamera is a function that constructs a matrix * based on camera position */ AimCamera(CameraFrom,CameraTo,cameraXform); RiTransform(cameraXform); RiWorldBegin(); /* * Global attributes and options could go here */ RiLightSource("distantlight",RI_NULL); /* More could go lights here */ /* * The following identifier implies a geometric * grouping of the whole model */ RiAttributeBegin(); RiAttribute("identifier", "name", (RtPointer) &Scene.SceneName, RI_NULL); for (i=0; i<Scene.NumObjects; i++) { RiAttributeBegin(); RiAttribute("identifier", "shadinggroup", (RtPointer) &Scene.ObjectList[i].ObjectName, RI_NULL); RiSurface(Scene.ObjectList[i].SurfaceName, RI_NULL); RiArchiveRecord("comment","Polygons for %s", Scene.ObjectList[i].ObjectName); /* * All of the geometry for a shading group goes here * Lots of polygons or patches or spheres * for(j=0;j<Scene.ObjectList[i].NPolys;j++) * RiPolygon(...); (read the internal data structure) */ RiAttributeEnd(); } RiAttributeEnd(); /* end of geometric entity */ RiWorldEnd(); RiEnd(); }
##RenderMan RIB-Structure 1.1 ##Here's a subset of the RIB file with actual polygon calls left out: ##Scene Big Building ##Frames 1 ##Shaders brick shingle adobe concrete glass oak ##CapabilitiesNeeded ShadingLanguage ##For Andy Warhol version 3.03 FrameAspectRatio 1.333333 ##CameraOrientation 100.0 100.0 100.0 400.0 600.0 400.0 Transform [-0.707107 -0.539164 0.457496 0 0 0.646997 0.762493 0 0.707107 -0.539164 0.457496 0 0 43.1331 -167.748 1] WorldBegin LightSource "distantlight" 1 AttributeBegin Attribute "identifier" "name" ["BigBuilding"] AttributeBegin Attribute "identifier" "shadinggroup" ["roof"] Surface "shingle" #Polygons for roof AttributeEnd AttributeBegin Attribute "identifier" "shadinggroup" ["windows"] Surface "glass" #Polygons for windows AttributeEnd . . . AttributeBegin Attribute "identifier" "shadinggroup" ["doors"] Surface "oak" #Polygons for doors AttributeEnd AttributeEnd WorldEnd
The next example shows how a modeler which employs notions of object space, modeling transforms, and hierarchy might choose to create well structured RenderMan calls. The scene is that of an office composed of several objects (pieces of furniture). Each object is built around its own origin and oriented in the scene with a modeling transformation. Further, each object might be composed of several shading groups which represent the different materials of the object.
#include <ri.h> RtPoint CameraFrom = {10.0, 20.0, 10.0}, CameraTo = {0.0, 0.0, 0.0}; extern char *ShadersList; main() { RtMatrix cameraXform; extern struct InternalDatabase Scene; int i, j; RiBegin(RI_NULL); RiArchiveRecord("structure", "RenderMan RIB-Structure 1.1"); RiArchiveRecord("structure", "Scene Office Layout"); RiArchiveRecord("structure", "Frames 1"); RiArchiveRecord("structure", "Shaders %s", ShadersList); RiArchiveRecord("structure", "For Bertolt Brecht"); RiArchiveRecord("structure", "CapabilitiesNeeded ShadingLanguage"); RiFrameAspectRatio(1.5); RiArchiveRecord("structure", "CameraOrientation %f %f %f %f %f %f", CameraFrom[0], CameraFrom[1], CameraFrom[2], CameraTo[0], CameraTo[1], CameraTo[2]); /* Construct matrix into cameraXform based on camera position */ AimCamera(CameraFrom, CameraTo, cameraXform); RiTransform(cameraXform); RiWorldBegin() /* overhead lamps, add other lights here */ RiLightSource("spotlight", RI_NULL); /* recursively traverse data structure */ Traverse(&Scene); RiWorldEnd(); RiEnd(); } Traverse(s) struct InternalDatabase *s; { int i; RiAttributeBegin(); if (s->IsObject) { RiAttribute("identifier", "name", (RtPointer) &(s->name), RI_NULL); RiTransform(s->transform); /* here's a modeling transform */ } if (s->IsShadinggroup) { RiAttribute("identifier", "shadinggroup", (RtPointer) &(s->name), RI_NULL); } if (s->IHavePrimitives) { /* * First set the appropriate surface type */ RiSurface(s->Surface, RI_NULL); RiColor(s->Color); RiOpacity(s->Opacity); /* * Next step through data structures for polygons, patches. * This loop is where all the geometry is emitted. */ for (i=0; i < s->nprimitives; i++) { RiPolygon(...); } } if (s->IHaveChildren) Traverse(s->child); /* here's one recursion path */ if (s->IHaveSibling) Traverse(s->next); /* here's another recursion path */ RiAttributeEnd(); }
##RenderMan RIB-Structure 1.1 ##This is an example RIB file for an Office with one table, one chair and a bookcase. ##Indentation has been added to make the RIB easier to read and understand. ##Scene Office Layout ##Frames 1 ##Shaders formica chrome oak fabric ##For Bertolt Brecht ##CapabilitiesNeeded ShadingLanguage version 3.03 FrameAspectRatio 1.50 ##CameraOrientation 10.0 20.0 10.0 0.0 0.0 0.0 Transform [0.707107 -0.57735 -0.408248 0 0 0.57735 -0.816497 0 -0.707107 -0.57735 -0.408248 0 0 0 24.4949 1 ] WorldBegin LightSource "spotlight" 1 AttributeBegin Attribute "identifier" "name" "bookcase" Surface "oak" Transform [1 0 0 0 0 1 0 0 0 0 1 0 2.5 0 5 1] # Polygons for bookcase AttributeEnd AttributeBegin Attribute "identifier" "name" "chair1" Transform [0.707107 0 -0.707107 0 0 1 0 0 -0.707107 0 -0.707107 0 0 0 1.41421 1] AttributeBegin Attribute "identifier" "shadinggroup" "chairframe" Surface "chrome" # Polygons for frame of the chair AttributeEnd AttributeBegin Attribute "identifier" "shadinggroup" "chairseat" Surface "fabric" # Polygons for seat of the chair AttributeEnd AttributeEnd AttributeBegin Attribute "identifier" "name" "table" Transform [0.0995037 0 -0.995037 0 0 1 0 0 -0.995037 0 -0.0995037 0 0 0 10.0499 1] AttributeBegin Attribute "identifier" "shadinggroup" "legs" Surface "oak" # geometric primitives for the legs AttributeEnd AttributeBegin Attribute "identifier" "shadinggroup" "top" Surface "oak" # geometric primitives for the table top AttributeEnd AttributeBegin Attribute "identifier" "shadinggroup" "surface" Surface "formica" # geometric primitives for the table surface AttributeEnd AttributeEnd WorldEnd
Appendix D provides a convention for RIB files that contain individual entities. A modeler which is not willing or able to output rendering information in the RIB file (e.g. lights, camera orientation, or surface attributes) can create a RIB entity file. By appending the Entity keyword to the first RenderMan structure request, a modeler identifies this file as an incomplete RIB stream that contains a single entity. These files cannot be sent directly to a renderer because they lack the requisite world block. Instead the intention is that a Render Manager will be employed to orient the entity in world space, to add light sources, surface attributes and camera definitions. The next example provides a skeleton for the creation of a RIB entity file. Below it is RIB output for the file.
#include <ri.h> main() { extern struct entity E; RtBound bbox; RiBegin(RI_NULL); RiArchiveRecord("structure", "RenderMan RIB-Structure 1.1 Entity"); RiAttributeBegin(); RiAttribute("identifier", "name", (RtPointer) &E.name, RI_NULL); bbox[0] = E.xmin; bbox[1] = E.xmax; bbox[2] = E.ymin; bbox[3] = E.ymax; bbox[4] = E.zmin; bbox[5] = E.zmax; RiBound(bbox); /* Traverse the structure describing the entity */ /* The traverse function might resemble that of the previous example */ traverse(&E.data); RiAttributeEnd(); RiEnd(); }
##RenderMan RIB-Structure 1.1 Entity version 3.03 AttributeBegin Attribute "identifier" "name" "Gertrude" Bound -100.0 100.0 -15.0 12.0 -38.0 49.0 # contents of user entity will appear here; it may be arbitrarily nested AttributeEnd
Because of the generality of the RenderMan Interface it is possible to generate RIB files that cannot be read or filtered by Render Managers. By abiding by the conventions described here and in Appendix D of the RenderMan Interface Specification, a developer can better integrate a product into an unknown and widely varying modeling and rendering environment.
In this application note we have presented several alternatives for storing a geometric scene in the RIB file. The examples illustrate the application of these conventions and represent points on a continuum from highly structured to minimally (but sufficiently) structured. This application note concentrates on the aspects of the RenderMan Interface that allow structural information to be inserted into the RIB stream. Other special commenting conventions for imbedding resource requirements and administrative information are described in Appendix D. In general, you should refer to the RenderMan Specification and Appendix D for definitive information on RIB and structured RIB.
The RenderMan Interface Specification, V3.1, September 1989, Pixar.
Appendix D of The RenderMan Interface Specification, V3.1
Pixar Animation Studios
|