Hi Cheng Xi Li,
Thank you for the reply.
Please find the sample attached.
I have used existing vp2 blinn shader from sample and added code to it. For workflow refer .gif file
The cpp code is as below:
#include <maya/MIOStream.h>
#include <math.h>
#include <cstdlib>
//#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnDependencyNode.h>
//#include <maya/MUserData.h>
//#include <maya/MHardwareRenderer.h>
// Includes for swatch rendering
#include <maya/MHWShaderSwatchGenerator.h>
#include <maya/MImage.h>
#include <maya/MRenderUtilities.h>
#include <maya/MMatrix.h>
// Viewport 2.0 includes
#include <maya/MDrawRegistry.h>
#include <maya/MPxShaderOverride.h>
#include <maya/MDrawContext.h>
#include <maya/MStateManager.h>
#include <maya/MViewport2Renderer.h>
#include <maya/MShaderManager.h>
//include for adding fragment
#include <maya/MFragmentManager.h>
#undef ENABLE_TRACE_API_CALLS
//#define ENABLE_TRACE_API_CALLS 1
#ifdef ENABLE_TRACE_API_CALLS
#define TRACE_API_CALLS(x) cerr <<(x)<<"\n"
#else
#define TRACE_API_CALLS(x)
#endif
#include "vp2BlinnShader.h"
// Node id
MTypeId vp2BlinnShader::id( 0x00081102 );
// Node attributes
MObject vp2BlinnShader::aColor;
MObject vp2BlinnShader::aTransparency;
MObject vp2BlinnShader::aSpecularColor;
MObject vp2BlinnShader::aNonTexturedColor;
MObject vp2BlinnShader::aNonTexturedTransparency;
MObject vp2BlinnShader::aAddFragment;
///////////////////////////////////////////////////////////////////////////////////////////
// Node methods
///////////////////////////////////////////////////////////////////////////////////////////
void * vp2BlinnShader::creator()
{
TRACE_API_CALLS("vp2BlinnShader::creator");
return new vp2BlinnShader();
}
vp2BlinnShader::vp2BlinnShader()
{
TRACE_API_CALLS("vp2BlinnShader::vp2BlinnShader");
}
vp2BlinnShader::~vp2BlinnShader()
{
TRACE_API_CALLS("vp2BlinnShader::~vp2BlinnShader");
}
MStatus vp2BlinnShader::initialize()
{
// Shader attributes for the node
// They have been created to match internal parameters of the
// hardware shader instance
//
TRACE_API_CALLS("vp2BlinnShader::initialize");
MFnNumericAttribute nAttr;
// Create textured mode input attributes
MStatus result;
aAddFragment = nAttr.create("addFragment", "adf", MFnNumericData::kBoolean);
result = nAttr.setStorable(true);
result = nAttr.setKeyable(true);
result = nAttr.setDefault(false);
result = nAttr.setCached(true);
result = nAttr.setInternal(true);
result = nAttr.setAffectsAppearance(false);
aColor = nAttr.createColor( "color", "c");
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.6f, 0.6f, 0.6f);
nAttr.setAffectsAppearance( true );
aTransparency = nAttr.create( "transparency", "tr", MFnNumericData::kFloat );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.0f);
nAttr.setMax(1.0f);
nAttr.setMin(0.0f);
nAttr.setAffectsAppearance( true );
aSpecularColor = nAttr.createColor( "specularColor", "sc" );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 1.0f, 1.0f);
nAttr.setAffectsAppearance( true );
// Create non-textured mode input attributes
aNonTexturedColor = nAttr.createColor( "nonTexturedColor", "nc");
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 0.0f, 0.0f);
nAttr.setAffectsAppearance( true );
aNonTexturedTransparency = nAttr.create( "nonTexturedTransparency", "nt", MFnNumericData::kFloat );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.0f);
nAttr.setMax(1.0f);
nAttr.setMin(0.0f);
nAttr.setAffectsAppearance( true );
// create output attributes here
// outColor is the only output attribute and it is inherited
// so we do not need to create or add it.
//
// Add the attributes to the node
addAttribute(aAddFragment);
addAttribute(aColor);
addAttribute(aTransparency);
addAttribute(aSpecularColor);
addAttribute(aNonTexturedColor);
addAttribute(aNonTexturedTransparency);
attributeAffects (aAddFragment, outColor);
attributeAffects (aColor, outColor);
attributeAffects (aTransparency, outColor);
attributeAffects (aSpecularColor, outColor);
attributeAffects (aNonTexturedColor,outColor);
attributeAffects (aNonTexturedTransparency,outColor);
return MS::kSuccess;
}
//
// Very simplistic software compute for the Maya software renderer
// This code is not the focus of this plug-in example so just
// returns a constant color.
//
MStatus vp2BlinnShader::compute(
const MPlug& plug,
MDataBlock& block )
{
TRACE_API_CALLS("vp2BlinnShader::compute");
if ((plug != outColor) && (plug.parent() != outColor))
return MS::kUnknownParameter;
MFloatVector & color = block.inputValue( aColor ).asFloatVector();
// set output color attribute
MDataHandle outColorHandle = block.outputValue( outColor );
MFloatVector& outColor = outColorHandle.asFloatVector();
outColor = color;
outColorHandle.setClean();
return MS::kSuccess;
}
////////////////////////////////////////////////////////////////////////////////////
// Swatch rendering:
// Does not matter the mode for the viewport VP1 or VP2
// Uses material viewer utility which uses the VP2 render to draw the swatch.
////////////////////////////////////////////////////////////////////////////////////
MStatus vp2BlinnShader::renderSwatchImage( MImage & outImage )
{
if (MHWRender::MRenderer::theRenderer())
{
// Use some sample objects for display
MString meshSphere("meshTeapot");
MString meshShaderball("meshShaderball");
unsigned int targetW, targetH;
outImage.getSize(targetW, targetH);
return MHWRender::MRenderUtilities::renderMaterialViewerGeometry(targetW > 128 ? meshShaderball : meshSphere,
thisMObject(),
outImage,
MHWRender::MRenderUtilities::kPerspectiveCamera,
MHWRender::MRenderUtilities::kSwatchLight);
}
return MS::kSuccess;
}
////////////////////////////////////////////////////////////////////////////////////
// Viewport 2.0 shader override implementation
////////////////////////////////////////////////////////////////////////////////////
class vp2BlinnShaderOverride : public MHWRender::MPxShaderOverride
{
public:
// Static method to create a new override
static MHWRender::MPxShaderOverride* Creator(const MObject& obj)
{
return new vp2BlinnShaderOverride(obj);
}
// Release the textured and non-textured mode shaders.
virtual ~vp2BlinnShaderOverride()
{
MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
if (theRenderer)
{
const MHWRender::MShaderManager* shaderMgr = theRenderer->getShaderManager();
if (shaderMgr)
{
if (fColorShaderInstance)
{
shaderMgr->releaseShader(fColorShaderInstance);
}
fColorShaderInstance = NULL;
if (fNonTexturedColorShaderInstance)
{
shaderMgr->releaseShader(fNonTexturedColorShaderInstance);
}
fColorShaderInstance = NULL;
}
}
}
// 1. Initialize phase
// For this plug-in we simply set up geometry requirements
// based on an MShaderInstance
//
virtual MString initialize(const MInitContext& initContext,
MInitFeedback& initFeedback)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::initialize");
if (fColorShaderInstance)
{
// This plugin is using the utility method
// MPxShaderOverride::drawGeometry(). For DX11 drawing,
// a shader signature is required. We use
// the signature from the same MShaderInstance used to
// set the geometry requirements so that the signature
// will match the requirements.
//
addShaderSignature( *fColorShaderInstance );
// Set the geometry requirements based on the shader instance
setGeometryRequirements( *fColorShaderInstance );
}
return MString("Autodesk Maya vp2 Blinn Shader Override");
}
// 2. Update Phase
// Access the node attributes and cache the values to update
// during updateDevice()
//
virtual void updateDG(MObject object)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::updateDG");
if (object == MObject::kNullObj)
return;
// Get the hardware shader node from the MObject.
vp2BlinnShader *shaderNode = (vp2BlinnShader *) MPxHardwareShader::getHardwareShaderPtr( object );
if (!shaderNode)
return;
// Cache any data from the node to local data members.
MStatus status;
bool addFragment = false;
MFnDependencyNode node(object, &status);
if (status)
{
node.findPlug("colorR").getValue(fDiffuse[0]);
node.findPlug("colorG").getValue(fDiffuse[1]);
node.findPlug("colorB").getValue(fDiffuse[2]);
node.findPlug("transparency").getValue(fTransparency);
fDiffuse[3] = 1.0f - fTransparency;
node.findPlug("specularColorR").getValue(fSpecular[0]);
node.findPlug("specularColorG").getValue(fSpecular[1]);
node.findPlug("specularColorB").getValue(fSpecular[2]);
node.findPlug("nonTexturedColorR").getValue(fNonTextured[0]);
node.findPlug("nonTexturedColorG").getValue(fNonTextured[1]);
node.findPlug("nonTexturedColorB").getValue(fNonTextured[2]);
float nonTextureTransparency = 0.0f;
node.findPlug("nonTexturedTransparency").getValue(nonTextureTransparency);
fNonTextured[3] = 1.0f - nonTextureTransparency;
node.findPlug("addFragment").getValue(addFragment);
if (FragmentStatus != addFragment)
{
reCreateShaderInstance(addFragment);
FragmentStatus = addFragment;
}
}
}
// 2. Update Phase
// Call into utility method to update the shader instance
// when device level update is allowed.
virtual void updateDevice()
{
updateShaderInstance();
}
// 2. Update Phase
// Transparency hint
virtual bool isTransparent()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::isTransparent");
return (fTransparency > 0.0f);
}
// 2. Update Phase
// There is nothing to do at the end of update.
// Method is included for debug tracing only.
virtual void endUpdate()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::endUpdate");
}
// 3. Draw Phase
// Return the shader instance used for rendering
virtual MHWRender::MShaderInstance* shaderInstance() const
{
TRACE_API_CALLS("vp2BlinnShaderOverride::shaderInstance");
return fColorShaderInstance;
}
// 3. Draw Phase
// Bind the shader on activateKey() and
// the termination occur in terminateKey().
virtual void activateKey(MHWRender::MDrawContext& context, const MString& key)
{
MString out("vp2BlinnShaderOverride::activateKey[");
out += key;
out += "]";
TRACE_API_CALLS(out.asChar());
fColorShaderInstance->bind( context );
std::cerr << "display mode no = " << context.getDisplayStyle() << std::endl;
}
// 3. Draw Phase
// Use custom shader instance
//
virtual bool draw(MHWRender::MDrawContext& context,
const MHWRender::MRenderItemList& renderItemList) const
{
MString out("vp2BlinnShaderOverride::draw[Count=");
out += renderItemList.length();
out += "]";
TRACE_API_CALLS(out);
// Activate all the shader passes and draw using internal draw methods.
unsigned int passCount = fColorShaderInstance->getPassCount( context );
for (unsigned int i=0; i<passCount; i++)
{
fColorShaderInstance->activatePass( context, i );
MHWRender::MPxShaderOverride::drawGeometry(context);
}
return true;
}
// 3. Draw Phase
// Unbind / terminate the shader instance here.
virtual void terminateKey(MHWRender::MDrawContext& context, const MString& key)
{
MString out("vp2BlinnShaderOverride::terminateKey[");
out += key;
out += "]";
TRACE_API_CALLS(out.asChar());
// Unbind the shader
fColorShaderInstance->unbind( context );
}
// We are using an internal resources so we support all draw APIs
// automatically.
virtual MHWRender::DrawAPI supportedDrawAPIs() const
{
return (MHWRender::kOpenGL | MHWRender::kDirectX11 | MHWRender::kOpenGLCoreProfile);
}
// Supply a shader instance to be used when in non-textured mode. This
// allows for identification of objects using the "non-textured" mode color.
//
virtual MHWRender::MShaderInstance* nonTexturedShaderInstance(bool &monitor) const
{
TRACE_API_CALLS("vp2BlinnShaderOverride::nonTexturedShaderInstance");
if (fNonTexturedColorShaderInstance)
{
monitor = true;
// Mark whether the shader is transparent or not
fNonTexturedColorShaderInstance->setIsTransparent( fNonTextured[3] != 1.0f );
return fNonTexturedColorShaderInstance;
}
return NULL;
}
protected:
//
// Update the shader using the values cached during DG evaluation
// Called from updateDevice() during update phase.
//
void updateShaderInstance()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::updateShaderInstance");
if (FragmentStatus)
{
if (fColorShaderInstance)
{
// Update shader to mark it as drawing with transparency or not.
fColorShaderInstance->setIsTransparent(false);
}
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setIsTransparent(false);
}
}
else
{
if (fColorShaderInstance)
{
// Update shader to mark it as drawing with transparency or not.
fColorShaderInstance->setIsTransparent(isTransparent());
fColorShaderInstance->setParameter("diffuseColor", &fDiffuse[0]);
fColorShaderInstance->setParameter("specularColor", &fSpecular[0]);
}
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
fNonTexturedColorShaderInstance->setIsTransparent(fNonTextured[3] != 1.0f);
}
}
}
// Code to create MShaderInstances using a stock internal Blinn shader
// Create one shader for textured mode and one for non-textured mode.
//
void createShaderInstance()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::createShaderInstance");
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (!shaderMgr)
return;
if (!fColorShaderInstance)
{
fColorShaderInstance = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
}
if (!fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
}
}
}
void reCreateShaderInstance(bool addFragment)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::createShaderInstance");
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (!shaderMgr)
return;
MHWRender::MFragmentManager* fragmentMgr =
renderer->getFragmentManager();
if (!fragmentMgr)
return;
//clean existing shader instance
if (addFragment) //then add the fragment
{
AddTestFragment();
}
else //remove fragment
{
if (fragmentMgr->hasFragment(testFragmentName))
{
fragmentMgr->removeFragment(testFragmentName);
}
}
shaderMgr->releaseShader(fColorShaderInstance);
fColorShaderInstance = nullptr;
shaderMgr->releaseShader(fNonTexturedColorShaderInstance);
fNonTexturedColorShaderInstance = nullptr;
shaderMgr->clearEffectCache();
MStatus result;
if (!fColorShaderInstance)
{
fColorShaderInstance = shaderMgr->getStockShader(MHWRender::MShaderManager::k3dBlinnShader);
if (addFragment) //then add the fragment
{
result = fColorShaderInstance->addInputFragment(testFragmentName, "outTestColor", "diffuseColor");
}
}
if (!fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance = shaderMgr->getStockShader(MHWRender::MShaderManager::k3dBlinnShader);
if (fNonTexturedColorShaderInstance)
{
if (addFragment) //then add the fragment
{
result = fNonTexturedColorShaderInstance->addInputFragment(testFragmentName, "outTestColor", "diffuseColor");
}
else
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
}
}
}
}
void AddTestFragment()
{
MString fragmentName("testFragment");
static const char* nonTexturedFragmentBodyBsdf =
"<fragment uiName=\"testFragment\" name=\"testFragment\" type=\"plumbing\" class=\"ShadeFragment\" version=\"1.0\">"
" <description><![CDATA[Simple file texture fragment]]></description>"
" <properties>"
" </properties>"
" <values>"
" </values>"
" <outputs>"
" <float3 name=\"outTestColor\" />"
" </outputs>"
" <implementation>"
" <implementation render=\"OGSRenderer\" language=\"Cg\" lang_version=\"2.100000\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"float3 testFragment() \n"
"{ \n"
" return float3(0.0, 0.0, 0.0); \n"
"} \n]]>"
" </source>"
" </implementation>"
" <implementation render=\"OGSRenderer\" language=\"HLSL\" lang_version=\"11.000000\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"float3 testFragment() \n"
"{ \n"
" return float3(0.0, 0.0, 0.0); \n"
"} \n]]>"
" </source>"
" </implementation>"
" <implementation render=\"OGSRenderer\" language=\"GLSL\" lang_version=\"3.0\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"vec3 testFragment() \n"
"{ \n"
" return vec3(0.0, 0.5, 0.5); \n"
"} \n]]>"
" </source>"
" </implementation>"
" </implementation>"
"</fragment>";
testFragmentName = "";
MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
if (theRenderer)
{
MHWRender::MFragmentManager* fragmentMgr =
theRenderer->getFragmentManager();
if (fragmentMgr)
{
// Add fragments if needed
bool fragAdded = fragmentMgr->hasFragment(fragmentName);
if (!fragAdded)
{
fragAdded = (fragmentName == fragmentMgr->addShadeFragmentFromBuffer(nonTexturedFragmentBodyBsdf, false));
}
if (fragAdded)
{
testFragmentName = fragmentName;
}
}
}
}
//
// Constructor. Simply initialize shader instances for usage.
//
vp2BlinnShaderOverride(const MObject& obj)
: MHWRender::MPxShaderOverride(obj)
, fColorShaderInstance(NULL)
, fNonTexturedColorShaderInstance(NULL)
, fTransparency(0.0f)
{
fDiffuse[0] = fDiffuse[1] = fDiffuse[2] = fDiffuse[3] = 0.0f;
fSpecular[0] = fSpecular[1] = fSpecular[2] = 0.0f;
fNonTextured[0] = 1.0; fNonTextured[1] = fNonTextured[2] = 0.0f;
fNonTextured[3] = 1.0f;
fNewColor[0] = 1.0; fNewColor[1] = fNewColor[2] = 0.0f;
FragmentStatus = false;
testFragmentName = "";
// Create a shader instance to use for drawing
//
createShaderInstance();
}
// Cached shader inputs values
float fTransparency;
float fDiffuse[4];
float fSpecular[3];
float fShininess[3];
float fNonTextured[4];
float fNewColor[4];
bool FragmentStatus;
MString testFragmentName;
// Shader to use to draw with
MHWRender::MShaderInstance *fColorShaderInstance;
// Shader to use to draw non-textured with
MHWRender::MShaderInstance *fNonTexturedColorShaderInstance;
};
/////////////////////////////////////////////////////////////////////////////////////////
// Plug-in handling
/////////////////////////////////////////////////////////////////////////////////////////
static const MString svp2BlinnShaderRegistrantId("vp2BlinnShaderRegistrantId");
// Note that we use the same drawdb classification for both registerNode()
// and registerShaderOverrideCreator() to associate the override with the Maya node.
//
MStatus initializePlugin( MObject obj )
{
TRACE_API_CALLS("initializePlugin");
MStatus status;
const MString& swatchName = MHWShaderSwatchGenerator::initialize();
const MString UserClassify( "shader/surface/utility/:drawdb/shader/surface/vp2BlinnShader:swatch/"+swatchName);
MFnPlugin plugin( obj, PLUGIN_COMPANY, "1.0", "Any");
status = plugin.registerNode( "vp2BlinnShader", vp2BlinnShader::id,
vp2BlinnShader::creator, vp2BlinnShader::initialize,
MPxNode::kHardwareShader, &UserClassify );
if (!status) {
status.perror("registerNode");
return status;
}
// Register a shader override for this node
MHWRender::MDrawRegistry::registerShaderOverrideCreator(
"drawdb/shader/surface/vp2BlinnShader",
svp2BlinnShaderRegistrantId,
vp2BlinnShaderOverride::Creator);
if (status != MS::kSuccess) return status;
return MS::kSuccess;
}
MStatus uninitializePlugin( MObject obj )
{
TRACE_API_CALLS("uninitializePlugin");
MStatus status;
MFnPlugin plugin( obj );
// Unregister the shader node
plugin.deregisterNode( vp2BlinnShader::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
// Deregister the shader override
status = MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
"drawdb/shader/surface/vp2BlinnShader", svp2BlinnShaderRegistrantId);
if (status != MS::kSuccess) return status;
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
if (renderer)
{
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (shaderMgr)
shaderMgr->clearEffectCache();
}
return MS::kSuccess;
}
//-
// ==========================================================================
// Copyright 2015 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
#include <maya/MIOStream.h>
#include <math.h>
#include <cstdlib>
//#include <maya/MGlobal.h>
#include <maya/MString.h>
#include <maya/MPlug.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFloatVector.h>
#include <maya/MFnPlugin.h>
#include <maya/MFnDependencyNode.h>
//#include <maya/MUserData.h>
//#include <maya/MHardwareRenderer.h>
// Includes for swatch rendering
#include <maya/MHWShaderSwatchGenerator.h>
#include <maya/MImage.h>
#include <maya/MRenderUtilities.h>
#include <maya/MMatrix.h>
// Viewport 2.0 includes
#include <maya/MDrawRegistry.h>
#include <maya/MPxShaderOverride.h>
#include <maya/MDrawContext.h>
#include <maya/MStateManager.h>
#include <maya/MViewport2Renderer.h>
#include <maya/MShaderManager.h>
//include for adding fragment
#include <maya/MFragmentManager.h>
#undef ENABLE_TRACE_API_CALLS
//#define ENABLE_TRACE_API_CALLS 1
#ifdef ENABLE_TRACE_API_CALLS
#define TRACE_API_CALLS(x) cerr <<(x)<<"\n"
#else
#define TRACE_API_CALLS(x)
#endif
#include "vp2BlinnShader.h"
// Node id
MTypeId vp2BlinnShader::id( 0x00081102 );
// Node attributes
MObject vp2BlinnShader::aColor;
MObject vp2BlinnShader::aTransparency;
MObject vp2BlinnShader::aSpecularColor;
MObject vp2BlinnShader::aNonTexturedColor;
MObject vp2BlinnShader::aNonTexturedTransparency;
MObject vp2BlinnShader::aAddFragment;
///////////////////////////////////////////////////////////////////////////////////////////
// This plug-in implementation shows the usage of an MPxShaderOverride for
// a Maya shader node.
//
// The purpose is to show as simple a plug-in as possible without worrying
// about the details of trying to write a shader system.
//
// As such it attempts to (re)use as many internal VP2 API
// intterfaces and constructs as possible.
//
// In this case the plug-in will use a stock shader instance (MShaderInstance)
// as it's single internal shader. As part of initialization it will reuse
// internal MShaderInstance utility methods to show how a DX11 shader signature
// can be set as as well as how to return the vertex requirements simply.
// As there is only one shader instance the shader key used is also simplistic
// as it can be a constant value.
//
// Transparency notificaiton is handle by setting the MPxShaderOverride::isTransparent()
// virtual method return value appropriately based on node attribute values.
//
// The code also demonstrates handling of non-textured draw by providing another stock
// MShaderInstance to be returned from MPxShaderOverride::nonTexturedShaderInstance()
// It will also handle transparency by appropriately updating the shader instance
// transparency hint using MShaderInstance::setIsTransparent().
//
// For update, the node attributes match a few of the MShaderInstance parameters
// to show DG update and device update. Additional attributes are added to
// show how the non-texture mode shader instance parameters can be driven
// by attribute changes.
//
// For drawing, the code reuses the internally provided binding interfaces on
// MShaderInstance as well as the utility geometry drawing method:
// MPxShaderOverride::drawGeometry().
//
// For swatch rendering the basic VP2 supplied utility
// MRenderUtilities::renderMaterialViewerGeometry() is used.
//
// (De)registration is shown as part of plug-in (de)initialization, noting
// that the MPxShaderOverride association to the MPxNode node is achieved
// by supplying the same "drawdb/shader" classification for both registration
// interfaces.
//
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Node methods
///////////////////////////////////////////////////////////////////////////////////////////
void * vp2BlinnShader::creator()
{
TRACE_API_CALLS("vp2BlinnShader::creator");
return new vp2BlinnShader();
}
vp2BlinnShader::vp2BlinnShader()
{
TRACE_API_CALLS("vp2BlinnShader::vp2BlinnShader");
}
vp2BlinnShader::~vp2BlinnShader()
{
TRACE_API_CALLS("vp2BlinnShader::~vp2BlinnShader");
}
MStatus vp2BlinnShader::initialize()
{
// Shader attributes for the node
// They have been created to match internal parameters of the
// hardware shader instance
//
TRACE_API_CALLS("vp2BlinnShader::initialize");
MFnNumericAttribute nAttr;
// Create textured mode input attributes
MStatus result;
aAddFragment = nAttr.create("addFragment", "adf", MFnNumericData::kBoolean);
result = nAttr.setStorable(true);
result = nAttr.setKeyable(true);
result = nAttr.setDefault(false);
result = nAttr.setCached(true);
result = nAttr.setInternal(true);
result = nAttr.setAffectsAppearance(false);
aColor = nAttr.createColor( "color", "c");
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.6f, 0.6f, 0.6f);
nAttr.setAffectsAppearance( true );
aTransparency = nAttr.create( "transparency", "tr", MFnNumericData::kFloat );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.0f);
nAttr.setMax(1.0f);
nAttr.setMin(0.0f);
nAttr.setAffectsAppearance( true );
aSpecularColor = nAttr.createColor( "specularColor", "sc" );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 1.0f, 1.0f);
nAttr.setAffectsAppearance( true );
// Create non-textured mode input attributes
aNonTexturedColor = nAttr.createColor( "nonTexturedColor", "nc");
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(1.0f, 0.0f, 0.0f);
nAttr.setAffectsAppearance( true );
aNonTexturedTransparency = nAttr.create( "nonTexturedTransparency", "nt", MFnNumericData::kFloat );
nAttr.setStorable(true);
nAttr.setKeyable(true);
nAttr.setDefault(0.0f);
nAttr.setMax(1.0f);
nAttr.setMin(0.0f);
nAttr.setAffectsAppearance( true );
// create output attributes here
// outColor is the only output attribute and it is inherited
// so we do not need to create or add it.
//
// Add the attributes to the node
addAttribute(aAddFragment);
addAttribute(aColor);
addAttribute(aTransparency);
addAttribute(aSpecularColor);
addAttribute(aNonTexturedColor);
addAttribute(aNonTexturedTransparency);
attributeAffects (aAddFragment, outColor);
attributeAffects (aColor, outColor);
attributeAffects (aTransparency, outColor);
attributeAffects (aSpecularColor, outColor);
attributeAffects (aNonTexturedColor,outColor);
attributeAffects (aNonTexturedTransparency,outColor);
return MS::kSuccess;
}
//
// Very simplistic software compute for the Maya software renderer
// This code is not the focus of this plug-in example so just
// returns a constant color.
//
MStatus vp2BlinnShader::compute(
const MPlug& plug,
MDataBlock& block )
{
TRACE_API_CALLS("vp2BlinnShader::compute");
if ((plug != outColor) && (plug.parent() != outColor))
return MS::kUnknownParameter;
MFloatVector & color = block.inputValue( aColor ).asFloatVector();
// set output color attribute
MDataHandle outColorHandle = block.outputValue( outColor );
MFloatVector& outColor = outColorHandle.asFloatVector();
outColor = color;
outColorHandle.setClean();
return MS::kSuccess;
}
////////////////////////////////////////////////////////////////////////////////////
// Swatch rendering:
// Does not matter the mode for the viewport VP1 or VP2
// Uses material viewer utility which uses the VP2 render to draw the swatch.
////////////////////////////////////////////////////////////////////////////////////
MStatus vp2BlinnShader::renderSwatchImage( MImage & outImage )
{
if (MHWRender::MRenderer::theRenderer())
{
// Use some sample objects for display
MString meshSphere("meshTeapot");
MString meshShaderball("meshShaderball");
unsigned int targetW, targetH;
outImage.getSize(targetW, targetH);
return MHWRender::MRenderUtilities::renderMaterialViewerGeometry(targetW > 128 ? meshShaderball : meshSphere,
thisMObject(),
outImage,
MHWRender::MRenderUtilities::kPerspectiveCamera,
MHWRender::MRenderUtilities::kSwatchLight);
}
return MS::kSuccess;
}
////////////////////////////////////////////////////////////////////////////////////
// Viewport 2.0 shader override implementation
////////////////////////////////////////////////////////////////////////////////////
class vp2BlinnShaderOverride : public MHWRender::MPxShaderOverride
{
public:
// Static method to create a new override
static MHWRender::MPxShaderOverride* Creator(const MObject& obj)
{
return new vp2BlinnShaderOverride(obj);
}
// Release the textured and non-textured mode shaders.
virtual ~vp2BlinnShaderOverride()
{
MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
if (theRenderer)
{
const MHWRender::MShaderManager* shaderMgr = theRenderer->getShaderManager();
if (shaderMgr)
{
if (fColorShaderInstance)
{
shaderMgr->releaseShader(fColorShaderInstance);
}
fColorShaderInstance = NULL;
if (fNonTexturedColorShaderInstance)
{
shaderMgr->releaseShader(fNonTexturedColorShaderInstance);
}
fColorShaderInstance = NULL;
}
}
}
// 1. Initialize phase
// For this plug-in we simply set up geometry requirements
// based on an MShaderInstance
//
virtual MString initialize(const MInitContext& initContext,
MInitFeedback& initFeedback)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::initialize");
if (fColorShaderInstance)
{
// This plugin is using the utility method
// MPxShaderOverride::drawGeometry(). For DX11 drawing,
// a shader signature is required. We use
// the signature from the same MShaderInstance used to
// set the geometry requirements so that the signature
// will match the requirements.
//
addShaderSignature( *fColorShaderInstance );
// Set the geometry requirements based on the shader instance
setGeometryRequirements( *fColorShaderInstance );
}
return MString("Autodesk Maya vp2 Blinn Shader Override");
}
// 2. Update Phase
// Access the node attributes and cache the values to update
// during updateDevice()
//
virtual void updateDG(MObject object)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::updateDG");
if (object == MObject::kNullObj)
return;
// Get the hardware shader node from the MObject.
vp2BlinnShader *shaderNode = (vp2BlinnShader *) MPxHardwareShader::getHardwareShaderPtr( object );
if (!shaderNode)
return;
// Cache any data from the node to local data members.
MStatus status;
bool addFragment = false;
MFnDependencyNode node(object, &status);
if (status)
{
node.findPlug("colorR").getValue(fDiffuse[0]);
node.findPlug("colorG").getValue(fDiffuse[1]);
node.findPlug("colorB").getValue(fDiffuse[2]);
node.findPlug("transparency").getValue(fTransparency);
fDiffuse[3] = 1.0f - fTransparency;
node.findPlug("specularColorR").getValue(fSpecular[0]);
node.findPlug("specularColorG").getValue(fSpecular[1]);
node.findPlug("specularColorB").getValue(fSpecular[2]);
node.findPlug("nonTexturedColorR").getValue(fNonTextured[0]);
node.findPlug("nonTexturedColorG").getValue(fNonTextured[1]);
node.findPlug("nonTexturedColorB").getValue(fNonTextured[2]);
float nonTextureTransparency = 0.0f;
node.findPlug("nonTexturedTransparency").getValue(nonTextureTransparency);
fNonTextured[3] = 1.0f - nonTextureTransparency;
node.findPlug("addFragment").getValue(addFragment);
if (FragmentStatus != addFragment)
{
reCreateShaderInstance(addFragment);
FragmentStatus = addFragment;
}
}
}
// 2. Update Phase
// Call into utility method to update the shader instance
// when device level update is allowed.
virtual void updateDevice()
{
updateShaderInstance();
}
// 2. Update Phase
// Transparency hint
virtual bool isTransparent()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::isTransparent");
return (fTransparency > 0.0f);
}
// 2. Update Phase
// There is nothing to do at the end of update.
// Method is included for debug tracing only.
virtual void endUpdate()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::endUpdate");
}
// 3. Draw Phase
// Return the shader instance used for rendering
virtual MHWRender::MShaderInstance* shaderInstance() const
{
TRACE_API_CALLS("vp2BlinnShaderOverride::shaderInstance");
return fColorShaderInstance;
}
// 3. Draw Phase
// Bind the shader on activateKey() and
// the termination occur in terminateKey().
virtual void activateKey(MHWRender::MDrawContext& context, const MString& key)
{
MString out("vp2BlinnShaderOverride::activateKey[");
out += key;
out += "]";
TRACE_API_CALLS(out.asChar());
fColorShaderInstance->bind( context );
std::cerr << "display mode no = " << context.getDisplayStyle() << std::endl;
}
// 3. Draw Phase
// Use custom shader instance
//
virtual bool draw(MHWRender::MDrawContext& context,
const MHWRender::MRenderItemList& renderItemList) const
{
MString out("vp2BlinnShaderOverride::draw[Count=");
out += renderItemList.length();
out += "]";
TRACE_API_CALLS(out);
// Activate all the shader passes and draw using internal draw methods.
unsigned int passCount = fColorShaderInstance->getPassCount( context );
for (unsigned int i=0; i<passCount; i++)
{
fColorShaderInstance->activatePass( context, i );
MHWRender::MPxShaderOverride::drawGeometry(context);
}
return true;
}
// 3. Draw Phase
// Unbind / terminate the shader instance here.
virtual void terminateKey(MHWRender::MDrawContext& context, const MString& key)
{
MString out("vp2BlinnShaderOverride::terminateKey[");
out += key;
out += "]";
TRACE_API_CALLS(out.asChar());
// Unbind the shader
fColorShaderInstance->unbind( context );
}
// We are using an internal resources so we support all draw APIs
// automatically.
virtual MHWRender::DrawAPI supportedDrawAPIs() const
{
return (MHWRender::kOpenGL | MHWRender::kDirectX11 | MHWRender::kOpenGLCoreProfile);
}
// Supply a shader instance to be used when in non-textured mode. This
// allows for identification of objects using the "non-textured" mode color.
//
virtual MHWRender::MShaderInstance* nonTexturedShaderInstance(bool &monitor) const
{
TRACE_API_CALLS("vp2BlinnShaderOverride::nonTexturedShaderInstance");
if (fNonTexturedColorShaderInstance)
{
monitor = true;
// Mark whether the shader is transparent or not
fNonTexturedColorShaderInstance->setIsTransparent( fNonTextured[3] != 1.0f );
return fNonTexturedColorShaderInstance;
}
return NULL;
}
protected:
//
// Update the shader using the values cached during DG evaluation
// Called from updateDevice() during update phase.
//
void updateShaderInstance()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::updateShaderInstance");
if (FragmentStatus)
{
if (fColorShaderInstance)
{
// Update shader to mark it as drawing with transparency or not.
fColorShaderInstance->setIsTransparent(false);
}
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setIsTransparent(false);
}
}
else
{
if (fColorShaderInstance)
{
// Update shader to mark it as drawing with transparency or not.
fColorShaderInstance->setIsTransparent(isTransparent());
fColorShaderInstance->setParameter("diffuseColor", &fDiffuse[0]);
fColorShaderInstance->setParameter("specularColor", &fSpecular[0]);
}
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
fNonTexturedColorShaderInstance->setIsTransparent(fNonTextured[3] != 1.0f);
}
}
}
// Code to create MShaderInstances using a stock internal Blinn shader
// Create one shader for textured mode and one for non-textured mode.
//
void createShaderInstance()
{
TRACE_API_CALLS("vp2BlinnShaderOverride::createShaderInstance");
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (!shaderMgr)
return;
if (!fColorShaderInstance)
{
fColorShaderInstance = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
}
if (!fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance = shaderMgr->getStockShader( MHWRender::MShaderManager::k3dBlinnShader );
if (fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
}
}
}
void reCreateShaderInstance(bool addFragment)
{
TRACE_API_CALLS("vp2BlinnShaderOverride::createShaderInstance");
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (!shaderMgr)
return;
MHWRender::MFragmentManager* fragmentMgr =
renderer->getFragmentManager();
if (!fragmentMgr)
return;
//clean existing shader instance
if (addFragment) //then add the fragment
{
AddTestFragment();
}
else //remove fragment
{
if (fragmentMgr->hasFragment(testFragmentName))
{
fragmentMgr->removeFragment(testFragmentName);
}
}
shaderMgr->releaseShader(fColorShaderInstance);
fColorShaderInstance = nullptr;
shaderMgr->releaseShader(fNonTexturedColorShaderInstance);
fNonTexturedColorShaderInstance = nullptr;
shaderMgr->clearEffectCache();
MStatus result;
if (!fColorShaderInstance)
{
fColorShaderInstance = shaderMgr->getStockShader(MHWRender::MShaderManager::k3dBlinnShader);
if (addFragment) //then add the fragment
{
result = fColorShaderInstance->addInputFragment(testFragmentName, "outTestColor", "diffuseColor");
}
}
if (!fNonTexturedColorShaderInstance)
{
fNonTexturedColorShaderInstance = shaderMgr->getStockShader(MHWRender::MShaderManager::k3dBlinnShader);
if (fNonTexturedColorShaderInstance)
{
if (addFragment) //then add the fragment
{
result = fNonTexturedColorShaderInstance->addInputFragment(testFragmentName, "outTestColor", "diffuseColor");
}
else
{
fNonTexturedColorShaderInstance->setParameter("diffuseColor", &fNonTextured[0]);
}
}
}
}
void AddTestFragment()
{
MString fragmentName("testFragment");
static const char* nonTexturedFragmentBodyBsdf =
"<fragment uiName=\"testFragment\" name=\"testFragment\" type=\"plumbing\" class=\"ShadeFragment\" version=\"1.0\">"
" <description><![CDATA[Simple file texture fragment]]></description>"
" <properties>"
" </properties>"
" <values>"
" </values>"
" <outputs>"
" <float3 name=\"outTestColor\" />"
" </outputs>"
" <implementation>"
" <implementation render=\"OGSRenderer\" language=\"Cg\" lang_version=\"2.100000\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"float3 testFragment() \n"
"{ \n"
" return float3(0.0, 0.0, 0.0); \n"
"} \n]]>"
" </source>"
" </implementation>"
" <implementation render=\"OGSRenderer\" language=\"HLSL\" lang_version=\"11.000000\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"float3 testFragment() \n"
"{ \n"
" return float3(0.0, 0.0, 0.0); \n"
"} \n]]>"
" </source>"
" </implementation>"
" <implementation render=\"OGSRenderer\" language=\"GLSL\" lang_version=\"3.0\">"
" <function_name val=\"testFragment\" />"
" <source><![CDATA["
"vec3 testFragment() \n"
"{ \n"
" return vec3(0.0, 0.5, 0.5); \n"
"} \n]]>"
" </source>"
" </implementation>"
" </implementation>"
"</fragment>";
testFragmentName = "";
MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
if (theRenderer)
{
MHWRender::MFragmentManager* fragmentMgr =
theRenderer->getFragmentManager();
if (fragmentMgr)
{
// Add fragments if needed
bool fragAdded = fragmentMgr->hasFragment(fragmentName);
if (!fragAdded)
{
fragAdded = (fragmentName == fragmentMgr->addShadeFragmentFromBuffer(nonTexturedFragmentBodyBsdf, false));
}
if (fragAdded)
{
testFragmentName = fragmentName;
}
}
}
}
//
// Constructor. Simply initialize shader instances for usage.
//
vp2BlinnShaderOverride(const MObject& obj)
: MHWRender::MPxShaderOverride(obj)
, fColorShaderInstance(NULL)
, fNonTexturedColorShaderInstance(NULL)
, fTransparency(0.0f)
{
fDiffuse[0] = fDiffuse[1] = fDiffuse[2] = fDiffuse[3] = 0.0f;
fSpecular[0] = fSpecular[1] = fSpecular[2] = 0.0f;
fNonTextured[0] = 1.0; fNonTextured[1] = fNonTextured[2] = 0.0f;
fNonTextured[3] = 1.0f;
fNewColor[0] = 1.0; fNewColor[1] = fNewColor[2] = 0.0f;
FragmentStatus = false;
testFragmentName = "";
// Create a shader instance to use for drawing
//
createShaderInstance();
}
// Cached shader inputs values
float fTransparency;
float fDiffuse[4];
float fSpecular[3];
float fShininess[3];
float fNonTextured[4];
float fNewColor[4];
bool FragmentStatus;
MString testFragmentName;
// Shader to use to draw with
MHWRender::MShaderInstance *fColorShaderInstance;
// Shader to use to draw non-textured with
MHWRender::MShaderInstance *fNonTexturedColorShaderInstance;
};
/////////////////////////////////////////////////////////////////////////////////////////
// Plug-in handling
/////////////////////////////////////////////////////////////////////////////////////////
static const MString svp2BlinnShaderRegistrantId("vp2BlinnShaderRegistrantId");
// Note that we use the same drawdb classification for both registerNode()
// and registerShaderOverrideCreator() to associate the override with the Maya node.
//
MStatus initializePlugin( MObject obj )
{
TRACE_API_CALLS("initializePlugin");
MStatus status;
const MString& swatchName = MHWShaderSwatchGenerator::initialize();
const MString UserClassify( "shader/surface/utility/:drawdb/shader/surface/vp2BlinnShader:swatch/"+swatchName);
MFnPlugin plugin( obj, PLUGIN_COMPANY, "1.0", "Any");
status = plugin.registerNode( "vp2BlinnShader", vp2BlinnShader::id,
vp2BlinnShader::creator, vp2BlinnShader::initialize,
MPxNode::kHardwareShader, &UserClassify );
if (!status) {
status.perror("registerNode");
return status;
}
// Register a shader override for this node
MHWRender::MDrawRegistry::registerShaderOverrideCreator(
"drawdb/shader/surface/vp2BlinnShader",
svp2BlinnShaderRegistrantId,
vp2BlinnShaderOverride::Creator);
if (status != MS::kSuccess) return status;
return MS::kSuccess;
}
MStatus uninitializePlugin( MObject obj )
{
TRACE_API_CALLS("uninitializePlugin");
MStatus status;
MFnPlugin plugin( obj );
// Unregister the shader node
plugin.deregisterNode( vp2BlinnShader::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
// Deregister the shader override
status = MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
"drawdb/shader/surface/vp2BlinnShader", svp2BlinnShaderRegistrantId);
if (status != MS::kSuccess) return status;
MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer();
if (renderer)
{
const MHWRender::MShaderManager* shaderMgr = renderer ? renderer->getShaderManager() : NULL;
if (shaderMgr)
shaderMgr->clearEffectCache();
}
return MS::kSuccess;
}
The header code is as below
#ifndef _vp2BlinnShader
#define _vp2BlinnShader
//-
// ==========================================================================
// Copyright 2015 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
#include <maya/MPxHardwareShader.h>
#include <maya/MRenderProfile.h>
class vp2BlinnShader : public MPxHardwareShader
{
public:
vp2BlinnShader();
virtual ~vp2BlinnShader();
virtual MStatus compute( const MPlug&, MDataBlock& );
// VP1 profile. Just leave as unsupported hardware profile
// as sample code.
virtual const MRenderProfile & profile()
{
static MRenderProfile sProfile;
if(sProfile.numberOfRenderers() == 0)
sProfile.addRenderer(MRenderProfile::kMayaSoftware);
return sProfile;
}
// Swatch rendering. Called irregardless of VP1 or VP2.
//
virtual MStatus renderSwatchImage( MImage & image );
static void * creator();
static MStatus initialize();
static MTypeId id;
protected:
private:
// Attributes
static MObject aColor;
static MObject aTransparency;
static MObject aDiffuseColor;
static MObject aSpecularColor;
static MObject aNonTexturedColor;
static MObject aNonTexturedTransparency;
static MObject aAddFragment;
};
#endif /* _vp2BlinnShader */
let me know if you need more details.
Thanks
Dhiraj