|
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:
- Write a class that implements an atmospheric shader algorithm.
- 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