オブジェクトのオーバーライド用にカスタムのレンダリング可能項目を処理する

このセクションでは、ビューポート 1.0 (既定のビューポート)の MPxSurfaceShapeUI インタフェースと MPxSurfaceShape インタフェース、およびコンセプトのビューポート 2.0 インタフェースへのマッピングに焦点を当てています。MPxGeometryOverride および MPxSubSceneOverride に焦点が当てられ、この 2 つのインタフェース間のアプローチにおける重要な相違点がハイライトされています。

主な目的は、カスタムのレンダリング可能項目を「カスタム」描画にインスタンス化する方法を示すことです。

ビューポート 1.0 のみのインタフェース

ビューポート 2.0 API では、レンダリング/描画インタフェースのみが提供されます。

既に存在するビューポート 1.0 インタフェースが、選択とコンポーネントの処理に使用されます。これには MPxSurfaceShapeUI::select()MPxSurfaceShapeUI::snap() などのメソッドが含まれています。

UV エディタへの描画は変更されません。既存のビューポート 1.0 の方法を使用する必要がありますが、ビューポート 2.0 には UV エディタでの描画を行う特定のインタフェースがないため、MPxSurfaceShapeUI::drawUV()/MPxSurfaceShapeUI::canDrawUV() を使用します。

関連するマテリアルをオーバーライドするビューポート 1.0 の方法は、ビューポート 2.0 のレンダリングには必要ありません。これには、MPxSurfaceShapeUI::material() および MPxSurfaceShapeUI::materials() が含まれます。

レンダリング可能項目(レンダー項目)

レンダリング可能項目は主要な要素です。

ビューポート 1.0 では、レンダリング可能項目は描画要求(MDrawRequest)として表されます。ビューポート 2.0 では、これに最も近いコンストラクトはレンダー項目(MRenderItem)です。これらの集合が、オブジェクトのレンダリング可能項目の合計セットを表すものと考えられます。

ビューポート 1.0 では、定義済みの MDrawRequests はありません。

ビューポート 2.0 では、オブジェクトに対して割り当てられたマテリアルを使用する描画を表す MRenderItem が提供されます。これらのマテリアルをレンダー項目と呼びます。 レンダラによって事前定義されていない項目は、「カスタム」と見なされます。

ビューポート 1.0 の手法では、描画要求を作成して適切なコレクションに返し、描画時に要求を明示的に描画します。ビューポート 2.0 の手法では、既存のレンダー項目を作成、更新、または選択的にオーバーライドして、ジオメトリ更新と最終的な内部描画に適したコレクションに返します。

レンダー項目には永続的な性質があるため、使用する予定のレンダー項目全体を事前に作成しておくことを検討する必要があります。

提供されるレンダー項目

ビューポート 2.0 レンダラは、マテリアルおよびバウンディング ボックスのレンダー項目を保持します。

マテリアル レンダー項目に使用されるシェーダをオーバーライドすることは可能ですが、必須ではありません。これらのレンダー項目を無効にして、カスタム レンダー項目に置き換えることもできます。

シェーダの割り当てがまったくないオブジェクトの処理は、プラグインに委ねられることに注意してください。ビューポート 1.0 では、マテリアル インタフェースが既定のマテリアルを返します。ビューポート 2.0 では、プラグインでないオブジェクトのフォールバックとしてワイヤフレームが表示されます。既定値のワイヤフレーム レンダー項目を提供できないため、これ(または、フォールバックとして適切なもの)をプラグインによって明示的に提供する必要があります。

カスタム レンダー項目

マテリアルとバウンディング ボックスのレンダー項目のみが提供される場合、追加の「カスタム」レンダー項目がいつ必要になるのかについての疑問が発生します。 これは、書かれているプラグインの種類に大きく依存しますが、1 つの共通する状況は非マテリアル レンダー項目のレンダリング、もう1つは既存のマテリアルまたはバウンディング ボックスの描画の上書きです。

非マテリアルのレンダー項目

一般的に、カスタム シェイプの場合は、ワイヤフレーム表示モードの描画のための最小限のサポートの実装が必要です。プラグインおよび描画する必要がある追加の UI 要素のタイプによって、追加されるものが異なります。

経験上、レンダリングの方法とタイミングが変わることで、新たにレンダー項目 インスタンスが生成されます。 MRenderItem のスキャンは可能なバリエーションを示します。

以下に、主要なバリアントの一部を示します。

  1. レンダー項目は表示モードに固有である(例: ワイヤフレームとシェーディング/テクスチャ モード)表示モードはレンダリングに使用される現在の表示モードと照合され、必要に応じて適切にフィルタされることに注意してください。例: ビューポートがワイヤフレーム モードの場合、シェーディング モード項目は描画されません。

図 78

描画モードのフィルタリングによって、描画フェーズに進む過程で項目がどのように除去されるかを示す基本的なパイプライン。 通常これは、項目がシェーディングされているかいないかに基づいたフィルタよりも優先されます。

  1. プリミティブ タイプで異なる必要がある。たとえば、あるレンダーはライン、別のレンダーはポイントを描画します。
  2. シェーダ インスタンス(MShaderInstance)で異なる必要がある。たとえば、点線のシェーダと実線のシェーダには異なるレンダー項目が必要です。 詳細については、「4.4.4.3 シェーダおよびシェーダ インスタンスの選択」を参照してください。
  3. シェーダのパラメータ値で異なる必要がある。たとえば、2 つのライン シェーダ インスタンスは異なるカラーを持つ必要があります。 詳細については、「4.4.4.3 シェーダおよびシェーダ インスタンスの選択」を参照してください。
  4. 深度の優先順位で異なる必要がある。

