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

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 アトリビュートを処理する場合だけです。このメソッドを設定するのは、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 );
	}
}