ハードウェア シェーディング ノード プラグインのサンプル

hwPhongShader ノード プラグインは、Maya Developer Kit で提供されています。このサンプルでは、立方体環境マップを使用してピクセルごとの Phong シェーディングを行います。ライトの方向は現在視点の位置に固定されています。

注:このサンプルで使用されているノード ベースのインタフェースは、ビューポート 2.0 インタフェースのサブセットのみを含んでいます。このインタフェースに関する詳細については、「エフェクトのオーバーライド」を参照してください。

プラグインの初期化と初期化解除

プラグインを初期化する手順は簡単です。初期化では、スウォッチ分類文字列が作成され、ノードとドラッグ & ドロップ動作のクラスが登録されます。ドラッグ & ドロップ動作には固有の MTypeId は必要ないことに注意してください。この動作を示す簡単なテキスト文字列で十分です。

MStatus initializePlugin( MObject obj )
{
    MStatus   status;

    const MString& swatchName =	MHWShaderSwatchGenerator::initialize();
    const MString UserClassify( "shader/surface/utility/:drawdb/shader/surface/hwPhongShader:swatch/"+swatchName );

    MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");
    status = plugin.registerNode( "hwPhongShader", hwPhongShader::id,
                                   hwPhongShader::creator, hwPhongShader::initialize,
                                   MPxNode::kHwShaderNode, &UserClassify );
    if (!status) {
        status.perror("registerNode");
        return status;
    }

    plugin.registerDragAndDropBehavior("hwPhongShaderBehavior",
                                        hwPhongShaderBehavior::creator);

    // Register a shader override for this node
    MHWRender::MDrawRegistry::registerShaderOverrideCreator(
        "drawdb/shader/surface/hwPhongShader",
        sHWPhongShaderRegistrantId,
        hwPhongShaderOverride::Creator);
    if (status != MS::kSuccess) return status;

    return MS::kSuccess;
}

プラグインの初期化を解除すると、Phong マテリアル ノードとドラッグ & ドロップ動作の登録が解除されます。

MStatus uninitializePlugin( MObject obj )
{
    MStatus   status;

    MFnPlugin plugin( obj );

    // Unregister all chamelion shader nodes
    plugin.deregisterNode( hwPhongShader::id );
    if (!status) {
        status.perror("deregisterNode");
        return status;
     }

     plugin.deregisterDragAndDropBehavior("hwPhongShaderBehavior");

     // Deregister the shader override
     status = MHWRender::MDrawRegistry::deregisterShaderOverrideCreator(
		       "drawdb/shader/surface/hwPhongShader", sHWPhongShaderRegistrantId);
     if (status != MS::kSuccess) return status;

     return MS::kSuccess;
}

ノードの初期化

ディペンデンシー グラフ(Dependency Graph)で使用されるハードウェア シェーダ プラグインの initialize() メソッドのアトリビュートは事前に設定されています。標準的なカラー アトリビュートは、その機能に応じたプロパティを使用して追加され、フラグされます。最初にアトリビュートが作成され、次にアトリビュートが追加され、その後アトリビュート間のリレーションシップが設定されます。次の例では、パフォーマンスを向上させるためにアトリビュートを内部的にキャッシュするように設定します。