図 79

アクティブ カメラに向かってレンダー項目を移動させる深度の優先順位設定の違いとその効果を論理的に示す図。 ストックの優先順位に代わり、「カスタム」の優先順位を使用することができます。 矢印は、カメラ(ビューア)の方向を示します。

  1. シェーダによって必要となるジオメトリ データのストリームで異なる必要がある。バリエーションには、ストリームの数およびストリームの説明が含まれています。

レンダー項目 セットの事前計画

プラグイン オブジェクトとのインタラクション中に使用する可能性のあるすべてのレンダー項目について事前に計画を立てることを推奨します。 この項目の作業用セットは、シェーダ、テクスチャおよびジオメトリ、および可能な再使用の量など、必要な依存関係のあるリソースを判断するのに役立ちます。

一般に、レンダー項目 セットは固定されていますが、プラグインの存続期間中にパラメータが変更されたり、各種の項目について有効化と無効化が切り替わることがあります。

バリエーションを作成し、一度に有効にするバリエーションの数を制限していれば、レンダー項目の数が多くてもパフォーマンスには影響しません。 同じバリエーションが大量に必要な場合は、プラグインを作成するときに MPxGeometryOverride ではなく MPxSubSceneOverride を使用することを検討してください。

UI 描画の場合、上書きコストを回避するためにオーバーラップする要素の数を最小限に抑えることをお勧めします。 この問題を回避することができない場合、同時に有効にできる要素の数を決定することが最良の方法です。 これによって、深度の優先度が異なるレンダー項目のバリアントがいくつ必要になるかをコントロールすることができます。 たとえば、休止状態のライン コンポーネント上にアクティブなライン コンポーネントを描画する場合、ワイヤフレーム上には 3 つのレンダー項目が必要ですが、これらの項目は深度の優先順位を除いてまったく同じ場合もあります。

DAG オブジェクトのインスタンスごとにレンダー項目が必要となる場合は、インスタンスごとに異なるレンダー項目を作成できるということを覚えておくと便利です。 項目とインスタンス間の明示的な関連を保持しておくかどうかは、プラグイン作成者の判断によります。

シェーダおよびシェーダ インスタンスの選択

レンダー項目のシェーダを選択するオプションにはさまざまな複雑さがあり、その用途も異なります。 選択したオプションに関係なく、ジオメトリの処理からシェーダを分離することが最善です。

最も単純なオプションは、MShaderManager によって提供されるストック シェーダを使用することです。これらは、ポイントラインおよび三角形の UI 描画に使用できるシェーダ、およびいくつかのストック マテリアル シェーダの既定の構成を供給します。 ストック シェーダのインスタンスは、プリミティブ タイプに固有です。たとえば、破線シェーダは三角形をレンダーするには不適切です。

また、カスタムのエフェクト ファイル シェーダを使用してさらにコントロールの精度を高めることもできますが、それにはすべての描画 API (DirectX11 や OpenGL など)に対する実装が必要です。 カスタム シェーダを使用する 1 つの理由は、大量のスタティックなレンダー項目 バリアントのセットを使用するのではなく、より複雑なパラメータのセットを使用してダイナミックに更新できるようにすることです。

フラグメントに基づいたシェーダは、Maya のシェーディング ネットワークとの統合に最も適したもので、UI 描画には推奨されません。 組み込みのフラグメントの使用は、完全なシェーディングとライティングのサポートを得るための簡単な方法です。

最高の柔軟性を得るには、シェーダをジオメトリ オーバーライドの一部としてコントロールするのではなく、個別の MPxShaderOverride を使用することを推奨します。つまり、ジオメトリ オーバーライドはジオメトリ ハンドラとなり、シェーダ オーバーライドはマテリアル ハンドラとなります。

各シェーダには、パラメータのセットおよびジオメトリ要件が関連付けられています。 シェーダのインスタンスにはそれぞれ異なるシェーダ パラメータと要件があり、それにより、各シェーダ インスタンスのレンダー項目の特徴が決まります。

更新の頻度にもよりますが、パラメータの更新には MShaderInstance へのコールバックをアタッチした方が簡単な場合があります。例えば、これによって、線の厚さパラメータ値が異なる 2 つのシェーダ インスタンス(つまりレンダー項目)を個別に作成することなくコールバック中にライン シェーダの厚さパラメータを変更することができます。

異なるストリームが同じジオメトリ要件のセットにバインドされる場合、ジオメトリ バインディングはダイナミックに行なわれるので、異なるシェーダ インスタンスは必要ありません。

変更の処理

表示状態変更の処理

既定では、プラグインは、関連する DAG オブジェクトの表示状態が変更したときにその UI レンダー項目を更新するために呼び出されます。 これは、次のような状況で発生します。

選択状態の変更は、一般にレンダー項目のカラーの変更を意味します。 これには 2 つのアプローチが考えられます。つまり、各選択状態のために複数のレンダー項目を作成するか、あるいは十分なシェーダ パラメータを備えた単一のレンダー項目を使用することができます。 ジオメトリが一般に共有される場合、複数の項目を持つことによる自明のコストは小さくなります。 描画モードが異なる場合は、異なるレンダー項目が必要です。 たとえば、休止状態とアクティブのワイヤフレームでは異なる項目が使用されます。前者はワイヤフレーム モードでのみ描画されますが、後者はすべての描画モードで描画されます。

カラーの実際のコントロールは、一般に MGeometryUtilities::wireframeColor() (現在のオブジェクトの状態を考慮して、正確なカラーを返す)などのユーティリティによって扱うことができます。

