シェーダのジオメトリック データをカスタマイズする

Maya DAG オブジェクトのデータ表現は、シェーディング アルゴリズムの入力として使用できるジオメトリック ストリームのフォーマットと型の両方を示します。指定されたシェーダの場合、提供されたストリームのフォーマットまたは型のいずれかが不十分である可能性があります。

こうした問題に対処するために、データをカスタマイズするための API が提供され、次のことが可能になります。

すべてのインタフェースが統一されたシステム内で動作するため、任意のカスタマイズまたはすべてのカスタマイズを相互に組み合わせて使用することができます。

カスタム データ ストリームとカスタム データ インデックスを提供するためのプラグイン機能を提供する 2 つのインタフェースがあります。

ジオメトリ ジェネレータ

カスタム ストリームは、MPxVertexBufferGenerator を使用して提供できます。この機能に一致する旧式のクラスはありません。

ジェネレータは、新規データや既存のストリーム データに基づいてストリームを提供できます。

MPxVertexBufferGenerator を使用するには、MDrawRegistry クラスを使用して、オーバーライドの登録方法と同様の方法でこれを登録する必要があります。

シェーダが特定のデータ ストリームを必要とする場合は、MVertexBufferDescriptors を使用してその要件を指定します。各 MVertexBufferDescriptor は、ストリーム名とセマンティックを使用してそれぞれのストリームを指定します。すべての MPxVertexBufferGenerator が、関連付けられた名前を持つ必要があります。ジェネレータを参照する場合は、MVertexBufferDescriptor でこの名前をそのセマンティック名として使用できます。

2 つのカスタム ストリームの要件を指定する MPxShaderOverride の例を次に示します。

// Create a custom position stream requirement. Set a unique semantic name for lookup.
// Add to the list of requirements.
MHWRender::MVertexBufferDescriptor positionDesc(
                empty, 
                MHWRender::MGeometry::kPosition,
                MHWRender::MGeometry::kFloat,
                3);

positionDesc.setSemanticName("customPositionStream");
addGeometryRequirement(positionDesc);

// Create a custom normal stream requirement. Set a unique semantic name for lookup.
// Add to the list of requirements.
MHWRender::MVertexBufferDescriptor normalDesc(
                empty,
                MHWRender::MGeometry:: kNormal,
                MHWRender::MGeometry::kFloat,
                3);
normalDesc.setSemanticName("customNormalStream");
addGeometryRequirement(normalDesc);

データに入力するために、ジェネレータがその createVertexStreams() メソッドを使用して呼び出されます。指定の DAG パスでは、ジェネレータが更新され、MVertexBuffer を返します。

既存データに基づいて新しいデータが生成される場合は、既存のソース ストリーム セットを指定できます。createVertexStreams() が呼び出される前に、ジェネレータ(getSourceStreams())に照会することで、必要とされる特定のストリームが返されます。

たとえば、既存の位置と法線ストリームを組み合わせることで、新しいストリームを形成できます。入力ストリーム(MVertexBuffers)は GPU リソースであるため、CPU メモリにマッピングしてから読み込む必要があります。ソース ストリーム データの変更は禁じられています。

データに正しい入力が行われるように、データ ストリームの参照に使用されるインデックスが提供されます。ジェネレータは、カスタム ストリーム(getSourceIndexing()) に相応しいインデックスを提供します。内部およびカスタムのすべてのストリームのインデックスに基づいて、インデックスの最適化を実行できます。ストリームの作成時に、オリジナルおよび共有の両方のインデックス データがジェネレータに提供されます。インデックスは、MComponentDataIndexing インスタンスを使用して指定されます。これは、一連のインデックス値とコンポーネント タイプのラップ元です。

ジェネレータは、提供元の MVertexBuffer に対し、データの割り当てと転送を正しく実行する役割を果たします。

図 69: バッファ ジェネレータのメソッド順序に基づいた実行フローの概要が示されています。上から下の順番に、ソース ストリームとインデックスの要件が照会されます。作成時に、要求されたすべてのソース ストリーム、関連する DAG パス、およびすべての共有インデックスが入力として提供されます。ジェネレータは、MVertexBuffer への入力を行います。この例では、入力位置と法線ストリームを新しいストリーム内にスウィズルしています。

