[SESI logo]

Houdini Development Toolkit - Version 6.5

Side Effects Software Inc. 2004

Mantra

Adding an Atmosphere Shader

To add a custom atmospheric shader, there are two steps to perform:

  1. Write a class that implements an atmospheric shader algorithm.

  2. Modify the Houdini interface to include the new shader.


Writing the atmospheric shader class

Houdini's rendering library is called the RAY library. Most of the functions and classes will contain this prefix. In the RAY library, atmospheric shaders are referred to as fog shaders.

The easiest way to write a new fog shader is to take an existing shader and modify it to implement a different algorithm. An example is provided here that can be copied and modified as necessary. Although it is named RAY_FogCustom, it actually shows how the uniform fog shader was implemented in Houdini.

The uniform shader takes as parameters a color and a density, both of which are optional. The color is specified with the -c red green blue option while the density is given with the -a alpha option.

When compiled, the source file will produce a DSO which should be accessible by Houdini.

The header file RAY_FogCustom.h:

#ifndef __RAY_FogCustom_h__ #define __RAY_FogCustom_h__ #include <RAY/RAY_Fog.h> class RAY_FogCustom : public RAY_Fog { public: RAY_FogCustom(CMD_Args &args); virtual ~RAY_FogCustom(); virtual int getCoverage(const UT_Vector3 &in_point, const UT_Vector3 &out_point, float distance, UT_Vector4 &color); virtual int initData(); static const char *options; static RAY_Fog *build(CMD_Args &args); protected: float r, g, b; float density; }; #endif All fog shaders are subclassed from the RAY_Fog class. This class provides the services that interface the shader with the rest of the RAY library. Most of the code here is necessary and fills in functions that are required by the RAY_Fog class.

Note that there is some data in the protected part of the class. This is data that is specific to the shader. The uniform shader has a color and density so this data is kept here. Any shader specific data should be placed here.

The source file RAY_FogCustom.C:

#include <CMD/CMD_Args.h> #include <RAY/RAY_FogTable.h> #include "RAY_FogCustom.h" void RAYaddFogShader(RAY_FogTable* fogTable) { RAY_FogTable* fogTable = (RAY_FogTable*) data; fogTable->addShaderType("custom", RAY_FogCustom::options, RAY_FogCustom::build); } The list of fog shaders available is stored in a class called RAY_FogTable. The DSO function RAYaddFogShader() provides access to this RAY_FogTable.

Shaders are added to the RAY_FogTable class with the method, addShaderType(). The method looks like this:

void addShaderType (const char *name, 
                    const char *shader_options,
                    RAY_Fog *(*build)(CMD_Args &args));
The first parameter is the fog shader's name. It is used as an identifier to look up the shader in the table. The names of the shaders must be unique, so make sure the custom name doesn't conflict with the shaders that come with Houdini. The second option is a list of options that the shader recognizes. Arguments are passed into the shader with switches similar to command line arguments. The format of the shader_options string will indicate which switches to use and how many parameters are associated with each option. Finally, the third parameter is a function that tells the table how to build the shader. Usually, one passes in the build() method of the fog shader class for this parameter.

RAY_FogCustom::RAY_FogCustom(CMD_Args &args) { r = g = b = 0; density = 0; if (args.found('c')) { r = atof(args.argp('c', 0)); g = atof(args.argp('c', 1)); b = atof(args.argp('c', 2)); } if (args.found('a')) density = atof(args.argp('a')); } The constructor for the shader is used to initialize shader specific data. Arguments are passed in using the CMD_Args class. This class handles the retrieval of parameters that were passed into the shader.

The uniform shader needs to initialize the color from the -c flag and the density from the -a flag. The constructor also has defaults for these variables in case neither option is specified.

RAY_FogCustom::~RAY_FogCustom() { } The destructor should clean up anything that was created by the shader that is no longer necessary.

The uniform shader didn't create anything so it has nothing to clean up.

int RAY_FogCustom::initData() { if (density <= 0.0F) return 0; return 1; } The initData() method is used to verify arguments. If some of the passed in parameters are invalid or the shader is unable to continue for any other reason, this function should return 0. If everything is alright, the function should return 1.

The uniform shader verifies that the passed in density value is not negative.

int RAY_FogCustom::getCoverage(const UT_Vector3 &in_point, const UT_Vector3 &out_point, float distance, UT_Vector4 &color) { float t; t = distance * density; if (t <= 0.0001F) return 0; if (t > 1.0F) t = 1.0F; color.assign(r, g, b, 1); color *= t; return 1; } The getCoverage() method is where the actual shading takes place. It takes as input an in point, an out point and the euclidean distance between the two points and the function is expected to shade the ray and return this as a color.

The in_point and out_point are points in world space represented as UT_Vector3s. The in point represents where the ray entered the atmosphere and is typically the viewer position. Rays can also be reflected in which case the in point represents the surface point where the ray is being reflected from. The out point is a point on a surface that the ray has hit. Although the distance between the points can be computed, it is also given.

The in point and out point are given in world space. The fog is an object that can be transformed and it is not necessarily positioned at the origin nor is it necessarily oriented as the world space. The points can be transformed into local fog object space by using the toLocal() function:

UT_Vector3 in_local, out_local; toLocal(in_point, in_local); toLocal(out_point, out_local); The shaded color is assigned into the color variable which is a UT_Vector4. The color requires RGB components as well as alpha. The components are values in the range [0-1].

If the shading is successful, the function should return 1, otherwise it should return 0.

How the function assigns values to color defines the behaviour of the shader. Recall that the uniform shader stores with it a color and a density. To shade the ray, the uniform shader uses the color, scaled by the density and the length of the ray.

const char * RAY_FogCustom::options = "c:::a:"; This string defines which options the shader recognizes. It is used by the CMD_Args class.

The uniform shader uses the -c flag which requires 3 parameters. It also uses the -a flag which requires 1 parameter.

RAY_Fog * RAY_FogCustom::build(CMD_Args &args) { return new RAY_FogCustom(args); } This function should just return a new instance of the shader.


Modifying the Houdini interface

Fog shaders are chosen from within Houdini using a user dialog which is built from a script. The new custom fog shader must now be added to this dialog script. The dialog script containing the list of fog shaders is found in hfs/houdini/config/Scripts/MANfog.ds. To add the new shader, just copy a simple entry like that for the uniform shader, and modify it as required. It may be a good idea to backup the original file before making changes to it.

The command name should match the shader name used with addShaderType().

Here is what the entry for the uniform shader looks like:

command { name uniform label "Uniform Fog" default "uniform -c 1 1 1 -a .01" parm { name color label "Fog Color" option -c type float size 3 default { 1 1 1 } } parm { name alpha label "Fog Density" option -a type float default { .01 } } }
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 © 2004 Side Effects Software Inc.
477 Richmond Street West, Toronto, Ontario, Canada M5V 3E7