オブジェクトの可視性については、マテリアル レンダー項目は UI レンダー項目と同様に自動的に扱われます。 ただし、テンプレートの状態変更は特定のレンダー項目に関しては可視性の変更と考えることができます。 たとえば、コンポーネントのレンダー項目は Maya の内部の振る舞いと一致するように手動で無効にする必要があります。 MGeometryUtilities::displayStatus() は状態の確認に役立つユーティリティです。

描画オーバーライド(アトリビュート エディタ(Attribute Editor)の描画オーバーライド(Draw Overrides)タブに公開)の場合は、カスタム カラー オーバーライドとともにテンプレート コントロールを扱う必要があります。上記と同じユーティリティを使用できます。

ディスプレイ レイヤは、テンプレート化およびカラー オーバーライドのために更新を呼び出すことができます。

Maya によってコントロールされない追加の項目には、それぞれ内部プラグイン ロジックと対応する更新ロジックがあります。 たとえば、フェース センターの表示を追加することを、カスタム レンダー項目の表示を駆動してこれらのセンターを表示するノード上のアトリビュートにすることができます。

表示モード変更の処理

既定では、レンダリングの表示モードが変更されると、オーバーライドは呼び出されません。 これは、すべての可能な表示モードのすべてのレンダー項目を考慮しなければならないということであり、重要な意味があります。 推奨されるアプローチは、適切なパラメータを使用してすべてのレンダー項目を作成し、表示モードに関係のない更新は、必要に応じて「更新」時に扱うというものです。

ジオメトリ オーバーライドとサブシーン オーバーライドの重要な相違点は、サブシーン オーバーライドは、フレーム コンテキストが提供される更新呼び出しを常時受け取るということです。 この場合、レンダー項目はオンデマンドで作成または修正することができます。

一般的な状態のモニタ

Maya で発生する一般的なイベントの場合、どのイベントをモニタすべきか、イベントに対してカスタムのレンダー項目の更新が必要かどうか、を決めるのはプラグインの責任です。 たとえば、ビュー依存のレンダー項目は自分自身のカメラ モニタ(MUIMessage などによる)を実行する必要があります。

ジオメトリの処理

カスタムのレンダー項目には名前が付いているため、管理する名前付きレンダー項目のセットがあります。 また、セマンティックは同じで名前が異なるジオメトリ ストリームの要件が存在する可能性もあります。

たとえば、マテリアルのレンダー項目の描画と、面法線を描画するレンダー項目が必要となる場合には、位置の 2 つのストリーム(セマンティック)が必要となる場合があります。 前者を「My shaded item」、後者を「My face center item」のように命名することができます。 後者は、「フェース センター」のカスタムのストリーム名を使用して MRenderItem::setShader() を呼び出すことができました。

さらに、ストリームとレンダー項目の間には明示的な関連性が存在しないため、特定のカスタムのレンダー項目で使用する必要がある場合には、ストリームに名前をつけると役に立つ場合があります。 ただし、ストリームごとに名前を付けることによって、追加のジオメトリ データが発生し、インデックスの割り当ても必要になるため、一概にメリットがあるとはいえません。 また、ジオメトリの更新では、ストリームの名前付けはレンダー項目単位で行われるという事実を考慮する必要があります。 すなわち、名前は 1 つの項目のすべてのストリームに適用されます。 使用例を「4.4.5.5 基本コンポーネントの処理」に示します。

図 80

上の図は、左側の 3 つのレンダー項目を示します。 一番上のアイテムはフェース センターの描画に使用されます。 これは位置のみを必要とし、「フェース センター」のカスタム名を持っています。 次はアクティブな頂点を描画するためのアイテムで、位置を必要とし、「アクティブな頂点」のカスタム ストリーム名を持っています。 最後はマテリアル レンダー アイテムで位置と法線を必要とします。 カスタム名はありません。 オーバーライドがジオメトリを供給しなければならない最終的な累積要件セットが戻ってきたときに、必要な 4 つのデータ ストリーム(位置ストリームが 3 つ、法線ストリームが 1 つ)に関する説明(MVertexBufferDescriptor)があります。位置ストリームは、マテリアルとアクティブな頂点の表示に共通のものを使用することができます。

データがいつ必要になるかについての想定はされないため、すべての要件が常に満たされるようにする必要があります。 適切なデータが提供されない場合、レンダラは、既定のデータを提供しようとする場合があります。しかし適切なデータを提供できるという保証はありません。 たとえば、ビューポートの構成において、1 つのパネルがシェーディング モードでもう 1 つのパネルがワイヤオン シェーディング モードを有効にしている場合、1 つのビューポート パネルは、シェーディング レンダー項目に加えてワイヤフレーム レンダー項目を必要とします。 シェーディングされたレンダー項目では、接線が必要になる場合があります。

基本コンポーネントの処理

コンポーネントが変更された場合、オーバーライドは DG 評価時にプラグイン オブジェクトから適切なコンポーネント情報を抽出する必要があります。 たとえば、コンポーネントのインデックス処理をキャッシュに入れることができます。 (MPxGeometryOverride の場合、これは updatedDG() 中に行われます)。このインデックス処理は、サポート対象コンポーネントに関連付けられたレンダー項目に対するプリミティブ インデックス処理を提供するために使用することができます。 たとえば、頂点のような単一のインデックス付きコンポーネントの場合、頂点の識別子をキャッシュに入れることができます。

