Monday, August 1, 2011

Maya Viewport 2.0 - HwPhongShader

HwPhongShader 這個範例新增了 Viewport 2.0 的支援,最主要的功能都是來於 MHWRender::MPxShaderOverride 的實作,MPxShaderOverride 可以讓使用者針對某一個 node 來自訂 render 的方式,輸入的資料有 geometry, textures 和 lights,我們可以將 MPxShaderOverride想像為一個 CgFx 或是 HLSL effect 檔的專用 renderer 根據 Maya 場景中所提供的資料來繪出物件。

整個繪製的流程可分為三個大階段
1. Initialization phase:
  • 當一個 shader 被指定到物件上或是 shader 的資料被更改的時候,就需要透過  addGeometryRequirement() 這個函式來重新建立 shader 會用到的 geometry stream requirements。
  • initialize() 一定要回傳一個 shader key 的字串,用來分別同一份 MPxShaderOverride 但是可以有很多的分身,每一個分身都可以有自已的參數,Maya 會將相同 shader key 的物件分到同一個群組中,所以只需做一次初始化的動作也可以將多個使用同樣 shader key 的物件做一次繪出的動作來增進效能。

2. Data update phase:
    資料更新也可以分三個階段
  • updateDG() : 將 input node 的 attribute 暫存起來
  • updateDevice() : 將暫存起來的資料送到 graphics device
  • endUpdate() : 清除暫存的資料
3. Draw phase:
  • 真正執行繪圖動作的指令都是做在這個函式中,回傳值是 true 的話就是繪圖成功,如果回傳 false,則 Maya 會使用預設的方法來畫這個物件。雖然說 shader 的設定和 render 的動作理論上可以在 draw() 的函式中一起作完,但 Maya 還是建議在 draw() 只做 shader 的設定,而在 drawGeometry() 再做最後 render 的動作。
  • activateKey 和 terminateKey 可以用來處理擁有多個 shader key ( multi-pass rendering) 的 render item,由於有相同 shader key 的 shader 只需處理一次,所需的 render state 切換也只需要做一次,多個物件多個 shader 也不會造成額外的負擔。

以上是在繼承 MPxShaderOverride 時一定需要實作的函式,這樣一來 Maya 才知道要如何處理使用者自定的 MPxShaderOverride,我們以 devkit 中的 HwPhongShader 範例來看看要怎樣使用 MPxShaderOverride。

首先,在 registerNode 指定這個 phongShaderNode 要用什麼方法來畫,
const MString UserClassify( "shader/surface/utility/:drawdb/shader/surface/hwPhongShader:swatch/"+swatchName );

MFnPlugin plugin( obj, PLUGIN_COMPANY, "4.5", "Any");
status = plugin.registerNode( ... &UserClassify );

接下來就是註冊 phongShaderOverride
// Register a shader override for this node
MHWRender::MDrawRegistry::registerShaderOverrideCreator(
    "drawdb/shader/surface/hwPhongShader",
    sHWPhongShaderRegistrantId,
    hwPhongShaderOverride::Creator);

在 hwPhongShader::initialize(),設定這個 hwPhongShader 的屬性
aColor = nAttr.createColor( "color", "c");
aTransparency = nAttr.create( "transparency", "tr",     MFnNumericData::kFloat );
aDiffuseColor = nAttr.createColor( "diffuseColor", "dc" );
aSpecularColor = nAttr.createColor( "specularColor", "sc" );
aShininess = nAttr.createPoint( "shininess", "sh" );

當切換到 Viewport 2.0 時,第一個進入的函式就是
MString hwPhongShaderOverride::initialize(MObject object)
MString empty;
addGeometryRequirement(
   MHWRender::MVertexBufferDescriptor(
   empty,
   MHWRender::MGeometry::kPosition,
   MHWRender::MGeometry::kFloat,
   3));
addGeometryRequirement(
   MHWRender::MVertexBufferDescriptor(
   empty,
   MHWRender::MGeometry::kNormal,
   MHWRender::MGeometry::kFloat,
   3));
設定畫 geometry 時所需的資料 ( Position 和 Normal )
MString hwPhongShaderOverride::updateDG(MObject object)
{
    // Get the hardware shader node from the MObject.
    fShaderNode = (hwPhongShader *)       MPxHwShaderNode::getHwShaderNodePtr( object );
    if (fShaderNode)
        fTransparency = fShaderNode->Transparency();
    else
        fTransparency = 0.0f;
}
在 updateDG 中從 node 中更新了 fTransparency 的屬性資料

在 draw phase 中,我覺得最有用的就是 customDraw 和 printContextInformation 這二個 debug 用的函式,customDraw基本上是用來模擬
MHWRender::MPxShaderOverride::drawGeometry(context);
所以透過這個函式我們就可以一窺 Maya 內部是如何實作 drawGeometry,至於 printContextInformation 是用來秀出 MDrawContext 的資訊,在 customDraw 的函式裡,可以看到如何拿到每一個  render item 的 geometry 資料,如何得到每一個 geometry 的 vertex 和 index buffer 的描述和內部資料,如何將 geometry 的資料 bind 到 OpenGL 到最後呼叫 glDrawElements 畫出這個 geometry,我個人是覺得在學習上非常的有幫助。

No comments: