API ガイド > ハードウェア シェーディング ノードを作成する >

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

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

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

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

    MStatus initializePlugin( MObject obj )
    {
        MStatus status;
        const MString&
        swatchName =	MHWShaderSwatchGenerator::initialize();
        const MString UserClassify( "shader/surface/utility/:swatch/"+swatchName );
        MFnPlugin plugin( obj, "Autodesk", "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);
        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");
        return MS::kSuccess;
    }

ノードの初期化

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

    MStatus hwPhongShader::initialize()
    {
        MFnNumericAttribute nAttr;
        // 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 );

        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 );

        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 );

        // This is defined as a point, so that users can easily enter
        // values beyond 1.
        aShininess = nAttr.createPoint( "shininess", "sh" );
        nAttr.setStorable(true);
        nAttr.setKeyable(true);
        nAttr.setDefault(100.0f, 100.0f, 100.0f);
        nAttr.setCached( true );
        nAttr.setInternal( true );

        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(aDiffuseColor);
        addAttribute(aSpecularColor);
        addAttribute(aShininess);
        addAttribute(aGeometryShape);

        attributeAffects (aColor,			outColor);
        attributeAffects (aDiffuseColor,	outColor);
        attributeAffects (aSpecularColor,	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;
    }

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

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

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 );
        }
    }