各コンポーネント タイプでは、必要な表示バリエーションに応じて、オーバーライドに 1 つまたは複数のレンダー項目が作成される場合があります(MPxGeometryOverride の場合、これは updateRenderItems() 中に行われます)。前述の頂点コンポーネントの例では、未選択の頂点を表示するための 1 つの項目と選択済みの頂点を表示するための 1 つの項目があります。 それらの項目は、たとえばカラーやサイズ、深度の優先順位が異なります(それらがオーバーラップする場合)。

各レンダー項目には、ポイントの描画に適切なストック シェーダ インスタンスが関連付けられている場合があります。 未選択頂点のレンダー項目はワイヤーフレーム モードでのみ適用され、その場合には描画モードを関連付ける必要があります。 選択済み頂点のレンダー項目は、シェーディング、テクスチャ、ワイヤフレーム モードで描画するように設定されます。この項目は、これらのモードすべてで表示されるためです。 既定では、項目は、頂点表示が有効の場合、あるいはオブジェクトの表示状態が「ハイライト」モードの場合に有効となります。

ジオメトリの更新時(MPxGeometryOverride の場合、これは populateGeometry() 中に行われます)に、シェーダ ジオメトリ要件に基づいて適切なストリーム データが要求されます。以前に評価されたコンポーネント インデックス処理に応じて、適切なプリミティブ インデックス処理を作成/更新する必要があります。

サブシーン オーバーライドの場合、すべてのトランザクションは update() メソッド内で処理されます。

名前付きのストリームを使用する例として、1 つの位置ストリームを未選択の描画に、もう 1 つの位置ストリームを選択された頂点び描画に使用することができます。 プラスの側面としては、選択される頂点の数が少ないため、インデックス処理とデータ転送の負荷が小さくなります。 マイナスの側面としては、追加のストリームを割り当てる必要があるため、メモリのコストが増大します。 同じストリームを再利用することは、同じメモリを再利用するということであり、複雑なインデックス処理の計算や過度の描画が行われる可能性があります。

カスタムの UI 描画の例

たとえば、プラグインは以下に対するカスタムのレンダー項目を作成することができます。

図 81

緑色のボックスは、プラグインが作成し管理する領域を表します。 白いボックスはレンダラによって提供されます。

以下に、上記の項目のパラメータの例と、それぞれの項目に適した更新の頻度を示します。

三日月の形の「コンテナ」は、ジオメトリとサブシーンのオーバーライド レンダー項目の両方が、どれくらい論理的にレンダー項目のセットに収集されるかを示します。 主な違いは、サブシーンのオーバーライド コンテナ クラスは明示的に API に公開されるということです。

以下に、ユーザ ワークフローの例、およびプラグインがどのようにそれらのレンダー項目を処理することができたかを示します。

  1. ユーザは、シェーディング モード中に、コンポーネント選択モードに入ります。
  2. オブジェクトはハイライト モードにあるので、休止中のワイヤフレーム項目を有効にして、カラーを適切に設定する必要があります。
  3. 休止中の頂点レンダー項目を有効にして、カラーを適切に設定する必要があります。
  4. アクティブなワイヤフレーム項目を無効にする必要があります。
  5. アクティブな頂点項目を無効にする必要があります。
  6. シェーディングされたテンプレート項目を無効にする必要があります。
  7. シェーディングされたプロキシ項目を無効にする必要があります。

    注: マテリアルにシェーディングされた項目は内部的に有効です。シェーディング モードになったときに有効になりました。

  8. 次にユーザはいくつかの頂点を選択します。
  9. アクティブな頂点項目を有効にする必要があります。 アクティブなコンポーネントを解析し、頂点のサブセットを表示するために適切なインデックス処理を設定する必要があります。
  10. ユーザはオブジェクト選択モードに戻ります。
  11. 休止中の頂点項目とアクティブな頂点項目は無効になります。
  12. アクティブなワイヤフレーム項目は有効になります。
  13. 休止中のワイヤフレーム項目は無効になります。
  14. ユーザはワイヤフレーム モードに切り替わります。
  15. シェーディング モードで描画するようにマーキングされたレンダー項目は描画パイプライン内でフィルタされるため、プラグインでは何もする必要はありません。
  16. ユーザはオブジェクト オーバーライドを設定してオブジェクト テンプレートを作成します。
  17. カスタム マテリアルのレンダー項目がある場合は、無効にする必要があります。提供されているマテリアル レンダー項目は自動的に無効になります。
  18. 休止中のワイヤフレーム項目とアクティブなワイヤフレーム項目を無効にする必要があります。
  19. 休止中の頂点項目を無効にする必要があります。
  20. シェーディングされたテンプレート項目を有効にして、オブジェクトがアクティブかどうかに応じて、適切なカラーに設定する必要があります。

図 82

上記の 5 つのアクションを上から下に、そして左から右に図で表しています。 どんな場合でも、すべてレンダー項目の保持は、基本的な有効と無効の発生、およびカラーなどの起こりうるパラメータの変化によって行われます。 有効な項目は緑色で表示され、それ以外のすべてのレンダー項目は無効になります。

ジオメトリ オーバーライドの場合、処理の大多数は updateRenderItems() 内で行われ、サブシーンのオーバーライドは update() 内で行われます。SDK プラグイン apiMeshShape には、上記のようなワークフロー、および多くの「標準の」ワークフローをサポートするロジックを含むコードの例が含まれています。

カスタム マテリアル

カスタム マテリアルのレンダリングが必要な場合、統合のレベルは使用されるインタフェースに依存します。 以下の表は、それぞれのインタフェースに関する主なトレードオフをまとめたものです。 柔軟性を高めるには、プラグイン作成者はより多くの作業を行う必要があります。 ある程度は、より単純なシェーダをジオメトリ ハンドラ/オーバーライド内部で維持することができます。

