Implementing a hardware shading node plug-in

Integration with other parts of Maya

You can integrate the hardware shader node plug-in with other parts of Maya using the MPxHwShaderNode class.

Software rendering and the Shader Swatch

You can implement a hardware shader node plug-in in a way that produces a reasonable output for software rendering and the Shader Swatch in the Attribute Editor. The basis of the functionality is implementing the compute() method to affect the outColor attribute. If this is done, the software rendering and Shader Swatches will no longer be default black.

Alternatively, a hardware shader node plug-in can also choose to implement a specific virtual for drawing just the Shader Swatch. This method is:

virtual MStatus renderSwatchImage( MImage & image );

The following swatch classification string is required by this functionality:

const MString UserClassify( "shader/surface/utility/:swatch/"+swatchName );

This string is passed to Maya when the plug-in calls MFnPlugin::registerNode().

The UV Texture Editor

There are a number of methods in MPxHwShaderNode that help to integrate hardware shader node plug-ins into the UV Texture Editor. These methods are:

virtual MStatus getAvailableImages( const MString& uvSetName, MStringArray& imageNames);
virtual MStatus renderImage( const MString& imageName, const float region[2][2], int& imageWidth, int& imageHeight);

The first method getAvailableImages() is used to provide a list of UV channels that will be made available in the UV Texture Editor. The second method renderImage() is invoked when a channel is selected in the UV Texture Editor. It is then up to this routine to draw the image as requested.

If a hardware shader node plug-in uses standard Maya textures, then there is no need to implement these virtuals. The standard Maya textures will available in the Texture Editor.

Icons for the Hypershade window

You can specify an icon for a hardware shader plug-in node that will then be displayed in the Hypershade window. If no icon is specified, then a default blank icon is shown. To specify an icon for a hardware shader plug-in node, do the following:

Drag and Drop Behavior

You can implement drag and drop behavior on a hardware shade node using the MPxDragAndDropBehavior class. The MPxDragAndDropBehavior class provides a number of virtuals that are implemented for supporting the drag and dropping of nodes in the Hypershade menu. Consult the hwPhongShaderBehavior.cpp example for how to implement drag and drop behavior.

Shading in the viewport

A connection from a hardware shader plug-in node can be made to the hardwareShader attribute which exists on nodes that derive from an internal Lambert shaders. This allows the node to only affect Maya’s hardware rendering. For a Viewport 2.0 integration, a shader fragment for a given plug-in node can instead be written.

For more information about the hardwareShader attribute, see the Technical Documentation section, Nodes section, lambert node documentation.

Reference information for Legacy Default Viewport

The following sections contain reference information for hardware shaders that render in the Legacy Default Viewport. The current recommendation is to create shaders for use with Viewport 2.0. See Maya Viewport 2.0 API Guide.

There are two interfaces in MPxHwShaderNode that can be implemented for renderer’s hardware shading node plug-ins. Although the two interfaces are similar when comparing parameters, the ways they are called are quite different.

The bind/geometry/unbind interface

Implementing the bind(), geometry() and unbind() methods allows a hardware shading node plug-in to render in the Maya scene view. The purpose of the three methods are:

  • bind is used to setup OpenGL state and resources.
  • geometry is used to render the shader effect.
  • unbind is used to restore OpenGL state and clean up resources.

Only Mesh shapes are supported in this mode and the following steps are used to draw:

  • Maya visits each Directed Acyclic Graph (DAG) node collecting draw requests.
  • Draw requests are sorted into opaque and transparent queues.
  • Maya performs the opaque draws without sorting.
  • Maya depth sorts the transparent queue and performs the transparent draws.

The response to Maya’s requests for draw information is the following:

  • Mesh shape receives the draw request.
  • Shape checks to see if the material is a hardware shader.
  • If it is a hardware shader node plug-in, the geometry is bundled and passed to the virtuals of the node.
  • The hardware shader node plug-in uses the information passed to it to decide how to render to the screen.

The order in which the interface virtuals are called is the following:

for every shape

The result of this calling pattern is that the OpenGL state can be set in the bind() method, used in the geometry() call and then restored in unbind().

