This reference page is linked to from the following overview topics: Rendering Plug-ins.
One of the base classes for the creation of the Shaders, which plug-in to the Standard material[?].
This class is available in release 3.0 and later only. Note that
developers should derive their plug-in Shader from
Class Shader and not
from this class directly. If not, the interactive renderer will not
know how to render the Shader in
the viewports. Developers of this plug-in type also need to have an
understanding of how the Standard material and the Shader work
together. Every material has a Shader. The
Shader is the
piece of code which controls how light is reflected off the
surface. The Standard material is the mapping mechanism which
handles all the texturing for the material. Managing the user
interface is also done by the standard material. This way the
Shader plug-in
needs only to worry about the interaction of light on the
surface.
Prior to release 3, developers could write Material plug-ins that performed their
own shading. However, this was usually a major programming task.
Release 3 provides the simpler Shader plug-in
that would benefit from sharing all the common capabilities. The
Standard material, with its 'blind' texturing mechanism, makes this
possible. It doesn't know what it is texturing - it simply textures
'stuff'. The shader names the channels (map), fills in the initial
values, and specifies if they are a single channel (mono) or a
triple channel (color). The Standard material handles the rest
including managing the user interface. The main part of the code in
a Shader is
responsible for supplying this information to a Standard material.
The values are passed and received back in class IllumParams. There is a specific
method named Illum() in
a shader, which actually does the shading.
#include <shaders.h>
Public Member Functions |
|
RefResult | NotifyRefChanged (Interval changeInt, RefTargetHandle hTarget, PartID &partID, RefMessage message) |
Receives and responds to messages. |
|
SClass_ID | SuperClassID () |
Retrieves a constant representing the type
of the plugin. |
|
BOOL | BypassPropertyLevel () |
Wants to promote the shader properties to
the material level. |
|
virtual ULONG | GetRequirements (int subMtlNum)=0 |
Returns the requirements of the Shader for the
specified sub-material. |
|
virtual ShaderParamDlg * | CreateParamDialog (HWND hOldRollup, HWND hwMtlEdit, IMtlParams *imp, StdMat2 *theMtl, int rollupOpen, int n=0)=0 |
Put up a dialog that lets the user edit the
plug-ins parameters. |
|
virtual int | NParamDlgs () |
Returns the number of rollups this shader is
requesting. |
|
virtual ShaderParamDlg * | GetParamDlg (int n=0)=0 |
Returns a pointer to the ShaderParamDlg object which
manages the user interface. |
|
virtual void | SetParamDlg (ShaderParamDlg *newDlg, int n=0)=0 |
Sets the ShaderParamDlg object which
manages the user interface to the one passed. |
|
IOResult | Save (ISave *isave) |
Saves and loads the plug-in's name. |
|
IOResult | Load (ILoad *iload) |
Loads the plug-in's name. |
|
virtual ULONG | SupportStdParams ()=0 |
Std parameter support. |
|
virtual void | ConvertParamBlk (ParamBlockDescID *descOld, int oldCount, IParamBlock *oldPB) |
Required for R2.5 shaders to convert the
previous Standard material parameter blocks to the current version.
|
|
virtual void | GetIllumParams (ShadeContext &sc, IllumParams &ip)=0 |
Updates the "channels"(as well as other)
data member of the "IllumParams" object passed to it with the
"local" variables of the material for possible mapping prior to
being given to the Shader's Illum()
method. |
|
virtual void | Illum (ShadeContext &sc, IllumParams &ip)=0 |
This is the illumination function for the
Shader.
|
|
virtual void | ShadeReflection (ShadeContext &sc, IllumParams &ip, Color &mapClr) |
Compute the reflected color from the
sc, ip, and reflection map (or ray) color. |
|
virtual void | ShadeTransmission (ShadeContext &sc, IllumParams &ip, Color &mapClr, float amount) |
Compute the transmission/refraction color
for the sample. |
|
virtual void | AffectReflection (ShadeContext &sc, IllumParams &ip, Color &rcol) |
virtual void | CombineComponents (ShadeContext &sc, IllumParams &ip) |
Finalizes the compositing of the various
illumination components. |
|
virtual long | nTexChannelsSupported ()=0 |
Returns the number of texture map map
channels supported by this Shader.
|
|
virtual MSTR | GetTexChannelName (long nTextureChan)=0 |
Returns the name of the specified texture
map channel. |
|
virtual MSTR | GetTexChannelInternalName (long nTextureChan) |
Returns the internal name of the specified
texture map. |
|
virtual long | ChannelType (long nTextureChan)=0 |
Returns the channel type for the specified
texture map channel. |
|
virtual long | StdIDToChannel (long stdID)=0 |
Returns the index of this Shader's channels
which corresponds to the specified Standard materials texture map
ID. |
|
virtual long | nUserIllumOut () |
Indicates the number of UserIllum output
channels used by the shader. |
|
virtual MCHAR ** | UserIllumNameArray () |
Returns a static array of channel names for
matching by render elements. |
|
virtual void | Reset ()=0 |
Called when the Shader is first
activated in the dropdown list of Shader choices.
|
RefResult NotifyRefChanged | ( | Interval | changeInt, |
RefTargetHandle | hTarget, | ||
PartID & | partID, | ||
RefMessage | message | ||
) | [inline, virtual] |
Receives and responds to messages.
A plugin which makes references must implement a method to receive and respond to messages broadcast by its dependents. This is done by implementing NotifyRefChanged(). The plugin developer usually implements this method as a switch statement where each case is one of the messages the plugin needs to respond to. The Method StdNotifyRefChanged calls this, which can change the partID to new value. If it doesn't depend on the particular message& partID, it should return REF_DONTCARE.
changeInt | - This is the interval of time over which the message is active. Currently, all plug-ins will receive FOREVER for this interval. |
hTarget | - This is the handle of the reference target the message was sent by. The reference maker uses this handle to know specifically which reference target sent the message. |
partID | - This contains information specific to the message passed in. Some messages don't use the partID at all. See the section List of Reference Messages for more information about the meaning of the partID for some common messages. |
message | - The message parameters passed into this method is the specific message which needs to be handled. |
Implements ReferenceMaker.
{return REF_SUCCEED;}
SClass_ID SuperClassID | ( | ) | [inline, virtual] |
Retrieves a constant representing the type of the plugin.
Reimplemented from ReferenceTarget.
{return SHADER_CLASS_ID;}
BOOL BypassPropertyLevel | ( | ) | [inline, virtual] |
Wants to promote the shader properties to the material level.
Reimplemented from Animatable.
{ return TRUE; }
virtual ULONG GetRequirements | ( | int | subMtlNum | ) | [pure virtual] |
Returns the requirements of the Shader for the specified sub-material.
Many objects in the rendering pipeline use the requirements to tell the renderer what data needs to be available. The shader's requirements are OR'd with the combined map requirements and returned to the renderer via the Stdmtl2's GetRequirements() function.
subMtlNum | This parameter is not used. |
virtual ShaderParamDlg* CreateParamDialog | ( | HWND | hOldRollup, |
HWND | hwMtlEdit, | ||
IMtlParams * | imp, | ||
StdMat2 * | theMtl, | ||
int | rollupOpen, | ||
int | n = 0 |
||
) | [pure virtual] |
Put up a dialog that lets the user edit the plug-ins parameters.
This method creates and returns a pointer to a ShaderParamDlg object and puts up the dialog which lets the user edit the Shader's parameters.
hOldRollup | The window handle of the old rollup. If non-NULL, the IMtlParams::ReplaceRollupPage() method is usually used instead of IMtlParams::AddRollupPage() to present the rollup. |
hwMtlEdit | The window handle of the material editor. |
imp | The interface pointer for calling methods in 3ds Max. |
theMtl | Pointer to the Standard material being edited. |
rollupOpen | True to have the UI rollup open; false to have it closed. |
n | This parameter is available in release 4.0 and later only. Specifies the number of the rollup to create. Reserved for future use with multiple rollups. |
virtual int NParamDlgs | ( | ) | [inline, virtual] |
virtual ShaderParamDlg* GetParamDlg | ( | int | n = 0 |
) | [pure virtual] |
Returns a pointer to the ShaderParamDlg object which manages the user interface.
n | Specifies the rollup to get ShaderParamDlg for. Reserved for future use with multiple rollups. |
virtual void SetParamDlg | ( | ShaderParamDlg * | newDlg, |
int | n = 0 |
||
) | [pure virtual] |
Sets the ShaderParamDlg object which manages the user interface to the one passed.
newDlg | Points to the new ShaderParamDlg object. |
n | Specifies the rollup to set ShaderParamDlg for. Reserved for future use with multiple rollups. |
Saves and loads the plug-in's name.
These should be called at the start of a plug-in's Save() method.
*isave | An interface for saving data. |
Reimplemented from SpecialFX.
{ return SpecialFX::Save(isave); }
Loads the plug-in's name.
This should be called at the start of a plug-in's Load() method.
*iload | An interface for loading data. |
Reimplemented from SpecialFX.
{ return SpecialFX::Load(iload); }
virtual ULONG SupportStdParams | ( | ) | [pure virtual] |
Std parameter support.
Returns a value which indicates which of the standard parameters are supported.
virtual void ConvertParamBlk | ( | ParamBlockDescID * | descOld, |
int | oldCount, | ||
IParamBlock * | oldPB | ||
) | [inline, virtual] |
Required for R2.5 shaders to convert the previous Standard material parameter blocks to the current version.
descOld | Pointer to the old parameter block descriptor. |
oldCount | The number in the array of parameters above. |
oldPB | Pointer to the old parameter block. |
{};
virtual void GetIllumParams | ( | ShadeContext & | sc, |
IllumParams & | ip | ||
) | [pure virtual] |
Updates the "channels"(as well as other) data member of the "IllumParams" object passed to it with the "local" variables of the material for possible mapping prior to being given to the Shader's Illum() method.
The shader plug-in copies the state of all its parameters (at their current animation state) into the data members of the "IllumParams" passed.
ip | Pointer to the IllumParams to update. |
virtual void Illum | ( | ShadeContext & | sc, |
IllumParams & | ip | ||
) | [pure virtual] |
This is the illumination function for the Shader.
Developers will find it very helpful to review the Mtl::Shade() method of the Standard material. This is the main method of the material which computes the color of the point being rendered. This code is available in /MAXSDK/SAMPLES/MATERIALS/STDMTL2.CPP. This code shows how the Standard calls Shader::GetIllumParams(), sets up mapping channels, calls this Illum() method, and calls the Shader::CombineComponents() method when all done.
sc | The ShadeContext which provides information on the pixel being shaded. |
ip | The object whose data members provide communication between 3ds Max and the shader. Input values are read from here and output values are also stored here. Note that the "XOut" (ambIllumout, etc) data members of this class are initialized to 0 before the first call to this method. The input to this method has the textured illumination parameters, the bump perturbed normal, the view vector, the raw (unattenuated) colors in the reflection and refraction directions, etc. |
void Blinn2::Illum(ShadeContext &sc, IllumParams &ip) { LightDesc *l; Color lightCol; // Blinn style phong BOOL is_shiny= (ip.channels[ID_SS].r > 0.0f) ? 1:0; double phExp = pow(2.0, ip.channels[ID_SH].r * 10.0) * 4.0; for (int i=0; i<sc.nLights; i++) { l = sc.Light(i); register float NL, diffCoef; Point3 L; if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) { if (l->ambientOnly) { ip.ambIllumOut += lightCol; continue; } if (NL<=0.0f) continue; // diffuse if (l->affectDiffuse) ip.diffIllumOut += diffCoef * lightCol; // specular (Phong2) if (is_shiny&&l->affectSpecular) { Point3 H = Normalize(L-ip.V); float c = DotProd(ip.N,H); if (c>0.0f) { if (softThresh!=0.0 && diffCoef<softThresh) { c *= Soften(diffCoef/softThresh); } c = (float)pow((double)c, phExp); ip.specIllumOut += c * ip.channels[ID_SS].r * lightCol; } } } } // Apply mono self illumination if ( ! selfIllumClrOn ) { float si = ip.channels[ID_SI].r; ip.diffIllumOut = (si>=1.0f) ? Color(1.0f,1.0f,1.0f):ip.diffIllumOut * (1.0f-si) + si; } else { // colored self illum, ip.selfIllumOut += ip.channels[ID_SI]; } // now we can multiply by the clrs, ip.ambIllumOut *= ip.channels[ID_AM]; ip.diffIllumOut *= ip.channels[ID_DI]; ip.specIllumOut *= ip.channels[ID_SP]; // the following is applicable only in R4 int chan = ip.stdIDToChannel[ ID_RL ]; ShadeTransmission(sc, ip, ip.channels[chan], ip.refractAmt); chan = ip.stdIDToChannel[ ID_RR ]; ShadeReflection( sc, ip, ip.channels[chan] ); CombineComponents( sc, ip ); }
Some of the key parts of this method are discussed below:
The is_shiny line sets a boolean based on if the Shader has a
shininess setting > 0. Note that the Blinn shader uses the same
channel ordering as the original Standard material so it can index
its channels using the standard ID ID_SS.
BOOL is_shiny= (ip.channels[ID_SS].r > 0.0f) ? 1:0;
Next the 'Phong Exponent' is computed. This is just a function that
is used to give a certain look. It uses 2^(Shinniness *10) * 4.
This calculation simply 'feels right' and gives a good look.
double phExp = pow(2.0, ip.channels[ID_SH].r * 10.0) *
4.0;
The following loop sums up the effect of each light on this point
on surface.
for (int i=0; i<sc.nLights; i++) {
Inside the loop, the light descriptor is grabbed from the ShadeContext:
l = sc.Light(i);
The LightDesc::Illuminate()
method is then called to compute some data:
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef))
{
To Illuminate() is passed the ShadeContext (sc), and the
normal to the surface (ip.N) (pointing away from the surface
point).
The method returns the light color (lightCol), light vector
(L) (which points from the surface point to the light), the
dot product of N and L (NL) and the diffuse coefficient
(diffCoef). The diffuse coefficient is similar to NL but has
the atmosphere between the light and surface point taken into
account as well.
The next piece of code checks if the light figures into the
computations:
if (NL<=0.0f)
continue;
If NL<0 then the cosine of the vectors is greater than 90
degrees. This means the light is looking at the back of the surface
and is therefore not to be considered.
The next statement checks if the light affect the diffuse channel
(lights may toggle on or off their ability to affect the diffuse
and specular channels.)
if (l->affectDiffuse)
ip.diffIllumOut += diffCoef * lightCol;
If the light affects the diffuse channel then the diffuse
illumination output component of the IllumParams is added to. This is
done by multiplying the diffuse coefficient (returned by
LightDesc::Illuminate())
times the light color (also returned by LightDesc::Illuminate()).
Notice that the diffIllumOut is being accumulated with each
pass of the light loop.
The next section of code involves the specular component. If the
light is shiny, and it affects the specular channel a vector
H is computed.
if (is_shiny&&l->affectSpecular) {
Note the following about this H vector computation. Most
vectors are considered pointing from the point on the
surface. The view vector (IllumParams::V) does not follow
this convention. It points from the 'eye' towards the surface. Thus
it's reversed from the usual convention.
H is computed to be the average of L and V.
This is (L+V)/2. Since we normalize this we don't have to
divide by the 2. So, if V followed the standard convention
this would be simply L+V. Since it doesn't it is
L+(-ip.V) or L-ip.V.
Point3 H =
Normalize(L-ip.V);
Next the dot product of N and H is computed and
stored in c. When you take the dot product of two normalized
vectors what is returned is the cosine of the angle between the
vectors.
float c = DotProd(ip.N,H);
If c>0 and the diffuse coefficient is less than the soften
threshold then c is modified by a 'softening' curve.
if (c>0.0f) { if (softThresh!=0.0 && diffCoef<softThresh) { c *= Soften(diffCoef/softThresh); } }
Note that the Soften() function is defined in /MAXSDK/SAMPLES/MATERIALS/SHADERUTIL.CPP and developers can copy this code.
c = (float)pow((double)c,phExp);
Next, c is raised to the power of the Phong exponent.
This is effectively taking a cosine (a smooth S curve) and raising
it to a power. As it is raised to a power it becomes a sharper and
sharper S curve. This is where the shape of the highlight curve in
the Materials Editor UI comes from.
That completes the pre computations for the specular function. Then
c is multiplied by the specular strength
(ip.channels[ID_SS].r) times the light color
(lightCol). The result is summed in specular illumination
out (ip.specIllumOut).
ip.specIllumOut += c * ip.channels[ID_SS].r * lightCol;
That completes the light loop. It happens over and over for each
light.
Next the self illumunation is computed. If the Self Illumination
Color is not on, then the
original code for doing mono self illumination is used.
// Apply mono self illumination if ( ! selfIllumClrOn ) { float si = ip.channels[ID_SI].r; ip.diffIllumOut = (si>=1.0f) ? Color(1.0f,1.0f,1.0f) : ip.diffIllumOut * (1.0f-si) + si; } else { // Otherwise the self illumination color is summed in to the Self Illumination Out (ip.selfIllumOut). // colored self illum, ip.selfIllumOut += ip.channels[ID_SI]; }
Then, we multiply by the colors for ambient, diffuse and
specular.
ip.ambIllumOut *= ip.channels[ID_AM]; ip.diffIllumOut *= ip.channels[ID_DI]; ip.specIllumOut *= ip.channels[ID_SP];
The results are ambIllumOut, diffIllumOut, and
specIllumOut. Note that these components are not summed. In
R3 and earlier these results would be returned to the Standard
material. However, R4 introduces a couple extra steps.
Finally, we call ShadeTransmission()
and
ShadeReflection() to apply the transmission/refraction and
reflection models. The core implementation of 3ds Max provides the
standard models, but the shader can override these methods to
compute its own models.
int chan = ip.stdIDToChannel[ ID_RL ]; ShadeTransmission(sc, ip, ip.channels[chan],ip.refractAmt); chan = ip.stdIDToChannel[ ID_RR ]; ShadeReflection( sc, ip, ip.channels[chan] );
In R4, It is a shader's responsibility to combine the components
of the shading process prior to exiting Illum()
(in R3, this was the responsibility of the Standard material). In
order to combine these values together to produce the final color
for that point on the surface (IllumParams.finalC),
the shader should call
CombineComponents() method. The Shader base
class provides a default implementation which simply sums
everything together and multiplies by the opacity.
virtual void CombineComponents( IllumParams& ip ) { ip.finalC = ip.finalOpac * (ip.ambIllumOut + ip.diffIllumOut + ip.selfIllumOut) + ip.specIllumOut + ip.reflIllumOut + ip.transIllumOut ; }
virtual void ShadeReflection | ( | ShadeContext & | sc, |
IllumParams & | ip, | ||
Color & | mapClr | ||
) | [inline, virtual] |
Compute the reflected color from the sc, ip, and reflection map (or ray) color.
The core implementation of this provides the standard 3ds Max reflection model. To support the standard reflection model, a shader may call this default implementation.
sc | The context which provides information on the pixel being shaded. |
ip | The object whose data members provide communication between 3ds Max and the shader. |
mapClr | The input reflection (or ray) color is passed in here and the resulting 'affected' color is stored here. |
Reimplemented in Shader.
{}
virtual void ShadeTransmission | ( | ShadeContext & | sc, |
IllumParams & | ip, | ||
Color & | mapClr, | ||
float | amount | ||
) | [inline, virtual] |
Compute the transmission/refraction color for the sample.
The core implementation of this provides the standard 3ds Max reflection model. To support the standard transmission/refraction model, a shader may call this default implementation.
sc | The context which provides information on the pixel being shaded. |
ip | The object whose data members provide communication between 3ds Max and the shader. |
mapClr | The input refraction (or ray) color is passed in here and the resulting 'affected' color is stored here. |
amount | The level of the amount spinner for the refraction channel. |
Reimplemented in Shader.
{}
virtual void AffectReflection | ( | ShadeContext & | sc, |
IllumParams & | ip, | ||
Color & | rcol | ||
) | [inline, virtual] |
This method provides the shader with an opportunity to affect the reflection code.
sc | The ShadeContext which provides information on the pixel being shaded. |
ip | The object whose data members provide communication between 3ds Max and the shader. |
rcol | The input reflection color is passed in here, and the resulting 'affected' color is stored here too. |
void AffectReflection(ShadeContext &sc, IllumParams &ip, Color &rcol) { rcol *= ip.channels[ID_SP]; };
#define DEFAULT_GLOSS2 0.03f void MultiLayerShader::AffectReflection(ShadeContext &sc, IllumParams &ip, Color &rcol) { float axy = DEFAULT_GLOSS2; float norm = 1.0f / (4.0f * PI * axy ); rcol *= ip.channels[_SPECLEV1].r * ip.channels[_SPECCLR1] * norm; }
{}
virtual void CombineComponents | ( | ShadeContext & | sc, |
IllumParams & | ip | ||
) | [inline, virtual] |
Finalizes the compositing of the various illumination components.
A default implementation is provided which simply adds the components together. Developers who want to do other more specialized composition can override this method. For example, a certain Shader might want to composited highlights over the underlying diffuse component since the light is reflected and the diffuse color wouldn't fully show through. Such a Shader would provide its own version of this method.
sc | The ShadeContext which provides information on the pixel being shaded. |
ip | The illumination parameters to composite and store. |
{};
virtual long nTexChannelsSupported | ( | ) | [pure virtual] |
Returns the number of texture map map channels supported by this Shader.
virtual MSTR GetTexChannelName | ( | long | nTextureChan | ) | [pure virtual] |
Returns the name of the specified texture map channel.
nTextureChan | The zero based index of the texture map channel whose name is returned. |
virtual MSTR GetTexChannelInternalName | ( | long | nTextureChan | ) | [inline, virtual] |
Returns the internal name of the specified texture map.
The Standard material uses this to get the fixed, parsable internal name for each texture channel it defines.
nTextureChan | The zero based index of the texture map whose name is returned. |
{ return GetTexChannelName(nTextureChan); }
virtual long ChannelType | ( | long | nTextureChan | ) | [pure virtual] |
Returns the channel type for the specified texture map channel.
There are four channels which are part of the Material which are not specific to the Shader. All other channels are defined by the Shader (what they are and what they are called.) The four which are not the province of the Shader are Bump, Reflection, Refraction and Displacement. For example, Displacement mapping is really a geometry operation and not a shading one. The channel type returned from this method indicates if the specified channel is one of these, or if it is a monochrome channel, a color channel, or is not a supported channel.
nTextureChan | The zero based index of the texture map whose name is returned. |
virtual long StdIDToChannel | ( | long | stdID | ) | [pure virtual] |
Returns the index of this Shader's channels which corresponds to the specified Standard materials texture map ID.
This allows the Shader to arrange its channels in any order it wants in the IllumParams::channels array but enables the Standard material to access specific ones it needs (for instance the Bump channel or Reflection channel).
stdID | The ID whose corresponding channel to return. |
static int stdIDToChannel[N_ID_CHANNELS] = { 0, 1, 2, 5, 4, -1, 7, 8, 9, 10, 11, 12 }; long StdIDToChannel(long stdID){ return stdIDToChannel[stdID]; }
virtual long nUserIllumOut | ( | ) | [inline, virtual] |
Indicates the number of UserIllum output channels used by the shader.
{ return 0; }
virtual MCHAR** UserIllumNameArray | ( | ) | [inline, virtual] |
Returns a static array of channel names for matching by render elements.
{ return NULL; }