FragmentEffects、および Stock は、シェーダ マネージャを介して利用可能なオプションです。そのため、サポートされるオプションは内部レンダラによって決定されます。Shader OverrideMPxShaderOverride です。

  フラグメント シェーダ エフェクト シェーダ ストック シェーダ シェーダ オーバーライド
柔軟性: 複雑さの比 高: 高 中: 低 低: 低 高: 高
使用例

一般に、既存のフラグメントのセットを強化するために使用されます。

「ストック」シェーダとして使用できますが、より複雑化することで適切なシェーディング ネットワークをセットアップすることもできます。

すべての内部セマンティック バインディングがサポートされます。

既定のセマンティック バインディングを使用できる自己完結型のエフェクト。

単純な組み込みのエフェクトで「十分に機能」。

以前の簡単に記述できる機能限定のコードに代わって複雑な UI シェーダを記述する作業を避けたい場合に役に立ちます。

内部レンダラが提供するサポートよりも複雑なシェーディングのサポートが必要です。たとえばテッセレーション シェーダなどです。
レンダー項目の「タイプ」 カスタム。 カスタム。 カスタム。 非カスタム。
シェーディング グラフのサポート

個々のフラグメントを使用する場合は、なし。

フラグメント シェーダが Maya シェーダ ツリーの一部としてノード内で使用される場合は、完全に統合されます。 フラグメント シェーダを使用して MShaderInstance を作成する場合、明示的なサポートはありません。

なし。 なし。 プラグインにより決定。
ライティングのサポート 統合。 自動バインドなし。 設計では無視されます。 プラグインは描画コンテキストへのアクセスを介してバインドします。
ポスト エフェクトのサポート 統合。 統合されていません。 設計では無視されます。 プラグインは、エフェクトによって要求される各種のパスをサポートする技術を提供します。
描画 API サポート カスタムの場合は、各描画 API ごとの実装が必要です。そうでなければ API 非依存になります。 プラグイン作成者は、サポートする API のバージョンを作成します。 組み込み。 プラグイン作成者は、サポートする API のバージョンを作成します。
シェーダ ステージをサポート 頂点、ピクセル。 頂点、ピクセル、ジオメトリ。 公開されていません。 プラグインによって決定。
ジオメトリ ハンドラ(オーバーライド)からの分離 ジオメトリ ハンドラによって作成されたレンダー項目上のシェーダ インスタンスのために使用することができます。 ジオメトリ ハンドラによって作成されたレンダー項目上のシェーダ インスタンスのために使用することができます。 ジオメトリ ハンドラによって作成されたレンダー項目上のシェーダ インスタンスのために使用することができます。 ジオメトリの処理から独立しています。

マテリアル項目を扱う場合、一般に、「シェーディング」または、「シェーディングとテクスチャ」描画モードがオーバーライドにパスされるレンダー項目があります。 DAG オブジェクトに割り当てられたマテリアルの数に応じて、このようなパスされる項目のセットの数は 0 またはそれ以上になります。

図 83

この例では、DAG オブジェクトは DAG オブジェクト上の異なるコンポーネントに 2 つのシェーダを割り当てます。 4 つの「マテリアル」レンダー項目を持つことができます。1 つは「シェーディング」描画モードで描画される各コンポーネント シェーダ、1 つは「シェーディングとテクスチャ」モードで描画される各コンポーネント シェーダに対するものです。

これらのレンダー項目のパラメータはそのまま使用することも、修正したり、無効にすることもできます。 無効にした場合、プラグインは代わりのパラメータを提供しなければなりません。 代わりのパラメータが提供されない場合、オブジェクトはシェーディングまたはテクスチャ モードのときに「消えた」ように見えます。 代わりのパラメータのタイプに応じて、統合のレベルは異なったものになります。

あるレンダー項目のシェーダ処理の複雑さが MPxShaderOverride が必要となるレベルに達すると、ジオメトリ ハンドラの複雑さが低減され、これらのレンダー項目のジオメトリ要件のみを満たすようになります。

たとえば、プラグインは完全に統合されたいくつかのプリセット シェーディング構成を持つビルトインの Blinn シェーダのみを必要とする場合があります。 この場合はストック フラグメント シェーダを使用することができます。 別の例では、ヘア シェーダのためのテッセレーションが必要になる場合があります。 この場合の最善の対応は、ジオメトリ ハンドラの内部からこれをコントロールせず、代わりにカスタムの MPxShaderOverrideの「ヘア シェーダ ノード」を作成し、レンダー項目をカスタムの「ヘア ジオメトリ ノード」オーバーライドに「フィードする」ことです。

図 84

各種の利用可能なオプションがすべて一度に表示されます。

レンダラが提供する(マテリアル)レンダー項目は、カラーなしで表示されます。 内部シェーダとプラグイン シェーダの組み合わせによってこれらの項目を派生させる場合もあります。 ノードには、カスタムのフラグメント シェーダを提供するもの、あるいはシェーダ オーバーライドが関連付けられるものがあります。 ジオメトリ ハンドラ(オーバーライド)は、これらを非カスタムのレンダー項目として受け取ります。

カスタムのレンダー項目(緑色で表示)は、fragmentstock、または effects files ファイル インタフェースを使用して、シェーダ マネージャを介して作成されるシェーダ インスタンスを用いてインスタンス化することができます。

UI 描画マネージャ