A M3dView parameter is passed to the methods of this interface. This parameter must be used to set the graphics context that will be used for drawing. Bracketing the functionality of the virtuals in this interface with beginGL() and endGL() method calls is required. For example:

MStatus hwShader::bind( const MDrawRequest& request, M3dView& view )
    // Operations ...

Failing to call these methods can result in program corruption.

The glBind/glGeometry/glUnbind interface

Implementing the glBind(), glGeometry() and glUnbind() methods allows a hardware shading node plug-in to render in the Maya scene view. The purpose of the three methods are:

  • glBind is used to set up plug-in state and resources.
  • glGeometry is used to set OpenGL state, render the shader effect and restore OpenGL state.
  • glUnbind is used to restore plug-in state and resources.

In this interface, the order that the virtuals are called is the following:

for every pass // a refresh may have more than one pass
    for every object
        if glBind() has not been called for this refresh
for every object

There are several implications that must be considered because of the change in calling order. With this interface, the OpenGL state must now be set in glGeometry() rather than glBind(). Now glBind() will only be used for loading resources and processing any required attributes that will be used in glGeometry().

Unlike the previous interface, graphics context is already set and no calls to beginGL() or endGL() are required.

Controlling what information is passed to geometry/glGeometry

The geometry() and glGeometry() methods have very large parameter lists including a mixture of mandatory and optional data that describes the vertices and faces the shader needs to render the current geometry. The following methods control which optional data should be passed to the shader:

virtual int normalsPerVertex();
virtual int colorsPerVertex();
virtual int getColorSetNames(MStringArray& names);
virtual int texCoordsPerVertex();
virtual int getTexCoordSetNames(MStringArray& names);
virtual bool provideVertexIDs();

It is important to note that requesting information that is not available will cause NULLs to be passed to the shader. Hardware shader plug-in nodes should test parameter information against NULL to ensure that data is valid.

Transparency and hardware shader plug-in nodes

Maya depth sorts draw information when transparency is present in a shader. If the hardware shader plug-in node does not support transparency, then the following virtual can be implemented to return false in order to gain shading performance.

virtual bool 	hasTransparency();

Indexing and Sparse Arrays with the geometry() and glGeometry() methods

Data is passed to these methods using an indexing mechanism. These methods are:

virtual MStatus geometry( const MDrawRequest& request, M3dView& view, int prim, unsigned int writable, int indexCount, const unsigned int * indexArray, int vertexCount, const int * vertexIDs, const float * vertexArray, int normalCount, const float ** normalArrays, int colorCount, const float ** colorArrays, int texCoordCount, const float ** texCoordArrays);
virtual MStatus glGeometry( const MDagPath& shapePath, int glPrim, unsigned int writeMask, int indexCount, const unsigned int* indexArray, int vertexCount, const int * vertexIDs, const float * vertexArray, int normalCount, const float ** normalArrays, int colorCount, const float ** colorArrays, int texCoordCount, const float ** texCoordArrays);

The parameter used for indexing is indexCount, which contains the length of the array indexArray. The indices contained within indexArray are used to access corresponding elements in the other arrays such as vertex positions, colors and normals.

There are several parameters passed to the geometry methods which are arrays of arrays. These are the pointer to a pointer parameters. It is possible for these arrays to be sparse. The sparseness can occur if one of the information requesting methods asks for more information than actually exists. For example, the getTexCoordSetNames() method can request 3 UV sets, but only the first and third exist. In this situation, the second position with the array of arrays will be NULL. As a result, it is important to use NULL checks to ensure the data is valid.

Blind Data

Using baked vertex mesh blind data for the hardware shader node plug-in can be a very useful technique for rendering. This is possible for both versions of the geometry methods. The hardware shader node plug-in must implement the provideVertexIDs() to return true. When this is done, the vertexIDs array will contain the requested information and vertexCount will contain the length of the array. If using a MDrawRequest, the mesh can be accessed using:


In the case of the gl version of the interface, the mesh is available from the shapePath parameter. Using the shape and the vertex IDs together lets you access vertex blind data through the MFnMesh class.