- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Examples

# contrastShader.cpp

    //-
    // ===========================================================================
    // Copyright 2012 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.
    // ===========================================================================
    //+

    //
    //    Description    :    Contrast shader plugin
    //
    //    Notes        :     The contrast operation consists in
    //                redistributing the energy around the middle
    //                point. In other words: every intensity below
    //                0.5 is pushed towards black, and every
    //                intensity above is pushed towards white.
    //                The trick is to do that in a "smooth" way.
    //
    //        The way to do that is:
    //            On interval [0,0.5] apply a gamma to intensity
    //            On interval [0.5,1] apply an "inverted" gamma to intensity
    //
    //        That is:
    //            When I<0.5,    newI=0.5*(2*I)^contrast
    //            When I>=0.5,    newI=1-0.5*(2*(1-I))^contrast
    //
    //        This function is C1 on [0,1], and if contrast==1, it is the
    //        identity.
    //
    //        The bias value stems from: why 0.5 and not something else ?
    //        So, we use a function f to remap the [0,1] interval on itself
    //        with the constraint:
    //            f(0)=0
    //            f(bias)=0.5
    //            f(1)=1
    //        and we compose with the above function.
    //
    //        Of course, when bias=0.5, f *has* to be the identity, and of
    //        course, f *has* to be smooth.
    //        A good candidate is f(x)=x^alpha, with alpha=log(0.5)/log(bias)
    //

    #include <math.h>

    #include <maya/MPxNode.h>
    #include <maya/MIOStream.h>
    #include <maya/MString.h>
    #include <maya/MTypeId.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MDataHandle.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MFloatVector.h>
    #include <maya/MFnPlugin.h>
    #include <maya/MDrawRegistry.h>
    #include <maya/MPxShadingNodeOverride.h>
    #include <maya/MViewport2Renderer.h>
    #include <maya/MFragmentManager.h>

    //
    // Node declaration
    class Contrast:public MPxNode
    {
        public:
                        Contrast();
        virtual         ~Contrast();

        virtual MStatus compute( const MPlug&, MDataBlock& );
        virtual void    postConstructor();

        static void    *creator();
        static MStatus initialize();

        //  Id tag for use with binary file format
        static MTypeId id;

        private:

        // Input attributes
        static MObject aColor;
        static MObject aContrast;
        static MObject aBias;

        // Output attributes
        static MObject aOutColor;
    };

    //
    // Override declaration
    class ContrastOverride : public MHWRender::MPxShadingNodeOverride
    {
    public:
        static MHWRender::MPxShadingNodeOverride* creator(const MObject& obj);

        virtual ~ContrastOverride();

        virtual MHWRender::DrawAPI supportedDrawAPIs() const;

        virtual MString fragmentName() const;

    private:
        ContrastOverride(const MObject& obj);

        MString fFragmentName;
    };

    //
    // Node definition
    // static data
    MTypeId Contrast::id( 0x81008 );

    // Attributes
    MObject Contrast::aColor;
    MObject Contrast::aContrast;
    MObject Contrast::aBias;

    MObject Contrast::aOutColor;

    #define MAKE_INPUT(attr)                        \
        CHECK_MSTATUS(attr.setKeyable(true));       \
        CHECK_MSTATUS(attr.setStorable(true));      \
        CHECK_MSTATUS(attr.setReadable(true));      \
        CHECK_MSTATUS(attr.setWritable(true));

    #define MAKE_OUTPUT(attr)                       \
        CHECK_MSTATUS(attr.setKeyable(false));      \
        CHECK_MSTATUS(attr.setStorable(false));     \
        CHECK_MSTATUS(attr.setReadable(true));      \
        CHECK_MSTATUS(attr.setWritable(false));

    //
    // DESCRIPTION:
    void Contrast::postConstructor( )
    {
        setMPSafe(true);
    }

    //
    // DESCRIPTION:
    Contrast::Contrast()
    {
    }

    //
    // DESCRIPTION:
    Contrast::~Contrast()
    {
    }

    //
    // DESCRIPTION:
    void *Contrast::creator()
    {
        return new Contrast();
    }

    //
    // DESCRIPTION:
    MStatus Contrast::initialize()
    {
        MFnNumericAttribute    nAttr;

        // Create input attributes

        aColor = nAttr.createColor("inputColor", "ic");
        MAKE_INPUT(nAttr);

        aContrast = nAttr.createColor("contrast", "c");
        MAKE_INPUT(nAttr);
        CHECK_MSTATUS(nAttr.setDefault(1.f, 1.f, 1.f));

        aBias = nAttr.createColor("bias", "b");
        MAKE_INPUT(nAttr);
        CHECK_MSTATUS(nAttr.setDefault(.5f, .5f, .5f));

        // Create output attributes
        aOutColor = nAttr.createColor("outColor", "oc");
        MAKE_OUTPUT(nAttr);

        /* Plug inputs and outputs
        ---------------------------*/
        CHECK_MSTATUS(addAttribute(aColor));
        CHECK_MSTATUS(addAttribute(aContrast));
        CHECK_MSTATUS(addAttribute(aBias));
        CHECK_MSTATUS(addAttribute(aOutColor));

        /* Build dependancies
        ----------------------*/
        CHECK_MSTATUS(attributeAffects(aColor,    aOutColor));
        CHECK_MSTATUS(attributeAffects(aContrast, aOutColor));
        CHECK_MSTATUS(attributeAffects(aBias,     aOutColor));

        return MS::kSuccess;
    }

    //
    // DESCRIPTION:
    MStatus Contrast::compute(const MPlug& plug, MDataBlock& block)
    {
        // outColor or individial R, G, B channel
        if((plug != aOutColor) && (plug.parent() != aOutColor))
            return MS::kUnknownParameter;

        MFloatVector resultColor;
        MFloatVector & col = block.inputValue(aColor).asFloatVector();
        MFloatVector & cont = block.inputValue(aContrast).asFloatVector();
        MFloatVector & bias = block.inputValue(aBias).asFloatVector();

        float xp;
        float logp5= logf(0.5F);

        xp = powf(col[0], logp5/logf(bias[0]));
        resultColor[0] = xp<0.5F ?
            0.5F*powf(2.0F*xp, cont[0]) :
            1.0F-0.5F*powf(2.0F*(1.0F-xp), cont[0]);

        xp = powf(col[1], logp5/logf(bias[1]));
        resultColor[1] = xp<0.5F ?
            0.5F*powf(2.0F*xp, cont[1]) :
            1.0F-0.5F*powf(2.0F*(1.0F-xp), cont[1]);

        xp = powf(col[2], logp5/logf(bias[2]));
        resultColor[2] = xp<0.5F ?
            0.5F*powf(2.0F*xp, cont[2]) :
            1.0F-0.5F*powf(2.0F*(1.0F-xp), cont[2]);

        // set ouput color attribute
        MDataHandle outColorHandle = block.outputValue( aOutColor );
        MFloatVector& outColor = outColorHandle.asFloatVector();
        outColor = resultColor;
        outColorHandle.setClean();

        return MS::kSuccess;
    }

    //
    // Override definition
    MHWRender::MPxShadingNodeOverride* ContrastOverride::creator(
        const MObject& obj)
    {
        return new ContrastOverride(obj);
    }

    ContrastOverride::ContrastOverride(const MObject& obj)
    : MPxShadingNodeOverride(obj)
    , fFragmentName("")
    {
        // Define fragments needed for VP2 version of shader, this could also be
        // defined in a separate XML file
        //
        // Define the input and output parameter names to match the input and
        // output attribute names so that the values are automatically populated
        // on the shader.
        static const MString sFragmentName("contrastShaderPluginFragment");
        static const char* sFragmentBody =
            "<fragment uiName=\"contrastShaderPluginFragment\" name=\"contrastShaderPluginFragment\" type=\"plumbing\" class=\"ShadeFragment\" version=\"1.0\">"
            "   <description><![CDATA[Contrast utility fragment]]></description>"
            "   <properties>"
            "       <float3 name=\"inputColor\" />"
            "       <float3 name=\"contrast\" />"
            "       <float3 name=\"bias\" />"
            "   </properties>"
            "   <values>"
            "       <float3 name=\"inputColor\" value=\"0.5,0.5,0.5\" />"
            "       <float3 name=\"contrast\" value=\"1.0,1.0,1.0\" />"
            "       <float3 name=\"bias\" value=\"0.5,0.5,0.5\" />"
            "   </values>"
            "   <outputs>"
            "       <float3 name=\"outColor\" />"
            "   </outputs>"
            "   <implementation>"
            "   <implementation render=\"OGSRenderer\" language=\"Cg\" lang_version=\"2.1\">"
            "       <function_name val=\"contrastShaderPluginFragment\" />"
            "       <source><![CDATA["
            "float3 contrastShaderPluginFragment(float3 inputColor, float3 contrast, float3 bias) \n"
            "{ \n"
            "   const float logp5 = log(0.5f); \n"
            "   float3 result; \n"
            "   float xp = pow(inputColor.r, logp5/log(bias.r)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.r = 0.5f*pow(2.0f*xp, contrast.r); \n"
            "   } else { \n"
            "       result.r = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.r); \n"
            "   } \n"
            "   xp = pow(inputColor.g, logp5/log(bias.g)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.g = 0.5f*pow(2.0f*xp, contrast.g); \n"
            "   } else { \n"
            "       result.g = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.g); \n"
            "   } \n"
            "   xp = pow(inputColor.b, logp5/log(bias.b)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.b = 0.5f*pow(2.0f*xp, contrast.b); \n"
            "   } else { \n"
            "       result.b = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.b); \n"
            "   } \n"
            "   return result; \n"
            "} \n]]>"
            "       </source>"
            "   </implementation>"
            "   <implementation render=\"OGSRenderer\" language=\"HLSL\" lang_version=\"11.0\">"
            "       <function_name val=\"contrastShaderPluginFragment\" />"
            "       <source><![CDATA["
            "float3 contrastShaderPluginFragment(float3 inputColor, float3 contrast, float3 bias) \n"
            "{ \n"
            "   const float logp5 = log(0.5f); \n"
            "   float3 result; \n"
            "   float xp = pow(inputColor.r, logp5/log(bias.r)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.r = 0.5f*pow(2.0f*xp, contrast.r); \n"
            "   } else { \n"
            "       result.r = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.r); \n"
            "   } \n"
            "   xp = pow(inputColor.g, logp5/log(bias.g)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.g = 0.5f*pow(2.0f*xp, contrast.g); \n"
            "   } else { \n"
            "       result.g = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.g); \n"
            "   } \n"
            "   xp = pow(inputColor.b, logp5/log(bias.b)); \n"
            "   if (xp < 0.5f) { \n"
            "       result.b = 0.5f*pow(2.0f*xp, contrast.b); \n"
            "   } else { \n"
            "       result.b = 1.0f - 0.5f*pow(2.0f*(1.0f-xp), contrast.b); \n"
            "   } \n"
            "   return result; \n"
            "} \n]]>"
            "       </source>"
            "   </implementation>"
            "   </implementation>"
            "</fragment>";

        // Register fragments with the manager if needed
        MHWRender::MRenderer* theRenderer = MHWRender::MRenderer::theRenderer();
        if (theRenderer)
        {
            MHWRender::MFragmentManager* fragmentMgr =
                theRenderer->getFragmentManager();
            if (fragmentMgr)
            {
                // Add fragments if needed
                bool fragAdded = fragmentMgr->hasFragment(sFragmentName);
                if (!fragAdded)
                {
                    fragAdded = (sFragmentName == fragmentMgr->addShadeFragmentFromBuffer(sFragmentBody, false));
                }

                // Use the fragment on successful add
                if (fragAdded)
                {
                    fFragmentName = sFragmentName;
                }
            }
        }
    }

    ContrastOverride::~ContrastOverride()
    {
    }

    MHWRender::DrawAPI ContrastOverride::supportedDrawAPIs() const
    {
        return MHWRender::kOpenGL | MHWRender::kDirectX11;
    }

    MString ContrastOverride::fragmentName() const
    {
        return fFragmentName;
    }

    //
    // Plugin setup
    static const MString sRegistrantId("contrastShaderPlugin");

    MStatus initializePlugin( MObject obj )
    {
        const MString UserClassify( "utility/color:drawdb/shader/operation/contrastNode" );

        MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");
        CHECK_MSTATUS( plugin.registerNode("contrastNode", Contrast::id,
                            Contrast::creator, Contrast::initialize,
                            MPxNode::kDependNode, &UserClassify ) );

        CHECK_MSTATUS(
            MHWRender::MDrawRegistry::registerShadingNodeOverrideCreator(
                "drawdb/shader/operation/contrastNode",
                sRegistrantId,
                ContrastOverride::creator));

        return MS::kSuccess;
    }

    MStatus uninitializePlugin( MObject obj )
    {
        MFnPlugin plugin( obj );
        CHECK_MSTATUS( plugin.deregisterNode( Contrast::id ) );

        CHECK_MSTATUS(
            MHWRender::MDrawRegistry::deregisterShadingNodeOverrideCreator(
                "drawdb/shader/operation/contrastNode",
                sRegistrantId));

        return MS::kSuccess;
    }