MUIDrawManager クラスは、一般にはオブジェクトの一部としての関連性をもたない追加の UI 描画をキューに入れるための簡易インタフェースを提供します。キューに入れることのできる「描画可能項目」は、レンダー項目とほぼ同じであると考えることができます。

マネージャを使用することの直接的な意味は、「描画可能項目」は永続的ではないので、このインタフェースを介してキューに入れられる描画可能項目の数は比較的少なくすることができるということです より多くの項目が必要な場合、またはデータ コンテンツが大きい場合は、mesh() 呼び出しなどのインタフェースを使用することができます。そうでなければ、カスタムのレンダー項目が推奨されます。

たとえば、プラグイン オブジェクト上に大量の頂点を描画する場合は、描画マネージャ内で個々の点を描画するよりも、カスタムのレンダー項目を使用することが推奨されます。 ただし、オブジェクト上で 3D または 2D のラベルを描画する場合は、描画マネージャが推奨されます。

検討が必要なもう 1 つの側面は、マネージャを使用して描画された UI は、パイプライン全体の中の固定された場所に描画され、より厳密な定義済み描画プロパティのセットを持つ場合があるということです。

エフェクトおよびコンテキストのインタラクション

既定では、UI 描画(ライン、ポイント)に使用されるレンダー項目は、スクリーンスペース アンビエント オクルージョン、モーション ブラーおよび被写界深度のようなシーン エフェクトから除外されます。 また一般に、それらはシャドウの投影や受像を行いません。 内部マテリアル シェーダ インスタンスを使用するレンダー項目は、これらのエフェクトの要素となります。

エフェクトの描画は、いくつかの描画コンテキスト内で実行されます。

ジオメトリ オーバーライドにはコンテキスト情報は渡されず、その動作はほとんどの場合コンテキストに依存しません(シェーダ コールバックは例外)。 サブシーンのオーバーライドにはコンテキストが提供されますが、これはフレーム コンテキストであり、パス コンテキストへのアクセスを提供しません。 したがって、どの項目も、呼び出し元のパスに関係なく動作します。

ビューポート 1.0/ビューポート 2.0 の比較チャート

  ビューポート 1.0 ビューポート 2.0 (Viewport 2.0)
カスタムのレンダリング可能項目の作成 MDrawInfo::getPrototype() MRenderItem::Create()
カスタムのレンダリング可能項目の削除 描画要求は一時的なもので、更新のたびに内部的に解放されます。 MRenderItem::Destroy()
レンダリング可能項目を待ち行列に入れる MPxSurfaceShapeUI:: getDrawRequests ()

MPxGeometryOverride::updatedDG() および MPxGeometryOverride::updateRenderItems()

MPxSubSceneOverride:: Update ()

レンダリング MPxSurfaceShapeUI:: draw () 適用できません。レンダリングはレンダラによって行われます。
マテリアルの処理 テンプレート修正関数のマテリアルが提供されます。任意にオーバーライドできます。

MShaderInstance に基づいています。パラメータは更新時に修正できます。レンダー項目へのシェーダ バインド(再バインド)が可能です。

より複雑または分離されたマテリアルの扱いは、MPxShaderOverride を介して実行します。

表示モードの処理 表示モードは入力として提供され、任意に無視できます。 描画モードは、レンダー項目の固有のパートです。内部ロジックは、表示モードのメンバーシップに基づいて項目を削減します。
ジオメトリ要件のインタフェース 概念がありません。

MPxGeometryOverride: : populateGeometry ()

MPxSubSceneOverride:: setGeometryForRenderItem ()

ジオメトリ要件の満足 概念がありません。 別のレンダー項目に対する明示的なストリームの命名が可能です。結果として追加のバッファとインデックスが処理されます。
バウンディング ボックスの描画 内部でコントロールします。 内部でコントロールします。
ゴースト化のコントロール 複数の描画呼び出しは、単一の描画要求のセットに対して実行されます。 プラグインでコントロールします。すべてのゴーストに対する呼び出しの単一セット。
シャドウの投影/受像 オブジェクトの「投影」および「受像」アトリビュートがあれば、それらを使用して制御します。そうでなければ、制御は行われません。 必要に応じてカスタム レンダー項目ごとにコントロールします。
表示状態の照会 MDrawInfo の一部として渡された表示状態の情報。 表示状態は MDagPathMGeometryUtilities から照会することができます。
コンポーネントの管理 要求の描画は可能です。プラグインは管理を実行します。 コンポーネント情報は適切なレンダー項目を使用して描画するために渡すことができます。 ビューポート 1.0 のインタフェースはコンポーネント管理を実行します。
可視性の処理 描画要求または描画を待ち行列に入れる場合の要求を削減します。 プラグインは、必要に応じてレンダー項目を有効/無効にするか、内部描画モード フィルタを使用するために描画モードを設定します。
UV のレンダリング MPxSurfaceShapeUI:: drawUV () 概念がありません。ビューポート 1.0 インタフェースを使用します。
選択 MPxSurfaceShapeUI: : select () 概念がありません。ビューポート 1.0 インタフェースを使用します。
選択状態の照会 MDrawInfo クラス内で見つかった情報は、選択したものについて描画が発生しているかどうかを示します。 選択はビューポート 2.0 の一部ではないため、適用できません。
ユーザ インタラクションのフィードバック いくつかの情報は MDrawInfo を介して利用可能です。 プラグインは、明示的に対象のユーザ イベントをモニタします。

サンプル コード

apiMeshShape Developer Kit サンプルは、UI の処理およびプロキシ シェーダの処理に関連する多くのワークフローを考慮に入れます。

図 85

