4.4. cri — RenderMan binding for direct access to a renderer

The cri module is an alternative version of the ri module that uses the Python ctypes module to interface a renderer directly. The ctypes module is a foreign function library that is part of the standard Python libraries since Python 2.5 [1]. With previous versions of Python, the module has to be installed separately. The module can be used in combination with any renderer that implements the RenderMan API in a shared library. Using this module instead of the ri module has a number of advantages:

  • You can mix Python code and C/C++ code and issue RenderMan commands from any language into the same stream.
  • You can feed the renderer directly without having to create an intermediate RIB. This also means you can use Python functions for procedurals, filter functions or error handlers.
  • Even when generating RIB using this module is usually faster as the actual RIB export is done by C/C++ code. The code is considerably faster if you are using ctypes arrays or numpy arrays in your parameter lists. In this case, no data conversion is required.

The disadvantage of using this module is that it depends on an external dynamic library that must implement the actual functionality. This means if you have not installed a renderer that ships with such a library you cannot use the module.

Before any RenderMan function can be used, a library has to be loaded using the loadRI() function. The returned module-like object can then be used just like the ri module.

cgkit.cri.loadRI(libName)

Load a RenderMan library and return a module-like handle to it. libName is the name of a shared library that implements the RenderMan interface. The name can either be an absolute file name or just the name of the library (without suffix or “lib” prefix) in which case the function tries to find the library file itself. The return value is an object that can be used like a module, i.e. it contains all RenderMan functions, constants, etc. If libName is None or the empty string, the return value is just a reference to the ri module.

import cgkit.cri

ri = cgkit.cri.loadRI("3delight")

ri.RiBegin(ri.RI_NULL)
ri.RiWorldBegin()
ri.RiSurface("plastic")
ri.RiSphere(1,-1,1,360)
ri.RiWorldEnd()
ri.RiEnd()

The RenderMan function names can either be used with or without the “Ri” prefix. So the following is equivalent to the above:

ri.Begin(ri.RI_NULL)
ri.WorldBegin()
ri.Surface("plastic")
ri.Sphere(1,-1,1,360)
ri.WorldEnd()
ri.End()

The following table lists the library names for some renderers that are known to work with this module:

Renderer Library Name
3Delight 3delight
Aqsis aqsislib / ri2rib
Pixie ri
PRMan (v14+) prman
cgkit.cri.importRINames(ri, ns)

Import the RenderMan names into the given namespace. ri is the module-like object that was returned by loadRI() and ns is a dictionary containing a module namespace (such as globals()) that will receive the “imported” symbols. Only the names with the “Ri” prefix will be available. Example:

ri = cgkit.cri.loadRI("3delight")
cgkit.cri.importRINames(ri, globals())

RiBegin(RI_NULL)
RiWorldBegin()
RiSurface("plastic")
RiSphere(1,-1,1,360)
RiWorldEnd()
RiEnd()

4.4.1. Example

The following example renders three Koch curves that form sort of a “snowflake” shape. Each curve is created by a procedural which just uses a regular Python function as subdivide function. No RIB is generated by this example. — It is possible to create the same image using the generic ri module, but the procedural would have to be written as a separate Python script that is invoked using the “RunProgram” procedural. The code would actually be longer because you would have to encode/decode the parameters of the procedural as strings whereas this example directly passes vector objects around.

# Render a Koch snowflake as procedurals

import cgkit.cri
from cgkit.cgtypes import *

def bound(A, E):
    """Compute the bounding box of one segment."""
    eps = 0.03
    dv = E-A
    n = vec3(-dv.y, dv.x, 0)
    C = 0.5*(A+E) + 0.2887*n
    xx = [A.x, C.x, E.x]
    yy = [A.y, C.y, E.y]
    bound = [min(xx)-eps, max(xx)+eps, min(yy)-eps, max(yy)+eps, -0.001, 0.001]
    return bound

def subdiv(data, detail):
    """Subdivide function."""
    A,E = data
    dv = E-A
    if dv.length()<0.005:
        RiCurves(RI_LINEAR, [2], RI_NONPERIODIC, P=[A,E], constantwidth=0.003)
    else:
        t = 1.0/3
        B = (1.0-t)*A + t*E
        D = (1.0-t)*E + t*A
        n = vec3(-dv.y, dv.x, 0)
        C = 0.5*(A+E) + 0.2887*n
        RiProcedural((A,B), bound(A,B), subdiv)
        RiProcedural((B,C), bound(B,C), subdiv)
        RiProcedural((C,D), bound(C,D), subdiv)
        RiProcedural((D,E), bound(D,E), subdiv)

# Load the RenderMan API.
# Replace the library name with whatever renderer you want to use.
ri = cgkit.cri.loadRI("3delight")
cgkit.cri.importRINames(ri, globals())

RiBegin(RI_NULL)
RiFormat(1024,768,1)
RiDisplay("koch.tif", RI_FRAMEBUFFER, RI_RGB)
RiPixelSamples(3,3)
RiProjection(RI_ORTHOGRAPHIC)
RiScale(vec3(0.8))
RiTranslate(0,0.55,5)
RiWorldBegin()
RiSurface("constant")
RiColor((1,1,1))
RiPatch(RI_BILINEAR, P=[-2,2,1, 2,2,1, -2,-2,1, 2,-2,1])
RiColor((0,0,0))
RiProcedural((vec3(-1,0,0),vec3(1,0,0)), [-2,2, -2,2, -0.01,0.01], subdiv)
RiProcedural((vec3(0,-1.732,0),vec3(-1,0,0)), [-2,2, -2,2, -0.01,0.01], subdiv)
RiProcedural((vec3(1,0,0), vec3(0,-1.732,0)), [-2,2, -2,2, -0.01,0.01], subdiv)
RiWorldEnd()
RiEnd()

Running the above example produces this image:

_images/koch.png

Footnotes

[1]Python 2.5 is embedded in Maya 2008/2009 and Houdini 9, whereas Maya 8.5 still uses Python 2.4.

Table Of Contents

Previous topic

4.3. ri — Generic RenderMan binding to produce RIB

Next topic

4.5. riutil — RenderMan utilities

This Page