Advanced Material and Texture Map Display API
Materials and texture maps can use MetaSL programmable shaders to describe their appearance when displayed in the Nitrous viewport, or when rendered with the Quicksilver renderer. Using MetaSL shaders allows for a more realistic and complex visual appearance than when ISimpleMaterial is used to represent materials and texture maps. Note that 3ds Max 2012 only supports MetaSL shaders. There are three steps involved in adding MetaSL shader support to a material or texture map plug-in:
The materials and texture maps often have submaps (slots) which can be either other materials or texture maps. Furthermore, the user can feed a certain material or texture map parameter such as the diffuse color with the output color of another texture map. Thus several material and texture map plug-ins, each with underlying MetaSL shaders, are effectively connected into a shader graph.
Figure 1 - 3ds Max material and texture map graph (left) and its corresponding MetaSL shader graph (right)
Note that 3ds Max 2012 supports only shaders written in the MetaSL shading language.
The full details of writing a MetaSL shader is beyond the scope of this document, but mental mill can be downloaded from the Autodesk 3ds Max Services and Support web site. You will need a 3ds Max 2011 or 3ds Max 2012 license to run it.
Once the shader is written, the msl and xmsl files need to be installed into the appropriate directories inside <3dsMax_Root>\mentalimages\shaders_3rdparty\metaSL.
3ds Max 2012 optimizes the MetaSL code used by materials and texture maps by compiling the MetaSL shader graph into a DirectX compatible format such as HLSL. To produce efficient GPU code 3ds Max supports the MetaSL "const" keyword. This allows the shader graph compiler to inline usages of const parameters and greatly reduce the number of instructions to execute. The drawback is that the shader needs to be recompiled whenever the parameter declared as const changes. Currently the system supports only const int and const bool parameters.
A material or texture map with shaders needs to create an IShaderManager instance for each of their shaders. 3ds Max 2012 creates the shader graph based on the IShaderManager instances created by the plug-ins.
IShaderManagerCreator::CreateShaderManager() needs to be used by the plug-in to create a IShaderManager instance for each of the shaders it supports. 3ds Max will query the plug-in for its IShaderManager via a request for the interface identified by ISHADER_MANAGER_INTERFACE_ID. Typically a plug-in would create an instance of IShaderManager in response to this request in its override of Animatable::GetInterface(Interface_ID) and delete it in its destructor by calling IShaderManagerCreator::DeleteShaderManager().
Note that the IShaderManager instances are immutable objects. If the plug-in needs to use a new shader it will have to create a new IShaderManager instance for it.
See maxsdk\include\graphics\ishadermanager.h for a code snippet illustrating how a plug-in would typically manage the lieftime of an IShaderManager instance.
Material and texture map parameter values need to be translated to the underlying shader parameters whenever the plug-in's parameters change. 3ds Max 2012 handles this translation automatically for ParamBlock2 based material and texture map parameters, if their names and types match those of the shader. Plug-ins that need control over the translation process can implement the IParameterTranslator interface.
IParameterTranslator::GetParameterValue() allows the plug-in to provide a shader parameter, specified by name and type, with a value returned by the method. This method is called for every shader parameter found when the system loaded the MetaSL shader. See maxsdk\include\graphics\iparametertranslator.h for a code snippet illustrating how a plug-in would typically implement this method.
IParameterTranslator::GetShaderInputParameterName() allows the plug-in to assist 3ds Max 2012 in creating the shader graph that corresponds to the material and texture map graph of 3ds Max by specifying the input shader parameter name to which the shader representing a submap of the plug-in should be connected. The following code sample illustrates how this method would be implemented to assist in setting up the shader graph in Figure 1 above.
bool StandardMaterial::GetShaderInputParameterName( SubMtlBaseType type, int subMtlBaseIndex, MSTR& shaderInputParamName ) { DbgAssert(type == MaxSDK::Graphics::IParameterTranslator::SubTexmap); if (MaxSDK::Graphics::IParameterTranslator::SubTexmap == type && 1 == subMtlBaseIndex) // 1 is the diffuse map slot index { // Diffuse_Color is the name of this material's shader parameter // we allow the shader of the diffuse map to be connected to shaderInputParamName = “Diffuse_Color”; return true; } return false; }
The Nitrous viewport display system and the Quicksilver renderer optimize the display lists to work with transparency optimally. A new method called MtlBase::GetTransparencyHint() is used to get a boolean value that indicates whether transparency is supported at a certain time (a transparency "hint") and to provide the validity interval for it. The validity of this hint will be checked by 3ds Max in each frame. Once it becomes invalid, MtlBase::GetTransparencyHint() is called again. By default, this method returns TRUE. If the material does not support transparency in any form, then the plug-in's override of this method should return FALSE. This is particularly important for custom materials that use shading languages. Only the shader or the host material will know what is happening internally and whether transparency is affected. Supporting this method will help optimize their drawing and rendering.