API Overview
API レイヤー
OpenSubidv はいくつかのレイヤーで構成されたライブラリです。この構造は様々な計算リソース (デバイス)を用いて、開発者が必要なレイヤーを必要に応じて使えるようにできています。 いくつかのレイヤーがありますが、外向きのインタフェースと内部用のものとがあります。
レイヤー一覧:
最も低レベルのレイヤーで、サブディジョンの中核アルゴリズムを実装しており 一貫性のある結果を保証します。ほとんどの場合は単純な公開型と定数を利用する です。 | |
効率的な分割のためのトポロジの中間表現を表すクラス群があります。 Vtr はライブラリの内部用で、公開インタフェースではありません | |
クライアントが与えるジオメトリを Osd で並列処理可能な連続データに変換する 中心となるインタフェースです。また Far 単独でもシングルスレッドの完全な サブディビジョン及び補間アルゴリズムが利用できます。 | |
様々なデバイスでの並列サブディビジョンカーネルおよび描画ユーティリティ を実装したクラス群です。TBB, CUDA, OpenCL, GLSL, DirectX などをサポート しています。 |
クライアントのメッシュデータは Far レイヤーを使って OpenSubdiv に与えます。通常は 結果は Osd レイヤーで受け取りますが、Far レイヤーだけで使うこともできます。
トポロジや頂点データを OpenSubdiv に与える方法はいくつかありますが、最終的には Vtr と Sdc を使ってトポロジ解析が行われます。
どのAPIを使うのか
OpenSubdiv の階層インタフェースは柔軟にできているので、様々なアプリケーションを 高速かつ堅牢にすることができます。ただ多くのクラスや機能があるため正しい方法を 見つけるのが難しい場合もあります。ここに一般的な用途をいくつかあげておきます。
アプリケーションが必要な機能:
リミットサーフェス | スムーズサーフェスの表現はポリゴンの近似で行っている アプリケーションがほとんどですが、双3次パッチでは C 2 連続なパッチを使うことが出来ます (例: 変形するディスプレイスメントマップ、スムーズ 法線、セミシャープクリースなど) |
変形サーフェス | オフラインレンダラでは通常一度に一枚のフレームを生成 しますが、インタラクティブなゲームなどでは毎フレーム 変形するキャラクターのサーフェスを計算する必要があり ます。メッシュのトポロジが変わらなければ、大部分の 計算をまとめて事前に行うことができるため、OpenSubdiv はステンシルテーブルを使って事前計算により効率的な サブディビジョンを行います。 |
マルチスレッド | OpenSubdiv は CPU や GPU など様々なデバイスやAPI における並列性を活かしたインタフェースを提供しています |
GPU 描画 | アプリケーションがインタラクティブな描画を行う場合、 OpenSubdivの D3D11/OpenGL バックエンドが利用できます。 これらはプログラマブルシェーディングを完全にサポート しています。 |
利用例1:単純なサブディビジョン
以下に示すのは均一分割によってサブディビジョンを行う最も簡単な例です。
- 分割をしたい頂点データクラスを定義します。 このクラスは Clear() および AddWithWeight() を実装する必要があります。
struct Vertex { void Clear() { x = y = z = 0; } void AddWithWeight(Vertex const &src, float weight) { x += weight * src.x; y += weight * src.y; z += weight * src.z; } float x, y, z; };
- Far::TopologyRefiner を Far::TopologyDescriptor から作成します。
Far::TopologyDescriptor desc; desc.numVertices = <頂点数> desc.numFaces = <面数> desc.numVertsPerFace = <各面の頂点数配列> desc.vertIndicesPerFace = <頂点インデックス配列> Far::TopologyRefiner * refiner = Far::TopologyRefinerFactory<Descriptor>::Create(desc);
- RefineUniform() を呼び出しトポロジを 'maxlevel' まで分割します。
refiner->RefineUniform(Far::TopologyRefiner::UniformOptions(maxlevel));
- レベル 'level' の頂点データを Far::PrimvarRefiner を使って補間します。
Far::PrimvarRefiner primvarRefiner(*refiner); Vertex const *src = <coarse vertices> Vertex *dst = <refined vertices> primvarRefiner.Interpolate(level, src, dst);
- 分割したレベルでのトポロジは Far::TopologyLevel から取得できます。
Far::TopologyLevel const & refLastLevel = refiner->GetLevel(maxlevel); int nverts = refLastLevel.GetNumVertices(); int nfaces = refLastLevel.GetNumFaces(); for (int face = 0; face < nfaces; ++face) { Far::ConstIndexArray fverts = refLastLevel.GetFaceVertices(face); // do something with dst and fverts }
- 以上で終わりです、詳しくは far_tutorial_0 を参照してください。
利用例2:GLでアニメーションするメッシュを適応型テセレーションで描画
次の例はOpenSubdiv を使って適応テセレーションパッチをGLで描画する例です。Osd レイヤーを使えば GLと様々なAPIとの間のやりとりを楽に行えます。またトポロジ一定でアニメーションするメッシュを 効率的に分割するために、ステンシルテーブルを用いて時間変化する頂点の分割を行っています。
以下の例では Osd::GLMesh ユーティリティクラスを使っています。GLMesh は Osd レイヤーの ステンシルテーブル、パッチテーブル、頂点バッファおよびエバリュエータなどをまとめたクラスです。 これらのクラスをそれぞれ独立に利用することもできます。
1. 利用例1と同様に、Far::TopologyRefiner を Far::TopologyDescriptor から作成します。
- Osd::Mesh を準備します。この例ではエンドキャップに B スプラインを使っています。
int numVertexElements = 3; // x, y, z Osd::MeshBitset bits; bits.set(Osd::MeshAdaptive, true); // set adaptive bits.set(Osd::MeshEndCapBSplineBasis, true); // use b-spline basis patch for endcap. Osd::GLMeshInterface *mesh = new Osd::Mesh<Osd::CpuGLVertexBuffer, Far::StencilTable, Osd::CpuEvaluator, Osd::GLPatchTable> (refiner, numVertexElements, 0, level, bits);
3. コントロール頂点を更新し、分割します (Osd::Mesh::Refine() は内部で Osd::CpuEvaluator::EvalStencils() を呼んでいます)
mesh->UpdateVertexBuffer(&vertex[0], 0, nverts); mesh->Refine();
- インデックスバッファ、パッチパラメータバッファ、頂点バッファをバインドします。
// インデックスバッファ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh->GetPatchTable()->GetPatchIndexBuffer()); // 頂点バッファ glBindBuffer(GL_ARRAY_BUFFER, mesh->BindVertexBuffer()); glEnableVertexAttribArray(0); glVertexAttribPointer(0, numVertexElements, GL_FLOAT, GL_FALSE, numVertexElements*sizeof(float), 0); // パッチパラメータバッファ glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_BUFFER, mesh->GetPatchTable()->GetPatchParamTextureBuffer());
5. 描画します。この例では Bスプラインのエンドキャップを使っているため、パッチテーブルにはパッチ配列がひとつしかありません。 もし他のエンドキャップを使った場合にはパッチ配列でループする必要があります。それぞれのパッチタイプでの GLSLプログラムの構成方法は osd shader interface をご覧ください。
Osd::PatchArray const & patch = mesh->GetPatchTable()->GetPatchArrays()[0]; Far::PatchDescriptor desc = patch.GetDescriptor(); int numVertsPerPatch = desc.GetNumControlVertices(); // 16 for B-spline patches glUseProgram(BSplinePatchProgram); glPatchParameteri(GL_PATCH_VERTICES, numVertsPerPatch); glDrawElements(GL_PATCHES, patch.GetNumPatches() * numVertsPerPatch, GL_UNSIGNED_INT, 0);
6. メッシュのアニメーションに応じて、ステップ3から繰り返します(頂点の更新、分割、描画)。 glViewer および他のサンプルプログラムも参照してください。