ワイヤフレーム モードにおける apiMeshShape のシーンのサンプル。

図 86

シェーディング モードにおける apiMeshShape のシーンのサンプル。

上のスナップショットは、使用されている多くの異なるカスタムのレンダー項目を示します。 プレーンと IK チェーンを除くすべてのオブジェクトは、同じプラグインを使用して描画されます。 以下が含まれます。

アクティブなワイヤフレーム処理のためのサンプル コード

次のコードは、MPxGeometryOverride::updateRenderItems() 内部からの「アクティブなワイヤフレーム」レンダー項目の処理を示します。コード ロジックは、MPxSubSceneOverride とともに使用した場合は類似したものになります。例外は、レンダー項目が MRenderItemList ではなくコンテナ(MPxSubSceneContainer)に属するということです。

以下に、シェーダ上にカラー パラメータを設定する方法の例を示します。

// Utility to set the color on a shader instance.
void setSolidColor(MHWRender::MShaderInstance* shaderInstance, const float *value)
{
  if (!shaderInstance)
    return;
  const MString colorParameterName = "solidColor";
  shaderInstance->setParameter(colorParameterName, value);
}

updateRenderItems() の実行中に、「アクティブ」なワイヤフレーム項目がまだ存在しない場合は、新しく作成されます。新しいワイヤフレームが作成されると、描画モードは「すべての」モードに設定され、項目は、上のシェーディングされたレンダー項目を描画するように設定されます。

// **\*****\*****\*****\*****\**** Update during updateRenderItems() **\*****\*****\******
…
// Unique name for active wireframe
MString fSelectedWireframeItemName = “selectedWireframeName”;
// Create the item once. Use enable/disable toggle to control when it should
// be drawn (below)
MHWRender::MRenderItem* selectItem = NULL;
int index = list.indexOf( // list = MRenderItemList passed as an input argument
    fSelectedWireframeItemName,
    MHWRender::MGeometry::kLines, // Type of primitive is lines 
    MHWRender::MGeometry::kAll); // Draw in all display modes
if (index < 0)
{
    static const bool raiseAboveShaded = true;
    selectItem = MHWRender::MRenderItem::Create(
        fSelectedWireframeItemName,
        MHWRender::MGeometry::kLines,
        MHWRender::MGeometry::kAll,
        raiseAboveShaded);
    // This is the same as setting the argument raiseAboveShaded = true,
    // since it sets the priority value to be the same. This line is just
    // an example of another way to do the same thing after creation of
    // the render item.
    selectItem->depthPriority( MHWRender::MRenderItem::sActiveWireDepthPriority);
    list.append(selectItem);

    // Choose an line shader to match primitive type
    MHWRender::MShaderInstance* shader = 
        shaderMgr->getStockShader( MHWRender::MShaderManager::k3dSolidShader );
    if (shader)
    {

        // Assign shader
        selectItem->setShader(shader);
        // Once assigned, no need to hold on to shader instance
        shaderMgr->releaseShader(shader);
    }
}
else
{
    selectItem = list.itemAt(index);
}


表示状態に基づいて、更新中に、ワイヤフレーム項目のカラーが更新されます。 これは、leadactivehighlightactive-component などのステータスに基づいてカラーを切り替えます。

// Perform updates on the render item parameters. In this case
// update the shader instance.
//
MHWRender::MShaderInstance* shader = NULL;
if (selectItem)
{
    shader = selectItem->getShader();
}

// Check the current display status of the object and color to use for this
// instance of the object.
MHWRender::DisplayStatus displayStatus = 
    MHWRender::MGeometryUtilities::displayStatus(path);
MColor wireColor = MHWRender::MGeometryUtilities::wireframeColor(path);

// It is not necessary to use the colors provided and instead a custom color could be 
// used if (fUseCustomColors) is set.
bool fUseCustomColors = false;
switch (displayStatus) {
case MHWRender::kLead:
    selectItem->enable(true);
    if (shader)
    {
        static const float theColor[] = { 0.0f, 0.8f, 0.0f, 1.0f };
        setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
    }
    break;
case MHWRender::kActive:
    if (shader)
    {
        static const float theColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
        setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
    }
    selectItem->enable(true); // Enable if the object is active
    break;
case MHWRender::kHilite:
case MHWRender::kActiveComponent:
    if (shader)
    {
        static const float theColor[] = { 0.0f, 0.5f, 0.7f, 1.0f };
        setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
    }
    selectItem->enable(true); // Enable if the object is hilite, or 
                                  // its components are active.

    break;
default:
    // Otherwise make sure to disable it. The item is still present, it will just
    // not be sent down the rendering pipeline to draw.
    selectItem->enable(false);
    break;
};

アクティブな頂点処理のためのサンプル コード

以下に、MPxGeometryOverride::updateRenderItems() の内部から「アクティブな頂点」レンダー項目を処理するためのサンプル コードを示します。表示状態をチェックして、コンポーネントのレンダー項目を無効にするかどうかを判断する、独立したユーティリティ メソッドがあります。

テストには、アクティブな頂点があるかどうかも反映されます。アクティブな頂点の有無は、DG 更新時(updatedDG())に決定されます。

このコード パスは、名前付きストリームを使用可能にするように記述されています。populateGeometry() メソッド内部では、これらの名前付きストリームをチェックして、位置の異なるセットを使用してアクティブな頂点を描画することができます。

以下のコードは、DG 更新時に実行されます。 これは、オブジェクトからコンポーネント リストを取得し、要素 ID を整数配列の形式でキャッシュに入れます。