カスタム ストリーム インデックスは、MPxPrimitiveGenerator を使用して指定する必要があります。

ストリーム ジェネレータと同様に、使用できるインデックスを識別するために一意の名前が使用されます。インデックスを作成するために、シェーダ インスタンスが MIndexBufferDescriptor を使用して必要なインデックスを指定します。記述子のプリミティブ名は、プリミティブ ジェネレータの名前に設定できます。

MPxPrimitiveGenerator を使用するには、MDrawRegistry クラスを使用して、MPxVertexBufferGenerator の場合と同様の方法でこれを登録する必要があります。

現在、インデックス要件の追加をサポートしているエントリ ポイントは 2 つあります。MPxShaderOverride の場合は、そのジオメトリ要件が定義されるときのポイントです。MPxGeometryOverride の場合は、追加のレンダー項目に対してジオメトリ要件が定義されるときのポイントです。どちらの場合も、要件は MIndexBufferDescriptor によって指定されます。

カスタム インデックスを要件として追加するには、適切に設定された MIndexBufferDescriptor を追加する必要があります。タイプにはカスタム(Custom)を設定し、一意の名前を指定する必要があります。この名前は、登録したジェネレータの名前と一致する必要があります。

MPxGeometryOverride に対して非カスタム インデックス要件を追加する方法を次の例に示します。

MHWRender::MGeometryRequirements geomRequirements;

// As we are not using MIndexBufferDescriptor::kCustom for custom named index buffers
// just use an empty string here.
MString noName;

// Add in a triangle indexing requirement
MHWRender::MIndexBufferDescriptor triangleDesc(
        MHWRender::MIndexBufferDescriptor::kTriangle, 
        noName, 
        MHWRender::MGeometry::kTriangles, 
        3, 
        compObj);

geomRequirements.addIndexingRequirement(triangleDesc);

MPxShaderOverride に対してカスタム インデックス要件を追加する方法を次の例に示します。

// Name of custom indexing 
MString customPrimitiveName("customPrimitive");

// Add in a custom triangle indexing requirement
MHWRender::MIndexBufferDescriptor indexingRequirement( 
        MHWRender::MIndexBufferDescriptor::kCustom, 
        customPrimitiveName, 
        MHWRender::MGeometry::kTriangles);
        
addIndexingRequirement(indexingRequirement);

図 70: サンプル コードに基づいた結果のリレーションシップです。

カスタム インデックスが必要な場合は、ジェネレータ インスタンスに対して適切なコールが行われます。ジェネレータは、DAG オブジェクトおよび DAG コンポーネント(computeIndexingCount())に対して相対的なインデックス サイズを最初に提供します。その後、ジェネレータは実際のインデックスを作成するように指示されます。ソースとターゲットのインデックス データも提供されます。MIndexBuffer を更新して返す必要があります。これは GPU リソースであるため、CPU メモリにマッピングして戻し、更新してから GPU メモリに転送して戻す必要があります。

図 71

Developer Kit に含まれる例:

クラックのない mutator が、法線三角形リストよりも詳細な情報が含まれたインデックス バッファを作成します。三角形のインデックスは、三角形ごとに 3 つの頂点のインデックスを提供します。クラックのない mutator が、テッセレーションにフィットするインデックスを提供します。

サンプルには現在、「PN-AEN9」モードと「PN-AEN18」モード(隣接エッジ法線を使用した点法線三角形)のサポートが実装されています。

PN-AEN9 モードでは、次のデータを使用して各三角形のインデックスが作成されます。

これにより、インデックス配列のストライドは 9 になります。PN-AEN9 という名前はこれに由来します。

頂点 a、b、c で表現された上図の三角形の場合、PNAEN9 コードは次のインデックスを生成します。

 [a b c] [d e f g h i]

PN-AEN18 では、PN-AEN9 よりも多くのデータが追加されます。

これにより、インデックス配列のストライドは 18 になります。

