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() の簡単な使用例について説明します。このメソッドを設定するのは、outColor アトリビュートを処理する場合だけです。このメソッドを設定するのは、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 他の配列から情報にアクセスする場合には、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 );
}
}