/**\*** Call in MPxGeometryOverride::updatedDG() to perform any DG operations ****/
void updateDG()
{
    fActiveVertices.clear(); // fActiveVertices is an MIntArray 
    
    MObjectArray clist = // Get the active vertex components – plug-in specific ;
    if (clist.length())
    {
        MFnSingleIndexedComponent fnComponent( clist[0] );
        if (fnComponent.elementCount())
        {
            // Cache the vertex identifiers for later usage.
            fnComponent.getElements( fActiveVertices );
        }
    }
}

以下のユーティリティは、コンポーネントを非表示にするかどうかを判断するためのテストを実行します。

/* Test to see if active components should be enabled.
           Based on active vertices + non-template state
*/
bool enableActiveComponentDisplay(const MDagPath &path) const
{
    bool enable = true;

    // If no active components then disable the active
    // component render item
    if (fActiveVertices.length() == 0)
    {
        enable = false;
    }
    else
    {
        // If there are components then we need to check
        // either the display status of the object, or
        // in the case of a templated object make sure 
        // to hide components to be consistent with how
        // internal objects behave
        //
        MHWRender::DisplayStatus displayStatus = 
            MHWRender::MGeometryUtilities::displayStatus(path);
        if (displayStatus == MHWRender::kTemplate ||
            displayStatus == MHWRender::kActiveTemplate)
        {
            enable = false;
        }
        else
        {
            // Do an explicit path test for templated
            // since display status does not indicate this.
            if (path.isTemplated())
                enable = false;
        }
    }
    return enable;
}

updateRenderItems() の実行中に、適切な項目が必要に応じて作成され、「すべて」のモードで描画されます。深度の優先順位は、休止中の頂点および任意のワイヤフレーム アイテムによって非表示にならないように設定されます。 項目はコンポーネントを表示するかどうかに基づいて有効になります。

…
// Unique name identifier for the render item
MString fActiveVertexItemName = “activeVertexItemName”;
MHWRender::MRenderItem* activeItem = NULL;
int index = list.indexOf(
    fActiveVertexItemName,
    MHWRender::MGeometry::kPoints,// Primitive type is points.
    MHWRender::MGeometry::kAll); // Render item should display in all display modes.
if (index < 0)
{
    activeItem = MHWRender::MRenderItem::Create(
        fActiveVertexItemName,
        MHWRender::MGeometry::kPoints,
        MHWRender::MGeometry::kAll,
        false);
    // Set depth priority to be active point. This should offset it 
    // to be visible above items with "dormant point" priority.
    activeItem->depthPriority( MHWRender::MRenderItem::sActivePointDepthPriority );
    list.append(activeItem);

    MHWRender::MShaderInstance* shader = shaderMgr->getStockShader(
        MHWRender::MShaderManager::k3dFatPointShader );
    if (shader)
    {
        // Set the point size parameter. Make it slightly larger for active
        // vertices
        static const float pointSize = 5.0f;
        setSolidPointSize( shader, pointSize );

        // Assign shader. Use a named stream if we want to supply a different
        // set of "shared" vertices for drawing active vertices
        bool fDrawSharedActiveVertices = true;
        MString fActiveVertexStreamName = “activeVertexStreamName”;
        if (fDrawSharedActiveVertices)
        {
            activeItem->setShader(shader, &fActiveVertexStreamName );
        }
        else
        {
            activeItem->setShader(shader, NULL);
        }

        // once assigned, no need to hold on to shader instance
        shaderMgr->releaseShader(shader);
    }
}
else
{
    activeItem = list.itemAt(index);
}

// Update the color and enable / disable the render item as appropriate.
// Just using a fixed color and not checking display status.
bool enableActiveDisplay = enableActiveComponentDisplay( <dagpath> );
if (activeItem)
{
    MHWRender::MShaderInstance* shader = activeItem->getShader();
    if (shader)
    {
        // Set active color
        static const float theColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
            setSolidColor( shader, theColor); // See code for active 
                    // wireframe for this code utility.
    }

    activeItem->enable( enableActiveDisplay );
}

以下に、populateGeometry() 内の名前付きカスタム ストリームを処理するためのコード ロジックの一部を示します。コードの主要な部分では、頂点バッファ記述子名をチェックして、上の updateRenderItems() の実行中に MRenderItem::setShader() を呼び出したときに設定された名前と一致するかを確認します。確認後、プラグインは自由にデータを入力し、変数 fActiveVertexItemName に格納された値に一致する名前を持つレンダー項目に適したインデックス構造を作成します。

const MHWRender::MVertexBufferDescriptorList& descList =
        requirements.vertexRequirements();
int numVertexReqs = descList.length();
MHWRender::MVertexBufferDescriptor desc;
for (int reqNum=0; reqNum<numVertexReqs; reqNum++)
{
    if (!descList.getDescriptor(reqNum, desc))
    {
        continue;
    }

    // Fill in vertex data for drawing active vertex components 
    //
    if (fDrawSharedActiveVertices && (desc.name() == fActiveVertexStreamName))
    switch (desc.semantic())
    {
        case MHWRender::MGeometry::kPosition:
        {
            if (!activeVertexPositionBuffer)
            {
                activeVertexPositionBuffer = data.createVertexBuffer(desc);
                if (activeVertexPositionBuffer)
                {
                   // Allocate a position buffer to fit the # of active vertices
                   unsigned int activeVertexCount = fActiveVertices.length()
                   activeVertexPositions =
                      (float*)activeVertexPositionBuffer>acquire(
                              activeVertexCount, 
                              true 
                              /*writeOnly*/             
                }
            }
        }
        break;

        default:
            break;
    }
}