HDK
|
The back-end in Houdini is composed of several different components
SOHO provides a very small set of API functions. These functions sit between Houdini and Python scripts and provide a simple object model of the Houdini scene.
The SOHO API consists of 25 functions. These are documented in the HDK_SOHO_API namespace, though they are currently only available in a Python API.
These simple API functions have been wrapped in pure Python modules: soho.py and sohog.py, which can be found in $HFS/houdini/soho. It is the soho.py and sohog.py modules which are typically used.
SOHO is invoked through a SOHO output driver. A SOHO output driver is an OTL with some specific parameters created to control the behavior of the output driver.
A new output driver can be created by starting Houdini, then choosing "File -> New Operator Type" then choosing the "Output Driver Type".
When the user hits the render button, the output driver evaluates some parameters (see HDK_SOHO_ROP_Parameters) to determine what should be done.
The string soho_program
parameter is used to specify a Python program which gets run.
Before the Python program is invoked, the output driver may open files.
The output driver evaluates int soho_outputmode
. This parameter can have three possible values:
string soho_pipecmd
parameter is opened as a pipe. The file descriptor of this pipe is passed to the SOHO program (see HDK_SOHO_API::getFile())string soho_diskfile
parameter is opened for writing. The file descriptor for this file is passed to the SOHO program (see HDK_SOHO_API::getFile()).In addition, the output driver will open a temporary file to trap errors from the Python program. This is passed through HDK_SOHO_API::getFile(2).
The process to invoke the Python program is given as follows:
string soho_program
sys.stdout
and sys.stderr
(after soho.init()
), you may have to use sys.__stderr__.write(msg)
to see the output in the textport. You can, of course, call soho.warning() or soho.message() to add text to the output driver.There are different ways of reporting errors to the ROP from within your Python code. The easiest is to write to the stderr
which was set by HDK_SOHO_API::getFile(2).
Alternately, you can call one of:
msg
to the ROP as information textmsg
to the ROP as a warningmsg
to the ROP as error textThe output driver looks for certain parameters which it uses to determine how the output driver should operate. As with all operators, the parameters do not have to be visible to the user, they just have to exist.
The parameters are all defined in $HFS/houdini/soho/parameters/SohoIntrinsics.ds
, but are not visible in the Houdini interface by default. There are instructions in that file to make the parameters visible.
The easiest way to enable these properties is to set the environment variable HOUDINI_SOHO_DEVELOPER
before starting Houdini. For example:
string soho_program
(mandatory) int soho_outputmode
string soho_diskfile
soho_outputmode
evaluates to 1, this parameter specifies the disk file which is opened and passed to HDK_SOHO_API::getFile(1) int soho_mkpath
soho_outputmode
evaluates to 1, this parameter specifies that the intermediate directories of output files will be created (during script creation). string soho_pipecmd
soho_outputmode
evaluates to 0, this parameter specifies the command which is opened as a pipe and passed to HDK_SOHO_API::getFile(1) int soho_foreground
soho_pipecmd
, this determines whether the SOHO output driver will block and wait for the command to finish. The rps
and rkill
hscript commands can be used to manage background renders. int soho_multiframe
soho_program
will only be invoked one time, regardless of the frame range of the ROP. int soho_initsim
If this parameter exists and evaluates to a non-zero value, the output driver will initialize all simulation OPs before invoking the soho_program
.
This traverses all nodes in the scene and invokes specific methods on certain node types.
In addition, these optional parameters can be used to control the behavior of the SOHO API functions:
string soho_shopstyle
int soho_precision
int soho_indentstep
float soho_almostzero
abs(value) < soho_almostzero
will be output as 0 exactly. int soho_safename
int soho_autoheadlight
Add a light source at the camera's location if there are no other light sources in the scene. int soho_ipr_support
int soho_viewport_menu
SHOP_Node is a sub-class of OP_Node which can be thought of as a parameter collection. The SHOP consists of a set of parameters which it knows nothing about.
SHOP_Clerk is a class which packages the parameters of a SHOP into a form usable by a particular renderer. Clerks can be written in C++ (SHOP_Clerk) or Python (see $HFS/scripts/python/shopclerks).
To be used by a renderer, the parameters have to be gathered and processed into a form that the renderer can use. For example, for mantra, the parameters need to be built into a single string. The class which does this processing is the SHOP_Clerk class.
The clerk is called in several places
shopstring(string shop_path, string render_type)
expression function. The render_type
determines which clerk is used to process the SHOP string.hou.ShopNode.shaderString(render_type)
. Again, render_type
is used to determine which SHOP_Clerk is invoked.type
is 'shader' (or 'bounds'). The clerk type is determined by the soho_shopstyle
parameter defined on the output driver (see HDK_SOHO_ROP_Parameters).When designing the interface for an Object OTL (i.e. an light or camera), there are two approaches to take. Firstly, you can embed the actual light or camera object inside a subnetwork, then build channel references between the contained object and the subnetwork container. This approach is easiest and works well if you don't want users to customize the contained object.
On the other hand, if users need to add a rendering property to the camera or light object, then, they have to unlock the asset and manually dive in to modify the contents.
Wrangling is a process which allows a Python script to sit between the soho_program
and the Houdini object interface. The wrangling process is implemented solely in Python and has been implemented for IFD.py (mantra) and RIB.py (RenderMan).
When processing a light or camera object, these scripts will look for a parameter named light_wrangler
or camera_wrangler
. These specify Python scripts which are called to evaluate parameters (rather than calling SOHO to evaluate the parameters directly. The wrangler is free to evaluate any parameters it chooses to evaluate any parameters on the renderer.
For example, a light wrangler might have a parameter for a shadow map which gets evaluated during shadow map generation, but also when evaluating the light shader string.
By using a wrangler, you can design the interface the way you want, but let the user make modifications easily (without having to unlock assets).
As mentioned in HDK_SOHO_API::evaluate(), there are a set of intrinsic parameters. One such parameter is the state:previewmode
parameter. The value of this parameter is controlled by the IPR viewer, and may be one of
default:
Standard rendering modegenerate:
Generation phase of IPR rendering generate
mode, SOHO will keep the pipe (soho_pipecmd
) command open between invocations of the soho_program
.update:
Send updated changes from previous generation
In this rendering mode, the special object list parameters: objlist:dirtyinstance
, objlist:dirtylight
, objlist:dirtyspace
and objlist:dirtyfog
will contain the list of all objects modified since the last render (whether a generate or update).
As well, the parameters objlist:deletedinstance
, objlist:deletedlight
, objlist:deletedspace
and objlist:deletedfog
will list all objects which have been deleted from the scene.
Houdini has some helpful tools to help integrating RenderMan style renderers. These tools are mostly stand-alone programs which are invoked from different places in Houdini.
In the Houdini bin directory, you can find slo2otl.py
. This parses the output of sloinfo
to query the parameters defined on a prman shader object. The program then invokes the rmands.py
module to generate a SHOP OTL for the shader.
This process is fairly trivial, and hinges on the stand-alone tool hotl
(see http://www.sidefx.com/docs/current/assets/advanced_otl)
The rmands module builds a dialog script and uses hotl to package it up into the appropriate form.
When building RSL shaders with VOPS, the compiler needs to generate the object code in a specific place, log the errors and output in another place. Each RenderMan implementation also has a different shader compiler.
VOPs are set up to use a wrapper program to compile shader code (hrmanshader). The source code for this application can be found in $HFS/houdini/public/hrmanshader.
A SOHO render can be started in one of several places in Houdini. The user may have hit the render button. But alternatively, the user may have chosen to call the output driver from the viewport menu, or from the viewport render state, or the IPR viewer, or even invoking a render to generate a thumb-nail for a material preview.
Each of these different calling contexts has an override mechanism similar to the HDK_SOHO_API::pushOverrides() method.
There are several files in $HFS/houdini/soho/overrides which define parameters and their override values. The different contexts pass information to the overrides through a set of variables which are specific to the context.
For example, the ViewportRender override file is called when the user chooses to render from the render menu in the viewport. The viewport passes the viewing camera transform in the $cam_xform
variable and the file overrides the space:world
parameter (which is an intrinsic SOHO parameter) with the value of that transform.
These files are used to force rendering to be routed to flipbooks, to force the soho_outputmode
etc.
Sometimes it's necessary to modify the factory SOHO programs. For example, if an output driver doesn't work exactly the way you need it to, you may want to modify the Python code. The most obvious way of doing this is to copy the Python code to a location earlier in the HOUDINI_PATH
. Your module will be picked up before the factory version and so you're free to make any changes you want.
The problem with this approach is that occasionally new features or bugs will be fixed by SideFX in the factory module. That means that when you start using a new version of Houdini, you need to verify that your modified version gets updated with the correct changes.
Monkey Patching (http://en.wikipedia.org/wiki/Monkey_patch) is a method of modifying runtime code of a module without altering the original code. Using Monkey Patching allows you to use the factory code, but to make surgical changes to the code by replacing single functions in a module.
An example of Monkey Patching would be changing the sin()
function in the math
module.
This will change the math.sin()
function to call the mySin()
function instead of its original function. If you call patchMath(), then, all other code that uses math.sin()
will now call your function instead.
Here's an example which replaces the LightSource
function in the RIBapi
module.
If this file were placed in $HOME/houdini$VERSION/soho/RIB
.py, then, it will be called instead of $HFS/houdini/soho/RIB
.api.
One way of thinking about Monkey Patching (philosophically) is that you
An alternative to monkey patching is to use the IFDhooks or RIBhooks modules.
The RIB/IFD modules (i.e. RIB.py
RIBframe.py
) have callbacks defined through the hook modules. By placing a file named RIBuserhooks.py
(or IFDuserhooks.py
) in the Python path (including $HOUDINI_PATH/soho), your hooks will be picked up and called during the RIB generation.
Please note that if your hook functions need to access other parts of SOHO (i.e. importing RIBsettings.py), you may run into recursion errors. That is, RIBsettings will import RIBhooks, which then imports RIBsettings, etc. Please look at RIBhooks.py for possible ways around this issue.
A simple example which uses this would be something like: