Rendering Using Python in mantra renders

See also: Mantra 9.1 and 9.5 rendering properties

Overview

There are two places where mantra can call Python. This allows users to fine-tune mantra’s processing of data.

  • As the IFD is parsed, mantra can call a filtering function to query and change properties.

  • Mantra can call a function at the completion of every tile. This lets you insert more sophisticated statistical analysis of the rendering process.

How to query and set properties in callback scripts

In Python scripts run by mantra, you can import the mantra module. The mantra module has two functions:

property("name")

Returns the value associated with the property name. The value will always be returned in a list, even if there’s a single scalar value.

setproperty("name", value)

This function will set the named property to the value given. The function only works in filtering callbacks (not tile callbacks).

Filtering

Use the -P option to mantra to specify a Python script that mantra should run during parting of the IFD file. You can specify multiple scripts separated by semicolons (;).

You can optionally specify arguments which mantra passes to the Python script when it’s loaded. You can use this to modify the behavior of the filtering script.

This script may define any of the following functions. If the script defines a function, mantra will call the function at the appropriate point while parsing the IFD file. Mantra will ignore any functions you don’t define.

filterGeometry()

Called just prior to the ray_end statement which locks off a geometry object. This allows the program to query geometry: settings and possibly alter them.

filterMaterial()

Mantra has material blocks which can be applied on a per-primitive basis. This function is called before a material is locked off. The function can add or change properties on the material.

filterInstance()

Called just prior to the ray_end statement which locks off the settings for an instance object. The function can query object: settings and possibly alter them.

filterLight()

Called just prior to the ray_end statement which locks off the settings for a light object. The function can query light: settings and possibly alter them.

filterFog()

Called just prior to the ray_end statement which locks off the settings for a fog object. The function can query fog: settings and possibly alter them.

filterCamera()

Called just before the global properties are locked off for the render. This is usually prior to the declaration of any objects in the IFD. If you change global properties after this, they probably will have no effect.

Since mantra calls this function before any lights or instances are declared, you can set default light or instance properties here. Mantra will apply any Instance/Light properties you set here to any objects which don’t declare the property explicitly.

filterRender()

Called just before the ray_raytrace command. It’s not possible to change any properties at this time in the IFD processing. However, for statistics or validation, it might be useful to have this method available.

filterPlane()

As of Houdini 9.1, image plane settings are now properties (see image plane properties. This filtering callback allows image plane properties to be queried/modified as the IFD is parsed.

def filterPlane():
    print 'Defining plane:',
          mantra.property('plane:channel'),
          mantra.property('plane:variable')
    mantra.setproperty('plane:quantization', 'float')

filterEndRender()

Called just after the image has been rendered.

filterQuit()

Called just before mantra quits.

Filter example

#
# Simple Example Filter
#

import sys, mantra

Verbose = False

# Process any arguments during initialization of the module

for arg in sys.argv[1:]:
    if arg == '-v':
        Verbose = True

# A callback to change the surface shader of all objects

def filterInstance():
    if Verbose:
        print 'Filtering:', mantra.property('object:name')

    mantra.setproperty('object:surface', 'plastic diff 1 0 0')

    catstr = mantra.property('object:categories')[0]
    categories = catstr.split(', ')
    for c in categories:
        if c == 'pass1':
            mantra.setproperty('object:renderable', 0)

        elif c == 'pass2':
            mantra.setproperty('object:surface', 'matte')
            mantra.setproperty('object:overridedetail', 1)

Tile completion

You can use the renderer:tilecallback property to specify a python script (and optional arguments) which mantra will run after each tile is completed.

Unlike the Python filters, only one script may be specified (commands may not be semi-colon separated). The script does not define functions that mantra calls. Instead, mantra runs the entire script each time a tile is complete. The tile script can not call mantra.setproperty().

Some additional properties are available to the tile callback which might be useful for gathering/printing statistics.

  • tile:ntiles – The total number of tiles in the image.

  • tile:ncomplete – The number of tiles completed so far.

  • tile:laptime – The number of seconds taken to render the last tile.

  • tile:totaltime – The total number of seconds to render since the render began. This does not include time to load the scene, but rather is defined as the time since the first tile began rendering.

  • tile:coords – The tile bounding box (in pixels).

  • tile:memory – The amount of RAM in use by mantra.

  • import sys,mantra
    
    tile = mantra.property('tile:ncomplete')[0]
    if tile == 1:
        # This is the first tile rendered in the image
    
        print mantra.property('renderer:name')
        ntiles = mantra.property('tile:ntiles')[0]
        prev_pct = -1
        lap = 0
        lsum = 0
    
    lap += mantra.property('tile:laptime')[0]
    pct = tile * 100 / ntiles
    
    if pct != prev_pct:
        mem = mantra.property('tile:memory')[0]
        total = mantra.property('tile:totaltime')[0]
        fpct = float(tile)/float(ntiles) # Percent complete
        prev_pct = pct
    
        print('%03d%% Complete - Laptime %g/%g - ETA %g seconds\n' %
                (pct, lap, total, total/fpct))
    
        lap = 0         # Reset the lap counter