MStatus hwPhongShader::initialize()
{
   // Make sure that all attributes are cached internal for
   // optimal performance !

   MFnNumericAttribute nAttr;
   MFnCompoundAttribute cAttr;

   // Create input attributes
   aColor = nAttr.createColor( "color", "c");
   nAttr.setStorable(true);
   nAttr.setKeyable(true);
   nAttr.setDefault(0.1f, 0.1f, 0.1f);
   nAttr.setCached( true );
   nAttr.setInternal( true );
   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.setCached( true );
   nAttr.setInternal( true );
   nAttr.setAffectsAppearance( true );

   aDiffuseColor = nAttr.createColor( "diffuseColor", "dc" );
   nAttr.setStorable(true);
   nAttr.setKeyable(true);
   nAttr.setDefault(1.f, 0.5f, 0.5f);
   nAttr.setCached( true );
   nAttr.setInternal( true );
   nAttr.setAffectsAppearance( true );

   aSpecularColor = nAttr.createColor( "specularColor", "sc" );
   nAttr.setStorable(true);
   nAttr.setKeyable(true);
   nAttr.setDefault(0.5f, 0.5f, 0.5f);
   nAttr.setCached( true );
   nAttr.setInternal( true );
   nAttr.setAffectsAppearance( true );

   // This is defined as a compound attribute, users can easily enter
   // values beyond 1.
   aShininessX = nAttr.create( "shininessX", "shx", MFnNumericData::kFloat, 100.0 );
   aShininessY = nAttr.create( "shininessY", "shy", MFnNumericData::kFloat, 100.0 );
   aShininessZ = nAttr.create( "shininessZ", "shz", MFnNumericData::kFloat, 100.0 );
   aShininess = cAttr.create( "shininess", "sh" );
   cAttr.addChild(aShininessX);
   cAttr.addChild(aShininessY);
   cAttr.addChild(aShininessZ) ;
   nAttr.setStorable(true);
   nAttr.setKeyable(true);
   nAttr.setCached( true );
   nAttr.setInternal( true );
   nAttr.setAffectsAppearance(true);	
   cAttr.setHidden(false);
	
   aGeometryShape = nAttr.create( "geometryShape", "gs", MFnNumericData::kInt );
   nAttr.setStorable(true);
   nAttr.setKeyable(true);
   nAttr.setDefault(0);
   nAttr.setCached( true );
   nAttr.setInternal( 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 here

   addAttribute(aColor);
   addAttribute(aTransparency);
   addAttribute(aDiffuseColor);
   addAttribute(aSpecularColor);
   addAttribute(aShininess);
   addAttribute(aGeometryShape);

   attributeAffects (aColor,			outColor);
   attributeAffects (aTransparency,	outColor);
   attributeAffects (aDiffuseColor,	outColor);
   attributeAffects (aSpecularColor,	outColor);
   attributeAffects (aShininessX,		outColor);
   attributeAffects (aShininessY,		outColor);
   attributeAffects (aShininessZ,		outColor);
   attributeAffects (aShininess,		outColor);

   return MS::kSuccess;
}

compute メソッド

次に、このクラスでの compute() の簡単な使用例について説明します。このメソッドを設定するのは、outColor アトリビュートを処理する場合だけです。このメソッドに他のアトリビュートを渡すと MS::kUnknownParameter が返されます。この場合、Maya がこれらのアトリビュートを処理します。それ以外の場合には、データ ブロックからアトリビュート aDiffuseColor がアクセスされ、この値を使って出力カラーが設定されます。

MStatus hwPhongShader::compute(
const MPlug&      plug,
   MDataBlock& block )
{

   if ((plug != outColor) && (plug.parent() != outColor))
      return MS::kUnknownParameter;

   MFloatVector & color  = block.inputValue( aDiffuseColor ).asFloatVector();

   // set output color attribute
   MDataHandle outColorHandle = block.outputValue( outColor );
   MFloatVector& outColor = outColorHandle.asFloatVector();
   outColor = color;

   outColorHandle.setClean();
   return MS::kSuccess;
}

このメソッドを実行すると、ハードウェア シェーディング ノード プラグインがソフトウェア レンダーで可視になります。ソフトウェア レンダーが必要ない場合、このメソッドを実行する必要はありません。

旧式の既定ビューポートのリファレンス情報

このセクションには、旧式の既定ビューポートでレンダリングする際のハードウェア シェーダのリファレンス情報が含まれています。現在はビューポート 2.0 で使用するシェーダを作成することをお勧めします。「Maya ビューポート 2.0 API ガイド」を参照してください。

バインド、描画、バインド解除のメソッド

bind() メソッドと glBind() メソッドは、同じ方法を使用します。必要なアトリビュートが変更されるか、Phong テクスチャが設定されると、Phong テクスチャが初期化されます。

MStatus	hwPhongShader::bind(const MDrawRequest& request, M3dView& view)

{
    if (mAttributesChanged || (phong_map_id == 0))
    {
        init_Phong_texture ();
    }
    return MS::kSuccess;
}


MStatus	hwPhongShader::glBind(const MDagPath&)
{
    if ( mAttributesChanged || (phong_map_id == 0))
    {
        init_Phong_texture ();
    }

    return MS::kSuccess;
}
        

このサンプルの unbind() メソッドと glUnbind() メソッドは、MS::kSuccess だけを返します。これは、リソースの解放に関する代替方法が記述されているためです。hwPhongShader の例では、API を使用してメッセージが確認され、新規ファイルの作成、ファイルのオープン、ファイルの参照などのイベントの前にリソースが解放されます。詳細については、完全なサンプル コードを参照してください。

MStatus	hwPhongShader::unbind(const MDrawRequest& request, M3dView& view)
{
    // The texture may have been allocated by the draw; it's kept
    // around for use again. When scene new or open is performed this
    // texture will be released in releaseEverything().
    return MS::kSuccess;
}

MStatus	hwPhongShader::glUnbind(const MDagPath&)
{
    // The texture may have been allocated by the draw; it's kept
    // around for use again. When scene new or open is performed this
    // texture will be released in releaseEverything().
    return MS::kSuccess;
}

geometry()glGeometry() メソッドは同じように設定されています。これらは、それぞれインタフェースに依存しない draw() メソッドをコールして、描画コードの重複を回避します。

MStatus	hwPhongShader::geometry( const MDrawRequest& request,
                                M3dView& view,
                                int prim,
                                unsigned int writable,
                                int indexCount,
                                const unsigned int * indexArray,
                                int vertexCount,
                                const int * vertexIDs,
                                const float * vertexArray,
                                int normalCount,
                                const float ** normalArrays,
                                int colorCount,
                                const float ** colorArrays,
                                int texCoordCount,
                                const float ** texCoordArrays)
{
    MStatus stat = MStatus::kSuccess;
    if (mGeometryShape != 0)
        drawDefaultGeometry();
    else
        stat = draw( prim, writable, indexCount, indexArray, vertexCount,
        vertexIDs, vertexArray, normalCount, normalArrays,
        colorCount, colorArrays, texCoordCount, texCoordArrays);
    return stat;
}

MStatus	hwPhongShader::glGeometry(const MDagPath & path,
                                  int prim,
                                  unsigned int writable,
                                  int indexCount,
                                  const unsigned int * indexArray,
                                  int vertexCount,
                                  const int * vertexIDs,
                                  const float * vertexArray,
                                  int normalCount,
                                  const float ** normalArrays,
                                  int colorCount,
                                  const float ** colorArrays,
                                  int texCoordCount,
                                  const float ** texCoordArrays)
{
    MStatus stat = MStatus::kSuccess;
    if (mGeometryShape != 0)
        drawDefaultGeometry();
    else
        stat = draw( prim, writable, indexCount, indexArray, vertexCount,
        vertexIDs, vertexArray, normalCount, normalArrays,
        colorCount, colorArrays, texCoordCount, texCoordArrays);
    return stat;
}
        

描画

draw() メソッドのパラメータは、情報を画面にレンダーする OpenGL に情報を渡す場合に使用します。他の配列から情報にアクセスする場合には、indexArray を使うことに注意してください。

MStatus	hwPhongShader::draw(int prim,
                            unsigned int writable,
                            int indexCount,
                            const unsigned int * indexArray,
                            int vertexCount,
                            const int * vertexIDs,
                            const float * vertexArray,
                            int normalCount,
                            const float ** normalArrays,
                            int colorCount,
                            const float ** colorArrays,
                            int texCoordCount,
                            const float ** texCoordArrays)
{
    if ( prim != GL_TRIANGLES && prim != GL_TRIANGLE_STRIP)	{
        return MS::kFailure;
    }

    {
        glPushAttrib ( GL_ENABLE_BIT );
        glDisable ( GL_LIGHTING );
        glDisable ( GL_TEXTURE_1D );
        glDisable ( GL_TEXTURE_2D );

        // Setup cube map generation
        glEnable ( GL_TEXTURE_CUBE_MAP_EXT );
        glBindTexture ( GL_TEXTURE_CUBE_MAP_EXT, phong_map_id );
        glEnable ( GL_TEXTURE_GEN_S );
        glEnable ( GL_TEXTURE_GEN_T );
        glEnable ( GL_TEXTURE_GEN_R );
        glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

        // Could modify the texture matrix here to do light tracking...
        glMatrixMode ( GL_TEXTURE );
        glPushMatrix ();
        glLoadIdentity ();
        glMatrixMode ( GL_MODELVIEW );
    }

        // Draw the surface.
        //
    {
        glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );
        glEnableClientState( GL_VERTEX_ARRAY );
        glEnableClientState( GL_NORMAL_ARRAY );

        glVertexPointer ( 3, GL_FLOAT, 0, &vertexArray[0] );
        glNormalPointer ( GL_FLOAT, 0, &normalArrays[0][0] );

        glDrawElements ( prim, indexCount, GL_UNSIGNED_INT, indexArray );

        // The client attribute is already being popped. You
        glPopClientAttrib();
    }

    {
        glMatrixMode ( GL_TEXTURE );
        glPopMatrix ();
        glMatrixMode ( GL_MODELVIEW );

        glDisable ( GL_TEXTURE_CUBE_MAP_EXT );
        glDisable ( GL_TEXTURE_GEN_S );
        glDisable ( GL_TEXTURE_GEN_T );
        glDisable ( GL_TEXTURE_GEN_R );

        glPopAttrib();
    }

        return MS::kSuccess;
}

