For each render item (MRenderItem), there is a 1:1 relationship with a shader. As the entire framework is based on programmable shading, an MShaderInstance is a wrapper around a hardware programmable shader. In this section it is assumed that you are already familiar with hardware Effects.
This particular interface aims to provide the most integrated solution for the usage of Effect based shaders with the rendering framework. The key properties of this interface are: simplicity, flexibility, management, and interoperability.
The closest match in the old interface is a hardware shader plug-in interface (MPxHwShaderNode). Below is a table contrasting MPxHwShaderNode versus MShaderInstance.
Characteristic |
MPxHwShaderNode interface |
MShaderInstance interface |
Interface |
A new node type needs to be created and can only act as a surface shader override. |
Does not require a DG node to be created and assigned to a surface shader. It can thus be (re)used for more than just one API interface. |
Parameters |
Custom code needs to be written as part of the plug-in. |
Uniform parameter definition and update is supported internally. Both simple and complex data types are supported and parameter binding works with all exposed API resources types such as textures and targets. |
Effects support |
Separate plug-ins have been used to support different languages with little or no commonality between them. All loading, parsing, compiling and maintenance is up to the plug-in code. |
The shaders supported are Effects file based. Effect files on disk can be read and compiled to return shader instances. Effects on disk are automatically monitored for modifications. No additional coding support is required by the plug-in. DirectX11 HLSL, OpenGL CgFX, and GLSL through the use of OGSFX files are supported. See Create and render hardware shaders and Create and visualize a GLSL shader in the Shading section of the Maya User Guide for more information. |
Semantic binding |
Custom code needs to be written as part of the plug-in. |
A set of SAS semantics are supported for automatically binding various uniform and varying parameters. Semantics supported include those required for surface shading and screen space rendering. The semantics supported between CgFx and HLSL are mostly equivalent but can differ due to shading language differences |
Internal shaders access |
This concept does not exist in this interface. |
There are a number of internally provided preset shaders available. These provide automatic integration with the rendering framework. Features such as light binding, full screen effects, and transparency require no additional work. Implementations of MPxShadingNodeOverride can access shaders built by Maya through this interface. |
Shader Management |
All shaders are managed by the internal resource manager and as such can be shared and reused. |
|
State Management |
Technique, pass and other state management is tracked internally within the rendering framework. Any optimizations are automatically provided without changes to any plug-in source code. |
The “eco-system” of each can be shown diagrammatically:
On the left, MShaderInstance is integrated as part of the framework. The MShaderManager class is shown. This is the API interface to the shader resources maintained by the internal resource management system. On the right, a possible setup for MPxHwShaderNode is shown. All “custom” boxes need to be written by the plug-in writer.
Note that we are using the term shader instance versus shader. This distinction is made as the shader provides the definition of the algorithm and the definition of input and output parameters. A shader instance defines the parameter input values or bindings for a given instance of the shader.
To acquire a shader instance, use the manager interfaces to either load in an Effects file from disk, or acquire an internally created instance. Some key properties of the manager include:
When acquiring a shader via any one of the various interfaces on MShaderManager, such as MShaderManager::getEffectsFileShader() or MShaderManager::getEffectsBufferShader(), if a shader compilation failure occurs, then errors will be output to the Output Window on Windows and Linux, and to the console on Mac. This should aid in the debugging process for shader writers.
It is possible to specify pre or post callback functions for each shader instance. This is useful, for example, to update parameters based on the current drawing context. Custom callbacks can be derived from the MShaderInstance::DrawCallback interface.
Parameters can also be set at any time, either within the callback or outside. There is a range of parameter types which are supported, including: Booleans, integers, float tuples, matrices, textures and texture samplers, and render targets. The general process for updating parameters is to query the parameter list, and then for each parameter of interest, set its value using the appropriate method on MShaderInstance.
Any semantic associated with a parameter can also be queried. In general, any parameters with system defined semantics are automatically updated. This includes uniforms such as the object-to-world matrix. For a list of semantics supported, see Shader semantics supported by Viewport 2.0.
The MShaderInstance::requiredVertexBuffers() method allows you to query a list of vertex buffer descriptors for a given shader instance (MVertexBufferDescriptorList). Subscene overrides, in particular, can make use of this method when using shader instances for custom render items, although this method can be called outside of the context of this override.
There is a natural separation between shading and rendering geometry. As such, varying parameters (geometry attributes) are not exposed as shader parameters. The render phase of the pipeline automatically handles binding of geometric data and making the draw calls for rendering.
As varying parameter binding relies upon semantics, any custom Effects must be written with the appropriate semantic tagging for the varying parameters. The following is an example vertex shader input structure which tags position and normal requirements.
struct VS_INPUT { float4 Pos : POSITION; // Position semantic to bind positional data streams float4 Norm : NORMAL; // Normal semantic to bind normal data streams };
As part of the integration of a shader instance, an instance can specify whether it draws with any transparency. This helps with the categorization of render items (MRenderItems) as they flow through the pipeline.
Revisiting the Update Phase, the main pieces can now be filled in as follows:
Instead of a “shader”, an MShaderInstance instance is referenced by an MRenderItem. The geometric attributes of the shader represented by the MShaderInstance provides set of vertex and index descriptors which specify the requirements to be used during update.
The pipeline in which the update and draw phases reside could look as follows:
In the figure above, some of the abstract constructs from the previous diagram have been replaced with constructs exposed in the API. The Update Phase produces MRenderItems (and their associated MShaderInstances). The transparency indicator on the MShaderInstance is taken into account during the consolidation and categorization phases so that the render item may be being transferred to a “transparent” render item list. During the Draw Phase, the MRenderItem is examined. The corresponding MShaderInstance sets up the shader and updates the actual hardware shader based on the parameter values of the MShaderInstance. Geometry referenced in the MGeometry will be bound and drawn. Any pre or post callback associated with the MShaderInstance would be invoked at the appropriate times.
There is one older interface which is used for fixed-function material setting. This is the MMaterial interface, which is used in conjunction with the UI DAG object interface (MPxSurfaceShapeUI). There is no connection between MShaderInstance and UI objects, as there is no mixing of programmable shaders with the fixed function legacy drawing framework.
The shader instance assigned to a render item can affect consolidation. Different instances of shaders mean that consolidation cannot occur between the associated render items.
If shader instances are used, the plug-in should keep a minimum number of unique configurations. For example, to have two colored shaders, the plug-in could keep 2 shader instances which both use the same shader definition but with different parameter values (color). Conversely, the plug-in should not keep two shaders with the same shader parameters.