Color management support for plug-in shaders in Viewport 2.0

This topic describes how to access the existing color management properties on a file texture node, and use these as inputs to a custom shader, which can render the texture associated with the file texture node.

NOTE:The contents of this topic can be applied to Maya 2016 Extension 2, with the exception that the following fixes are not available:

Obtaining a shader instance with color management support

Assume that a plug-in node P creates a textured mode render item (MRenderItem). This item may be created either for a geometry or subscene override (MPxGeometryOverride or MPxSubSceneOverride). The render item's shader instance will provide the support for exposure and input color transform properties.

By default, a shader obtained via MShaderManager does not support a color transform, as the transform is dynamically created based on the color management settings in the user preferences (Preferences window), and on the file texture node.

To workaround this, first create a shader instance without color management support, then add in support dynamically. For convenience, the fragment mayaTextureShader has been made available as a starting fragment. This fragment can be used as part of a fragment graph to create a new shader instance. Alternatively, a new stock shader using just this fragment can be obtained by specifying MShaderManager::k3dSolidTextureShader as the desired shader.

The shader instance without color management support can then be used to return a new shader instance with color transform support, by calling the following method:

MShaderInstance:createShaderInstanceWithColorManagementFragment(<colorSpace>) 

The requirement for using this method is to specify an input color space: <colorSpace>.

Obtaining an input color space specification

In order to determine the current input color space, a query of the file texture node’s attributes can be performed. The attributes of interest are working space (“workingSpace”) and input color space (“colorSpace”).

As the color space is relative to the working space, both values should be queried to determine when to create a new shader instance. The string value for the color space attribute can then be used with the MShaderInstance:createShaderInstanceWithColorManagementFragment() method indicated above.

It is possible that color management is disabled. To test for this state, the value for the attribute “colorManagementEnabled” should be queried on the file texture node.

The following is a sample snippet of code, which assumes that a file texture is connected to the plug-in node P via its “color” attribute.

MObject connectedObject = MObject::kNullObj;
// Get connections to “color” attribute on node of interest
//
MPlug plug = P.findPlug("color");
if (!texture)
{
   if (!plug.isNull())
   {
      MPlugArray plugArray;
      if (plug.connectedTo(plugArray, true, false) && plugArray.length() == 1)
      {
         // Check for a file texture node connection
         //
         const MPlug& connectedPlug = plugArray[0];
         connectedObject = connectedPlug.node();
         if (connectedObject.hasFn( MFn::kFileTexture))
         {
            // Get the name of the file texture image and acquire a texture to use
            MString fileTextureName;
            MRenderUtil::exactFileTextureName(connectedObject, fileTextureName);
            if (fileTextureName.length())
            {
               texture = textureManager->acquireTexture(fileTextureName);
            }

            // Check if color management is even enabled
            MFnDependencyNode fileNode(connectedObject);
            MPlug cmEnabledPlug = fileNode.findPlug("colorManagementEnabled");
            bool cmEnabled = false;
            if (!cmEnabledPlug.isNull())
            {
               MString workingColorSpace;
               MString colorSpace;

               cmEnabled = true;
               cmEnabledPlug.getValue(cmEnabled);
               if (cmEnabled)
               {
                  // Get working space and input color space values.
                  // 
                  MPlug workingColorSpacePlug = fileNode.findPlug("workingSpace");
                  workingColorSpacePlug.getValue(workingColorSpace);

                  MPlug colorSpacePlug = fileNode.findPlug("colorSpace");
                  colorSpacePlug.getValue(colorSpace);

                  // If working or input color space changes then 
                  // we need to create a new shader. Previous shader resource
                  // will be released when a new one is created.
                  //
                  // Note that is the shader initially
                  // created which does not have a CM fragment.
                  // 
                  // m_texturedColorManagedShader
                  if (!m_texturedColorManagedShader || 
                        workingColorSpace != m_workingColorSpace ||
                        colorSpace != m_inputColorSpace)
                  {
                     m_texturedColorManagedShader = 
                        m_texturedShader->createShaderInstanceWithColorManagementFragment(colorSpace);
                     if (!m_texturedColorManagedShader)
                        cmEnabled = false;

                     m_workingColorSpace = workingColorSpace;
                     m_inputColorSpace = colorSpace;
                  }
               }
            }
         }
      }
   }
}

Obtaining Exposure

The “exposure” attribute on a file texture node can be obtained and used to set the “exposure” parameter on the previously mentioned internal shader instances, or a custom one used by the plug-in.

Note that exposure can be set regardless of whether color management has been enabled globally or on the file node.