スウォッチの描画

スウォッチを描画する場合には、renderSwatchImage() 仮想メソッドを実行し、MHardwareRenderer クラスを MGeometryData と OpenGL と組み合わせて使用してイメージを描画します。renderSwatchImage() に渡される MImage には、求められる出力と高さに関する情報が含まれています。

MStatus hwPhongShader::renderSwatchImage( MImage & outImage )
{
    MStatus status = MStatus::kFailure;

    // Get the hardware renderer utility class
    MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
    if (pRenderer)
    {
        const MString& backEndStr = pRenderer->backEndString();

        // Get geometry
        // ============
        unsigned int* pIndexing = 0;
        unsigned int numberOfData = 0;
        unsigned int indexCount = 0;
        MHardwareRenderer::GeometricShape gshape =
            MHardwareRenderer::kDefaultSphere;
        if (mGeometryShape == 2)
        {
            gshape = MHardwareRenderer::kDefaultCube;
        }
        else if (mGeometryShape == 3)
        {
            gshape = MHardwareRenderer::kDefaultPlane;
        }
        MGeometryData* pGeomData =
            pRenderer->referenceDefaultGeometry( gshape, numberOfData, pIndexing, indexCount );
        if( !pGeomData )
        {
            return MStatus::kFailure;
        }

        // Make the swatch context current
        // ===============================
        //
        unsigned int width, height;
        outImage.getSize( width, height );
        unsigned int origWidth = width;
        unsigned int origHeight = height;

        MStatus status2 = pRenderer->makeSwatchContextCurrent( backEndStr,
            width, height );

        if( status2 == MS::kSuccess )
        {
            // NOTE: Must be called after makeSwatchContextCurrent()
            glPushAttrib ( GL_ALL_ATTRIB_BITS );

            // Get camera
            // ==========
            {
                // Get the camera frustum from the API
                double l, r, b, t, n, f;
                pRenderer->getSwatchOrthoCameraSetting( l, r, b, t, n, f );

                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                glOrtho( l, r, b, t, n, f );
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                // Rotate the cube a bit so we don't see it head on
                if (gshape == MHardwareRenderer::kDefaultCube)
                    glRotatef( 45, 1.0, 1.0, 1.0 );
                else if (gshape == MHardwareRenderer::kDefaultPlane)
                    glScalef( 1.5, 1.5, 1.5 );
            				else
                                glScalef( 1.0, 1.0, 1.0 );
            }

                // Draw The Swatch
                // ===============
                drawTheSwatch( pGeomData, pIndexing, numberOfData, indexCount );

                // Read pixels back from swatch context to MImage
                // ==============================================
                pRenderer->readSwatchContextPixels( backEndStr, outImage );

                // Double check the outing going image size as image resizing
                // was required to properly read from the swatch context
                outImage.getSize( width, height );
                if (width != origWidth || height != origHeight)
                {
                    status = MStatus::kFailure;
                }
                else
                {
                    status = MStatus::kSuccess;
                }

                glPopAttrib();
        }
        else
        {
            pRenderer->dereferenceGeometry( pGeomData, numberOfData );
        }
    }
    return status;
}

