ri -- Python RenderMan binding

  1. Introduction
  2. Using the API
  3. Online documentation
  4. Differences between the C and Python API
  5. Implementation specific options
  6. Error handling

1. Introduction

The RenderMan interface is an API that's used to communicate a 3D scene description (which includes 3D geometry, light sources, a camera description, etc) to a renderer which will produce a 2D image of that scene. The API itself is independent of a particular renderer and can be used for a whole bunch of renderers, where each could use entirely different rendering algorithms. There are some excellent renderers freely available, such as 3Delight or the Open Source renderer Aqsis or Pixie. On the commercial side, the most popular renderers are Pixar's Photorealistic RenderMan (PRMan), RenderDotC and AIR. The RenderMan interface was created by Pixar and the official specification can be downloaded from their site (The RenderMan Interface Specification).

This document is not an introduction to the RenderMan interface itself, it just explains the usage of this particular Python binding. The binding was written to be compliant to v3.2 of Pixar's RenderMan Interface specification. However, it also supports some newer features such as string handles for light sources or object instances.

2. Using the API

It is safe to import the module using

from ri import *

All the functions that get imported start with the prefix "Ri", all constants start with "RI_" or "RIE_", so you probably won't get into a naming conflict.

After importing the module this way you can use the functions just as you're used to from the C API (well, almost).

from ri import *


The parameter to RiBegin() determines where the output is directed to. You can pass one of the following:

3. Online documentation

Every function has an associated doc string that includes a short description of the function, some information about what parameters the function expects and an example how the function is called.

Example (inside an interactive Python session):

>>> from ri import *
>>> print RiPatch.__doc__
RiPatch(type, paramlist)

    type is one of RI_BILINEAR (4 vertices) or RI_BICUBIC (16 vertices).

    Number of array elements for primitive variables:
    constant: 1              varying: 4
    uniform:  1              vertex:  4/16 (depends on type)

    Example: RiPatch(RI_BILINEAR, [0,0,0, 1,0,0, 0,1,0, 1,1,0])

or using the pydoc module:

>>> from pydoc import help     # this line isn't required anymore in Python 2.2 or later
>>> from ri import *
>>> help(RiMotionBegin)

Help on function RiMotionBegin in module ri:

    Start the definition of a moving primitive.

    You can specify the time values directly or inside a sequence,
    for example, RiMotionBegin(0,1) or RiMotionBegin([0,1]).

    Example: RiMotionBegin(0.0, 1.0)
             RiTranslate(1.0, 0.0, 0.0)
             RiTranslate(1.0, 2.0, 0.0)

or from the shell (outside the Python shell):

$ pydoc ri.RiCropWindow

Python Library Documentation: function RiCropWindow in ri

RiCropWindow(left, right, bottom, top)
    Specify a subwindow to render.

    The values each lie between 0 and 1.

    Example: RiCropWindow(0.0, 1.0 , 0.0, 1.0)  (renders the entire frame)
             RiCropWindow(0.5, 1.0 , 0.0, 0.5)  (renders the top right quarter)

4. Differences between the C and Python API

The Python RenderMan binding is rather close to the C API, however there are some minor differences you should know about.

4.1 Types

In this binding typing is not as strict as in the C API. The type names RtBoolean, RtInt, RtFloat, etc. don't exist in this API, you just use the corresponding Python types (for booleans the module defines the constants RI_TRUE and RI_FALSE).

Wherever the API expects vector types (RtPoint, RtMatrix, RtBound, RtBasis) you can use any value that can be interpreted as a sequence of the corresponding number of scalar values. These can be lists, tuples or your own class that can be used as a sequence.

It is also possible to use nested sequences instead of flat ones. For example, you can specify a matrix as a list of 16 values or as a list of four 4-tuples. The following two calls are identical:

RiConcatTransform([2,0,0,0, 0,2,0,0, 0,0,2,0, 0,0,0,1])

RiConcatTransform([[2,0,0,0], [0,2,0,0], [0,0,2,0], [0,0,0,1]])

4.2 Parameter lists

4.3 Arrays

In the C API functions that take arrays as arguments usually take the length of the array as a parameter as well. This is not necessary in the Python binding. You only have to provide the array, the length can be determined by the function.

For example, in C you might write:

RtPoint points[4] = {0,1,0, 0,1,1, 0,0,1, 0,0,0};
RiPolygon(4, RI_P, (RtPointer)points, RI_NULL);

The number of points has to be specified explicitly. In Python however, this call could look like this:

points = [0,1,0, 0,1,1, 0,0,1, 0,0,0]
RiPolygon(RI_P, points)

The functions that are affected by this rule are:


4.4 User defined functions

Some RenderMan functions allow to take user defined functions as input which will be used during rendering. Since this implementation only outputs RIB streams it's not possible to use your own functions in these cases, instead you always have to use one of the predefined functions.

Filter functions

It's not possible to use your own filter functions, you have to use one of the predefined filters:


It's not possible to use your own procedurals directly in the RIB generating program, you can only use one of the predefined procedural primitives: However, this is not really a restriction since you always can use RiProcRunProgram to invoke your Python program that generates geometry.

4.5 Extended transformation functions

The transformation functions RiTranslate(), RiRotate(), RiScale() and RiSkew() have been extended in a way that is not part of the official spec. Each of these functions takes one or two vectors as input which usually are provided as 3 separate scalar values, like the axis of a rotation for example:

RiRotate(45, 0,0,1)

Now in this implementation you can choose to provide such vectors as sequences of 3 scalar values:

RiRotate(45, [0,0,1])

axis = vec3(0,0,1)
RiRotate(45, axis)

4.6 Empty stubs

All the functions do what they're supposed to do (well, hopefully ;) except for the function RiTransformPoints(). This function is defined but it does nothing, it's only an empty stub (since the module only outputs RIB and doesn't maintain transformation matrices).

5. Implementation specific options

There's currently one option that's specific to this RenderMan binding and that won't produce any RIB call but will control what gets written to the output stream:
If this option is set to 0 directly after RiBegin() is called, then no "version" call will be generated in the RIB stream (default is 1).
New in version 1.1.

6. Error handling

Besides the three standard error handlers RiErrorIgnore, RiErrorPrint (default) and RiErrorAbort the module provides an additional error handler called RiErrorException. Whenever an error occurs RiErrorException raises the exception RIException.

If you install a new error handler with RiErrorHandler() only the three standard error handlers will produce an output in the RIB stream, if you install RiErrorException or your own handler then the handler is installed but no RIB output is produced.

The module does some error checking, however there are still quite a bit of possible error cases that are not reported. For example, the module checks if parameters are declared, but it is not checked if you provide the correct number of values. In general, the module also does not check if a function call is valid in a given state (e.g. the module won't generate an error if you call RiFormat() inside a world block).

Copyright © 2002 Matthias Baas (baas@ira.uka.de)

The RenderMan (R) Interface Procedures and Protocol are:
Copyright 1988, 1989, 2000, Pixar
All Rights Reserved

RenderMan (R) is a registered trademark of Pixar