Houdini Development Toolkit - Version 9.0
Side Effects Software Inc. 2007
Compositing Operators
Pixel Function Operations
Pixel function COPs are simple functions that take an input pixel and
some parameter values, and produce the corresponding output pixel.
Because they don't access other neighbouring pixels, other inputs or
planes, they can be highly optimized. Gamma and luminance functions are
examples of pixel functions.
Unlike other COPs, you don't override the cook method. Instead, you
override the method:
virtual RU_PixelFunction * addPixelFunction(const TIL_Plane *plane,int array_index, float t, int xres, int yres, int thread)
and create a class derived from RU_PixelFunction which implements your
function. In addPixelFunction, you can evaluate parameters, so it is
not necessary to create a context data for a pixel function COP.
To begin, let's take a look at the important methods in
RU_PixelFunction that you may need to override:
class RU_API RU_PixelFunction
{
public:
RU_PixelFunction();
virtual ~RU_PixelFunction();
virtual bool needAllComponents() const;
virtual bool eachComponentDifferent() const;
virtual RUPixelFunc getPixelFunction() const;
virtual RUVectorFunc getVectorFunction() const;
};
Your pixel function will also need to contain any parameters that it
needs to run. Instead of creating a Context Data and stashing the
parameter values there, you'll be stashing the parameter values in your
Pixel Function class. Your addPixelFunction() method should initialize
these values.
Pixel functions are divided into two groups - scalar and vector pixel
functions. A scalar pixel function does not depend on the other
components of the plane - like a gamma() function. A vector pixel
function requires the other components of the plane to be used in the
calculation - like luminance(). So first, you need to decide which type
of function your pixel function is. If it is a scalar function,
override:
bool needAllComponents() const { return false; }
Otherwise, if it is a vector function:
bool needAllComponents() const { return true; }
A scalar pixel function should override getPixelFunction() to return a
pointer to your static pixel function:
static float pixel_function(RU_PixelFunction *func, float val, int comp);
In this function, you can access your pixel function class to read the
stashed parameters, you have access to the input pixel value (val),
and you know which component you are processing (comp). You then
return the result from the function. For example,
float simpleAdd(RU_PixelFunction *func, float val, int comp)
{
cop2_AddFunction *add_parms = dynamic_cast<cop2_AddFunction *>(func);
return val + add_parms->myAddend[comp];
}
RUPixelFunction getPixelFunction() const { return cop2_AddFunction::simpleAdd; }
In addition, scalar pixel functions can also override the virtual
method eachComponentDifferent(). If false, then the components can all
be processed exactly the same. If true, then the parameters for each
component differ, and each component must be processed individually.
For example, if you were brightening the color by 2,
eachComponentDifferent() should return false. If you were adding the
color (0.7,0.5,0.6) to each color, it should return true.
If your function is a vector function, then you need to override
getVectorFunction() to return a pointer to your static vector function,
which is in the form:
static void vector_function(RU_PixelFunction *f, float **vals, const bool *scope)
This function is slightly more complicated, since up to 4 components
are passed to you at once. vals acts as both the input and the
output; it contains the input values, and you write the results back to
that array. scope is an array of 4 booleans that tells you which
components you can write the result to (if true, write the result to
the array; if false, it is not scoped so don't write it). An example:
void saturate(RU_PixelFunction *func, float **vals, const bool *scope)
{
cop2_SaturateFunc *sat_parms = dynamic_cast<cop2_SaturateFunc *>(func);
float amount = sat_parms->myAmount;
float lum_level = 0;
if(vals[0]) lum_level = *vals[0] *0.3;
if(vals[1]) lum_level += *vals[1] *0.6;
if(vals[2]) lum_level += *vals[2] *0.1;
if(scope[0] && vals[0]) *vals[0] = (*vals[0] - lum_level) * amount + lum_level;
if(scope[1] && vals[1]) *vals[1] = (*vals[1] - lum_level) * amount + lum_level;
if(scope[2] && vals[2]) *vals[2] = (*vals[2] - lum_level) * amount + lum_level;
}
RUVectorFunction getVectorFunction() const { return cop2_SaturateFunc::saturate; }
In this example, even though there are up to 4 components, we didn't
read or write to the 4th component. It will be passed through
untouched. Also note that you need to check if the component has data
(for an alpha plane, only vals[0] would be non-null).
For an example of a pixel function COP, see the COP HDK sample COP2_PixelAdd.C.
Table of Contents
Operators |
Surface Operations |
Particle Operations |
Composite Operators |
Channel Operators
Material & Texture |
Objects |
Command and Expression |
Render Output |
Mantra Shaders |
Utility Classes |
Geometry Library |
Image Library |
Clip Library
Customizing UI |
Questions & Answers
Copyright © 2007 Side Effects Software Inc.
477 Richmond Street West, Toronto, Ontario, Canada M5V 3E7