drawTheSwatch() メソッドは、 renderSwatchImage() メソッドからコールされます。このメソッドは、イメージに対して OpenGL 描画を実行します。

void			
hwPhongShader::drawTheSwatch( MGeometryData* pGeomData,
                             unsigned int* pIndexing,
                             unsigned int numberOfData,
                             unsigned int indexCount )
{
    MHardwareRenderer *pRenderer = MHardwareRenderer::theRenderer();
    if( !pRenderer )	return;
    if ( mAttributesChanged || (phong_map_id == 0))
    {
        init_Phong_texture ();
    }

    // Get the default background color
    float r, g, b, a;
    MHWShaderSwatchGenerator::getSwatchBackgroundColor( r, g, b, a );
    glClearColor( r, g, b, a );
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    glDisable ( GL_LIGHTING );
    glDisable ( GL_TEXTURE_1D );
    glDisable ( GL_TEXTURE_2D );
    {
        glEnable ( GL_TEXTURE_CUBE_MAP_EXT );
        glBindTexture ( GL_TEXTURE_CUBE_MAP_EXT, phong_map_id );
        glEnable ( GL_TEXTURE_GEN_S );
        glEnable ( GL_TEXTURE_GEN_T );
        glEnable ( GL_TEXTURE_GEN_R );
        glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_EXT );
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_CUBE_MAP_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );

        // Could modify the texture matrix here to do light tracking...
        glMatrixMode ( GL_TEXTURE );
        glPushMatrix ();
        glLoadIdentity ();
        glRotatef( 5.0, -1.0, 0.0, 0.0 );
        glRotatef( 10.0, 0.0, 1.0, 0.0 );
        glMatrixMode ( GL_MODELVIEW );
    }

        // Draw default geometry
    {
        if (pGeomData)
        {
            glPushClientAttrib ( GL_CLIENT_VERTEX_ARRAY_BIT );

            float *vertexData = (float *)( pGeomData[0].data() );
            if (vertexData)
            {
                glEnableClientState( GL_VERTEX_ARRAY );
                glVertexPointer ( 3, GL_FLOAT, 0, vertexData );
            }
            float *normalData = (float *)( pGeomData[1].data() );
            if (normalData)
            {
                glEnableClientState( GL_NORMAL_ARRAY );
                glNormalPointer ( GL_FLOAT, 0, normalData );
            }

            if (vertexData && normalData && pIndexing )
                glDrawElements ( GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, pIndexing );

            glPopClientAttrib();

            // Release data references
            pRenderer->dereferenceGeometry( pGeomData, numberOfData );
        }
    }

    {
        glMatrixMode ( GL_TEXTURE );
        glPopMatrix ();
        glMatrixMode ( GL_MODELVIEW );
        glDisable ( GL_TEXTURE_CUBE_MAP_EXT );
        glDisable ( GL_TEXTURE_GEN_S );
        glDisable ( GL_TEXTURE_GEN_T );
        glDisable ( GL_TEXTURE_GEN_R );
    }
}