:mod:`cri` --- RenderMan binding for direct access to a renderer ================================================================ .. module:: cgkit.cri :synopsis: RenderMan binding for direct access to a renderer The :mod:`cri` module is an alternative version of the :mod:`ri` module that uses the Python :mod:`ctypes` module to interface a renderer directly. The :mod:`ctypes` module is a foreign function library that is part of the standard Python libraries since Python 2.5 [#]_. 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 :mod:`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 :func:`loadRI` function. The returned module-like object can then be used just like the :mod:`ri` module. .. % loadRI .. function:: 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 :mod:`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`` | +--------------+---------------------------+ .. % importRINames .. function:: importRINames(ri, ns) Import the RenderMan names into the given namespace. *ri* is the module-like object that was returned by :func:`loadRI` and *ns* is a dictionary containing a module namespace (such as :func:`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() 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 :mod:`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: .. image:: pics/koch.* :width: 14cm .. rubric:: Footnotes .. [#] Python 2.5 is embedded in Maya 2008/2009 and Houdini 9, whereas Maya 8.5 still uses Python 2.4.