ドミナント エッジは、最下部の頂点インデックスが含まれているエッジです。上に示した同じ例では、三角形 [abc][ab][de] 間のドミナント エッジは [ab] になります。また、三角形 [dej] の場合でも [ab] になります。

ドミナント UV 位置は、各頂点に最下部の UV 座標が含まれる三角形です。上の例では、三角形 [abc] のドミナント位置は [afh] です。

PN-AEN18 コードは、次のインデックスを生成します。

[a b c] [d e f g h i] [a b f g h i] [a f h]

dx11Shader プラグインには HLSL 用ジェネレータの使用方法が、CgFx プラグインには CgFx 用ジェネレータの使用方法がそれぞれ表示されます。dx11Shader は、シェーダの「PNAEN9」セマンティックと「PNAEN18」セマンティック用にクラックのないテッセレータ mutator を明示的に登録します。

SDK が提供するサンプル シェーダ(AutodeskUberShader.fx)は、次のテクニック宣言を使用して、API に特殊なインデックス要件を通知します。

technique11 TessellationON
<
    string index_buffer_type = "PNAEN18"; // Indicate index buffer type required
>
{  
    pass p0
    {
        SetRasterizerState(CullFront);
        SetDepthStencilState(DepthNormal, 0);
        SetVertexShader(CompileShader(vs_5_0, vt()));
        SetHullShader(CompileShader(hs_5_0, HS()));
        SetDomainShader(CompileShader(ds_5_0, DS()));
        SetGeometryShader(NULL);                                
        SetPixelShader(CompileShader(ps_5_0, f()));
    }
}

この場合、テクニックごとの注釈は、プラグイン コード内で解析されます。customIndexingMutatorName の文字列値「PNAEN18」は、更新時にクラックのない登録済みインデックス mutator をコールする、インデックス記述子要件におけるセマンティック名です。

MString customIndexingMutatorName;

ID3DX11EffectTechnique * mpD3DTechnique;

ID3DX11EffectVariable* indexBufferType = 
        mpD3DTechnique->GetAnnotationByName("index_buffer_type");

if(indexBufferType && indexBufferType->IsValid())
{
    ID3DX11EffectStringVariable* indexBufferTypeStr = indexBufferType->AsString();
    if(indexBufferTypeStr && indexBufferTypeStr->IsValid())
    {
        LPCSTR value; 
        if( SUCCEEDED ( indexBufferTypeStr->GetString( &value ) ) )
        {
            customIndexingMutatorName = MString(value);
        }
    }
}

ジオメトリ mutator

mutator は、その名前が示すように、指定された既存の頂点バッファ(MVertexBuffer)でデータのパッキングを修正するために使用できます。ストリーム ジェネレータと同様に、mutator は、mutator と記述子(MVertexBufferDescriptor)上のセマンティック名が一致した場合に、その記述子に関連付けられます。

mutator は、MPxVertexBufferMutator として表現されます。mutator は、MDrawRegistry に登録する必要があります。

使用する既存の mutator の指定方法を次の例に示します。

MString empty;

MHWRender::MVertexBufferDescriptor positionDesc(
        empty,
        MHWRender::MGeometry::kPosition,
        MHWRender::MGeometry::kFloat,
        3);
        
// Use the custom semantic name "swizzlePosition" which corresponds to a plug-in which will
// swizzle the x,y, and z channels of a position stream using this positionDesc.setSemanticName("swizzlePosition"); 

// Add the descriptor as part of the requirements for this shader
addGeometryRequirement(positionDesc);

次の図は、上記のコード例のリレーションシップを示しています。セマンティック名でルックアップを表示するために、「customMutator」がさらに追加されています。

図 73

mutator が更新を実行する必要がある場合、関連付けられている Maya DAG パスとデータ ストリーム インデックスが渡されます。更新を行うために既存の MVertexBuffer が渡され、データがその場で修正されます。

図 74

Developer Kit のサンプルである vertexBufferMutator には、スウィズルを実行する mutator を登録するための完全なコードがあります。hwPhongShader プラグインには、カスタム スウィズルの使用方法を例示するコードがあります。

