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
.
The “eco-system” of each can be shown diagrammatically:
Figure 24 | Figure 25 |
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.
When a shader cannot be returned, a NULL value is returned.
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:
Figure 26
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:
Figure 27
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.