その場編集の実行方法を示すサンプル コードからの抜粋を次に示します。

// Input argument
MVertexBuffer& vertexBuffer;

unsigned int vertexCount = vertexBuffer.vertexCount();
if (vertexCount <= 0)
    return;
    
// Acquire the buffer to fill with data. Data is not in CPU memory
float* buffer = (float*)vertexBuffer.acquire(vertexCount);
float* start = buffer;

for (unsigned int i = 0; i < vertexCount; ++i)
{
    // Here we swap the x, y and z values
    float x = buffer[0];
    buffer[0] = buffer[1];    // y --> x
    buffer[1] = buffer[2];    // z --> y
    buffer[2] = x;            // x --> z
    buffer += 3;
}

// Commit the buffer to signal completion. CPU data will be transferred to GPU memory.
vertexBuffer.commit(start);

インデックス mutator

インデックス mutator を使用して、指定された既存のインデックス バッファ(MIndexBuffer)のデータを修正できます。mutator は、mutator と記述子(MIndexBufferDescriptor)上のセマンティック名が一致した場合に、その記述子に関連付けられます。

mutator は、MPxIndexBufferMutator として表現されます。mutator は、MDrawRegistry に登録する必要があります。

使用する既存の mutator の指定方法を次の例に示します。

MHWRender::MIndexBufferDescriptor indexingRequirement(
        MHWRender::MIndexBufferDescriptor::kCustom,
        customIndexingMutatorName,
        MHWRender::MGeometry::kTriangles);
        
// Add the descriptor as part of the requirements for this shader
addIndexingRequirement(indexingRequirement);

次の図は、上記のコード例のリレーションシップを示しています。セマンティック名でルックアップを表示するために、「customMutator」がさらに追加されています。

図 75

mutator が更新を実行する必要がある場合、関連付けられたデータス トリーム インデックスと事前に生成された頂点バッファ配列が渡されます。更新を行うために既存の MIndexBuffer が渡され、データがその場で修正されます。

図 76

Developer Kit のサンプルであり、dx11Shader プロジェクトの一部である crackFreePrimitiveGenerator には、PN AEN パッチ生成を実行する mutator を登録するための完全なコードがあります。dx11Shader プラグインには、MayaUberShader.fx エフェクト ファイルの「TessellationON」または「Wireframe」テクニックが選択されているときの、カスタム パッチの使用方法を例示するコードが含まれています。

その場編集の実行方法を示すサンプル コードからの抜粋を次に示します。

// Input argument
MIndexBuffer& indexBuffer;

// Acquire the buffer to fill with data. Data is not in CPU memory
unsigned int* buffer = (unsigned int *) indexBuffer.acquire(numTri * triSize, true);
unsigned int* start = buffer;

for (unsigned int i = 0; i < numTri; ++i)
{
    // Here we fill PN AEN information
    buffer[0] = vertexId0; 
    buffer[1] = vertexId1;
    buffer[2] = vertexId2;
    // ...
    buffer[17] = dominantVertex2;
    buffer += triSize;
}

// Commit the buffer to signal completion. CPU data will be transferred to GPU memory.
indexBuffer.commit(start);
primitiveStride = triSize; // Output argument
return MHWRender::MGeometry::kPatch;

パイプラインの概要:

図 77

上図には、ジェネレータと mutator をパイプライン全体にフィットさせる方法が示されています。頂点バッファの生成と変異、およびプリミティブ生成(記述子によって表現される)に関する(シェーダ パラメータに基づいた)カスタム要件を持つシェーダのオーバーライドが示されています。更新を行う必要がある場合(更新フェーズ)は、頂点またはインデックス バッファを更新または作成するために呼び出されるジェネレータおよび mutator と名前が一致することが MPxGeometryOverride でのシェーダ要件となります。これらのカスタム バッファは、描画フェーズに到達するまで、パイプラインを流れる 1 つまたは複数のレンダー項目で使用できます。この時点で、カスタム データ要件を開始したシェーダ パラメータにカスタム頂点とインデックス バッファをバインドできます。