はじめに
OpenGLは、OpenSceneGraphのバックエンドであり、幾何プリミティブ(ポイント、ライン、三角形、ポリゴンフェースなど)を使用して、3次元世界のすべてのオブジェクトを構築します。
これらのプリミティブは、頂点の座標、法線成分、色データ、テクスチャ座標を含む頂点に関するデータによって定義されます。 このデータは特別な配列に保存されます。 プリミティブは、たとえば、それらを記述するオブジェクトに頂点インデックスのリストを指定することで形成できます。 このメソッドは頂点配列メソッドと呼ばれ、メモリ内の冗長な頂点のストレージを排除し、優れたパフォーマンスを発揮します。
さらに、OpenGLは、ビデオメモリで一度準備されたプリミティブを再利用できる場合、いわゆる表示リストのメカニズムを使用できます。これにより、静的オブジェクトの表示が大幅に高速化されます。
デフォルトでは、OSGは頂点配列法と表示リスト法を使用してジオメトリをレンダリングします。 ただし、ジオメトリデータの表示方法によっては、レンダリング戦略が変更される場合があります。 この記事では、OSGでジオメトリを操作するための基本的なテクニックについて説明します。
1.クラスGeodeおよびDrawable
osg :: Geodeクラスはターミナルであり、シーンツリーのいわゆる「リーフ」ノードです。 子ノードを持つことはできませんが、ジオメトリのレンダリングに必要なすべての情報が含まれています。 彼の名前Geodeは、geometryノードの略です。
エンジンで処理される幾何データは、osg :: Geodeクラスによって管理されるosg :: Drawableクラスのオブジェクトのセットに格納されます。 osg :: Drawableクラスは純粋に仮想的なクラスです。 OpenGLパイプラインによって処理される3次元モデル、画像、およびテキストである多くのサブクラスがそこから継承されます。 OSGは、エンジンで描画できるすべての要素として、drawableを指します。
osg :: Geodeクラスは、ドロアブルをアタッチおよびデタッチするためのいくつかのメソッドを提供します。
- パブリックメソッドaddDrawable()-osg :: Geodeクラスのインスタンス内の描画可能な要素にポインターを渡します。 これらの要素はすべて、osg :: ref_ptr <>スマートポインターによって制御されます。
- パブリックメソッドremoveDrawable()およびremoveDrawables()は、osg :: Geodeからオブジェクトを削除し、その参照カウントを減らします。 removeDrawable()メソッドは、関心のある要素へのポインターを単一のパラメーターとして受け取り、removeDrawables()メソッドは、osg :: Geodeオブジェクト配列から削除される初期インデックスと要素数の2つのパラメーターを受け取ります。
- getDrawable()メソッドは、パラメーターとして渡されたインデックスにある要素へのポインターを返します。
- getNumDrawables()メソッドは、osg :: Geodeにアタッチされた要素の総数を返します。 たとえば、osg :: Geodeからすべての要素を削除するには、このようなコードを使用できます
geode->removeDrawables(0, geode->getNumDrawables());
2.単純な図形の描画
OSGはosg :: ShapeDrawableクラスを提供します。これはosg :: Drawableクラスの子孫であり、単純な3次元プリミティブを作成するように設計されています。 このクラスには、特定のジオメトリおよびその他のパラメーターに関する情報を格納するosg :: Shapeオブジェクトが含まれます。 プリミティブは、たとえばsetShape()メソッドを使用して生成されます
shapeDrawable->setShape(new osg::Box(osg::Vec3(1.0f, 0.0f, 0.0f), 10.0f, 10.0f, 5.0f));
ポイント(1.0、0.0、0.0)に幾何学的中心を持ち、幅と高さが10、深さが5単位の長方形のボックスを作成します。 osg :: Vec3クラスは、3次元空間のベクトルを定義します(さらに、対応する次元のベクトルを記述するosg :: Vec2およびosg :: Vec4クラスも提示されます)。
最も一般的なプリミティブは、osg :: Box、osg :: Capsule、osg :: Cone、osg :: Cylinderおよびosg :: SphereクラスによってOSGで表されます。
このメカニズムの適用例を考えてみましょう。
main.h
#ifndef MAIN_H #define MAIN_H #include <osg/ShapeDrawable> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H
main.cpp
#include "main.h" int main(int argc, char *argv[]) { (void) argc; (void) argv; osg::ref_ptr<osg::ShapeDrawable> shape1 = new osg::ShapeDrawable; shape1->setShape(new osg::Box(osg::Vec3(-3.0f, 0.0f, 0.0f), 2.0f, 2.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape2 = new osg::ShapeDrawable; shape2->setShape(new osg::Cone(osg::Vec3(0.0f, 0.0f, 0.0f), 1.0f, 1.0f)); shape2->setColor(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::ShapeDrawable> shape3 = new osg::ShapeDrawable; shape3->setShape(new osg::Sphere(osg::Vec3(3.0f, 0.0f, 0.0f), 1.0f)); shape3->setColor(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(shape1.get()); root->addDrawable(shape2.get()); root->addDrawable(shape3.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
この例では特にコメントは不要です。プログラムでは、3つの単純な形状が作成されます。コンパイルと起動の後、このような結果が表示されます。
例に示されているメカニズムは単純で単純ですが、ジオメトリを作成する最も効果的な方法ではなく、テスト専用に使用できます。 osg :: Geometryクラスは、高性能のOSGベースのアプリケーションでジオメトリを作成するために使用されます。
3.ジオメトリデータの保存:クラスosg ::配列およびosg ::ジオメトリ
osg :: Arrayクラスは、OpenGL関数に渡されたデータを保存するためのいくつかの子孫が継承される基本的な抽象クラスです。 このクラスの操作は、C ++標準ライブラリのstd :: vectorの操作に似ています。 次のコードは、push_back()メソッドを使用して頂点配列にベクトルを追加する方法を示しています
vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
OSG配列はヒープに割り当てられ、スマートポインターによって管理されます。 ただし、これはosg :: Vec3やosg :: Vec2などのスタック要素にも作成できる配列要素には適用されません。
osg :: Geometryクラスは、頂点配列で動作するOpenGL関数のラッパーです。 osg :: Drawableクラスから派生し、osg :: Geodeオブジェクトのリストに簡単に追加できます。 このクラスは、上記の配列を入力として受け取り、それを使用してOpenGLを使用してジオメトリを生成します。
4.頂点とその属性
頂点は、ジオメトリプリミティブの原子単位です。 これには、2次元または3次元空間のポイントを記述する多くの属性があります。 属性には、位置、色、法線ベクトル、テクスチャ座標、フォグ座標などが含まれます。 他の属性については、オプションで存在できるように、トップは常に空間内の位置を持っている必要があります。 OpenGLは16個の基本的な頂点属性をサポートし、異なる配列を使用してそれらを保存できます。 すべての属性配列はosg :: Geometryクラスでサポートされており、set * Array()という形式のメソッドを使用して設定できます。
OpenSceneGraphの頂点属性
属性 | データ型 | Osg ::ジオメトリメソッド | 同等のOpenGL呼び出し |
---|---|---|---|
役職 | 3-ベクトル | setVertexArray() | glVertexPointer() |
普通 | 3-ベクトル | setNormalArray() | glNormalPointer() |
色 | 4-ベクトル | setColorArray() | glColorPointer() |
二次色 | 4-ベクトル | setSecondaryColorArray() | glSecondaryColorPointerEXT() |
フォグ座標 | 浮く | setFogCoordArray() | glFogCoordPointerEXT() |
テクスチャ座標 | 2または3ベクトル | setTexCoordArray() | glTexCoordPointer() |
その他の属性 | ユーザー定義 | setVertexArribArray() | glVertexAttribPointerARB() |
原則として、各頂点に独自の属性を設定する必要があります。これにより、同じサイズの属性の複数の配列が形成されます。そうしないと、配列のサイズの不一致によりエンジンの動作が未定義になります。 OSGは、頂点属性をリンクするためのさまざまな方法をサポートしています。
geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
は、各頂点と頂点の各色が互いに1対1で相関していることを意味します。 ただし、そのようなコードを見ると
geom->setColorBinding(osg::Geometry::BIND_OVERALL);
次に、彼はジオメトリ全体に1色を適用します。 同様に、他の属性間の関係は、setNormalBinding()、setSecondaryColorBinding()、setFogCoordBinding()、およびsetVertexAttribBinding()メソッドを呼び出すことで構成できます。
5.ジオメトリプリミティブのセット
頂点属性配列を定義した後の次のステップは、頂点データのレンダリング方法を記述することです。 仮想osg :: PrimitiveSetクラスは、頂点のセットからレンダラーによって生成された幾何学的プリミティブを制御するために使用されます。 osg :: Geometryクラスは、ジオメトリプリミティブのセットを操作するためのいくつかのメソッドを提供します。
- addPrimitiveSet()-osg :: Geometryオブジェクトの一連のプリミティブにポインターを渡します。
- removePrimitiveSet()-プリミティブのセットを削除します。 パラメータとして、セットの初期インデックスと削除するセットの数を取ります。
- getPrimitiveSet()-パラメーターとして渡されたインデックスにあるプリミティブのセットを返します。
- getNumPrimitiveSets()-このジオメトリに関連付けられたプリミティブのセットの総数を返します。
osg :: PrimitiveSetクラスは抽象的であり、インスタンス化することはできませんが、osg :: DrawArraysやosg :: DrawElementsUIntなど、OpenGLが動作するプリミティブのセットをカプセル化するいくつかの派生クラスはそれを継承します。
osg :: DrawArraysクラスは、頂点配列のいくつかの連続した要素を使用して幾何学的プリミティブを構築します。 メソッドを呼び出して作成し、ジオメトリにアタッチできます。
geom->addPrimitiveSet(new osg::DrawArrays(mode, first, count));
最初のパラメーターモードは、プリミティブタイプを対応するOpenGLプリミティブタイプに設定します:GL_POINTS、GL_LINE_STRIP、GL_LINE_LOOP、GL_LINES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN、GL_TRIANGLES、GL_QUAD_STRIP、GL_QUADSおよびGL_POLY。
最初と2番目のパラメーターは、頂点配列の最初のインデックスと、ジオメトリを生成する頂点の数を指定します。 さらに、OSGは、指定された頂点の数がモードで指定されたジオメトリを構築するのに十分かどうかをチェックしません。これにより、アプリケーションがクラッシュする可能性があります。
6.例-塗られた正方形を描く
上記のすべてを簡単な例として実装します
完全なクワッドソースコード
main.h
main.cpp
#ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgViewer/Viewer> #endif // MAIN_H
main.cpp
#include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get()); quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
コンパイルと実行の後、次のような結果が得られます
この例では説明が必要です。 そのため、まず、座標が格納されている正方形の頂点の配列を作成します
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); vertices->push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); vertices->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
次に、法線の配列を設定します。 単純な場合、頂点ごとに法線を作成する必要はありません-正方形の平面に垂直に向けられた1つの単位ベクトルを記述するだけです
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
各頂点の色を設定します。
osg::ref_ptr<osg::Vec4Array> colors = new osg::Vec4Array; colors->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); colors->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); colors->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f));
次に、正方形の記述が保存されるジオメトリオブジェクトを作成します。このオブジェクトは、レンダーによって処理されます。 頂点の配列をこのジオメトリに渡します
osg::ref_ptr<osg::Geometry> quad = new osg::Geometry; quad->setVertexArray(vertices.get());
法線の配列を渡すと、法線のバインド方法( "binding")を指定することにより、すべての頂点に1つの法線が使用されることをエンジンに通知しますBIND_OVAERALL
quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL);
逆に、頂点の色を渡すと、各頂点が独自の色を持つことになります
quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX);
次に、ジオメトリのプリミティブセットを作成します。 インデックス(0)の頂点を最初の頂点として、頂点配列から正方形(GL_QUADS)の面を生成する必要があることを示します。頂点の総数は4になります。
quad->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
さて、ジオメトリの転送とレンダーの開始を説明する価値はないと思います
osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(quad.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run();
上記のコードは、純粋なOpenGLの次の設計と同等です。
static const GLfloat vertices[][3] = { … }; glEnableClientState( GL_VERTEX_ARRAY ); glVertexPointer( 4, GL_FLOAT, 0, vertices ); glDrawArrays( GL_QUADS, 0, 4 );
7.プリミティブの頂点のインデックス付け
osg :: DrawArraysクラスは、頂点データを隙間なく配列から直接読み取るときにうまく機能します。 ただし、同じ頂点がオブジェクトの複数の面に属する場合、これはそれほど効果的ではありません。 例を見てみましょう。
キューブには8つの頂点があります。 ただし、図からわかるように(平面上の立方体の展開を見る)、いくつかの頂点は複数の面に属します。 12個の三角形の面の立方体を構築すると、これらの頂点が繰り返され、8個の頂点の配列の代わりに、36個の頂点の配列が得られます。そのほとんどは実際には同じ頂点です!
OSGには、osg :: DrawElementsUInt、osg :: DrawElementsUByteおよびosg :: DrawElementsUShortというクラスがあり、これらは説明された問題を解決するためにデータとして頂点インデックス配列を使用します。 インデックス配列には、面やその他のジオメトリ要素を記述するプリミティブの頂点インデックスが格納されます。 これらのクラスをキューブに適用する場合、インデックスの配列を介して面に関連付けられている8つの頂点の配列を格納するだけで十分です。
タイプosg :: DrawElements *のクラスは、標準クラスstd :: vectorと同じ方法で構築されます。 このようなコードは、インデックスを追加するために使用できます。
osg::ref_ptr<osg::DrawElementsUInt> de = new osg::DrawElementsUInt(GL_TRIANGLES); de->push_back(0); de->push_back(1); de->push_back(2); de->push_back(3); de->push_back(0); de->push_back(2);
このコードは、図に示すキューブの前面を定義します。
もう一つの実例を考えてみましょう-八面体
興味深いのは、頂点が6つしか含まれていないためですが、各頂点は最大4つの三角形の面に入ります! osg :: DrawArraysを使用して、24個の頂点の配列を作成して、8つの面すべてを表示できます。 ただし、別の方法で行います。頂点を6つの要素の配列に格納し、osg :: DrawElementsUIntクラスを使用して面を生成します。
八面体の例の完全なソース
main.h
main.cpp
#ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/SmoothingVisitor> #include <osgViewer/Viewer> #endif
main.cpp
#include "main.h" int main(int argc, char *argv[]) { osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f); osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2; (*indices)[ 3] = 0; (*indices)[ 4] = 4; (*indices)[ 5] = 1; (*indices)[ 6] = 4; (*indices)[ 7] = 5; (*indices)[ 8] = 1; (*indices)[ 9] = 4; (*indices)[10] = 3; (*indices)[11] = 5; (*indices)[12] = 3; (*indices)[13] = 2; (*indices)[14] = 5; (*indices)[15] = 1; (*indices)[16] = 5; (*indices)[17] = 2; (*indices)[18] = 3; (*indices)[19] = 0; (*indices)[20] = 2; (*indices)[21] = 0; (*indices)[22] = 3; (*indices)[23] = 4; osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->addPrimitiveSet(indices.get()); osgUtil::SmoothingVisitor::smooth(*geom); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
このコードをさらに詳しく分析してみましょう。 もちろん、最初に行うことは、6つの頂点の配列を作成することです
osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array(6); (*vertices)[0].set( 0.0f, 0.0f, 1.0f); (*vertices)[1].set(-0.5f, -0.5f, 0.0f); (*vertices)[2].set( 0.5f, -0.5f, 0.0f); (*vertices)[3].set( 0.5f, 0.5f, 0.0f); (*vertices)[4].set(-0.5f, 0.5f, 0.0f); (*vertices)[5].set( 0.0f, 0.0f, -1.0f);
ポインターおよび演算子[]演算子の逆参照操作を使用して座標のベクトルにアクセスすることにより、各頂点を直接初期化します(osg :: Arrayはデバイスでstd :: vectorに似ていることを覚えています)。
頂点インデックスのリストとして面を作成します
osg::ref_ptr<osg::DrawElementsUInt> indices = new osg::DrawElementsUInt(GL_TRIANGLES, 24); (*indices)[ 0] = 0; (*indices)[ 1] = 1; (*indices)[ 2] = 2; // 0 (*indices)[ 3] = 0; (*indices)[ 4] = 4; (*indices)[ 5] = 1; // 1 (*indices)[ 6] = 4; (*indices)[ 7] = 5; (*indices)[ 8] = 1; // 2 (*indices)[ 9] = 4; (*indices)[10] = 3; (*indices)[11] = 5; // 3 (*indices)[12] = 3; (*indices)[13] = 2; (*indices)[14] = 5; // 4 (*indices)[15] = 1; (*indices)[16] = 5; (*indices)[17] = 2; // 5 (*indices)[18] = 3; (*indices)[19] = 0; (*indices)[20] = 2; // 6 (*indices)[21] = 0; (*indices)[22] = 3; (*indices)[23] = 4; // 7
面は三角形になり、8になります。つまり、インデックスのリストには24個の要素が含まれます。 この配列の面のインデックスは連続しています。たとえば、面0は頂点0、1、および2によって形成されます。 面1-頂点0、4、および1。 面2-頂点4、5、1など。 顔の顔を見ると、頂点は反時計回りにリストされます(上の図を参照)。
前の例で実行したジオメトリを作成するための追加手順。 実行しなかった唯一のことは、この例では
osgUtil::SmoothingVisitor::smooth(*geom);
実際、顔の頂点が与えられている場合、その法線を計算するのは簡単です。 複数の面が収束する頂点で、特定の平均法線が計算されます-収束する面の法線が合計され、結果の合計が再び正規化されます。 これらの操作(さらに多く!)は、osgUtilライブラリのクラスを使用してエンジン自体で実行できます。 したがって、この例では、* .proファイルにこのライブラリを使用してプログラムをビルドするための命令をリンカーに追加します
octahedron.pro
CONFIG(debug, debug|release) { TARGET = $$join(TARGET,,,_d) . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtild } else { . . . LIBS += -L$$OSG_LIB_DIRECTORY -losgUtil }
その結果、次の結果が得られます
この仕組みを理解するには、OpenGLパイプラインを検討してください
頂点配列メカニズムは、OpenGL呼び出しの数を減らします。 クライアント側で使用されるアプリケーションメモリに頂点データを保存します。 サーバー側のOpenGLパイプラインは、さまざまな頂点配列にアクセスします。 図に示すように、OpenGLはクライアント側の頂点バッファーからデータを受信し、規則正しい方法でプリミティブを構築します。 これは、osg :: Geometryクラスのset * Array()メソッドを使用してデータを処理する方法です。 osg :: DrawArraysクラスは、これらの配列を直接調べて表示します。
osg :: DrawElements *を使用すると、頂点配列の次元が減少し、パイプラインに転送される頂点の数が減少します。 インデックスの配列を使用すると、サーバー側で頂点キャッシュを作成できます。 OpenGLは、クライアント側の頂点バッファーから読み取る代わりに、キャッシュから頂点データを読み取ります。 これにより、全体的なレンダリングパフォーマンスが大幅に向上します。
8.ポリゴンメッシュ処理技術
OpenSceneGraphは、シーンジオメトリオブジェクトのポリゴンメッシュを処理するためのさまざまな手法をサポートしています。 ポリゴンリダクションやテッセレーションなどのこれらの前処理方法は、ポリゴンモデルの作成と最適化によく使用されます。 シンプルなインターフェースを備えていますが、その過程で多くの複雑な計算を実行し、オンザフライでの実行にはあまり適していません。
説明する手法には次のものがあります。
- osgUtil :: Simplifier-ジオメトリの三角形の数を減らします。 単純化()パブリックメソッドは、モデルジオメトリを単純化するために使用されます。
- osgUtil :: SmootingVisitor-法線の計算。 smooth()メソッドを使用して、モデルの平滑化された法線を個別に計算し、法線の配列を介して明示的に設定する代わりに使用できます。
- osgUtil :: TangentSpaceGenerator-モデル頂点の接線基底ベクトルの生成。 generate()メソッドを呼び出すことで起動され、getTangentArray()、getNormalArray()、およびgetBinormalArray()メソッドによって返された結果を保存します。 これらの結果は、GLSLでシェーダーを作成するときに、さまざまな頂点属性に使用できます。
- osgUtil :: Tesselator-ポリゴンメッシュのテッセレーションを実行します-複雑なプリミティブを単純なシーケンスに分割します(retesselatePolygons()メソッド)
- osgUtil :: TriStripVisitor-ジオメトリックサーフェスを三角形の面のストリップのセットに変換します。これにより、効率的なメモリ消費でのレンダリングが可能になります。 stripify()メソッドは、GL_TRIANGLE_STRIPセットに基づいて、モデルプリミティブのセットをジオメトリに変換します。
すべてのメソッドは、osg :: Geometry&linkによって渡されるパラメーターとしてオブジェクトのジオメトリを受け入れます。たとえば、次のようになります
osgUtil::TriStripVisitor tsv; tsv.stripify(*geom);
geomは、スマートポインターによって記述されるジオメトリのインスタンスを指します。
クラスosg :: Simplifier、osg :: SmoothingVisitorおよびosg :: TriStripVisitorは、たとえばシーングラフ内のノードを直接操作できます。
osgUtil::TriStripVisitor tsv; node->accept(tsv);
accept()メソッドは、指定された操作がosg :: Geodeタイプのノードに保存されているシーンツリーのこの部分のすべてのターミナルノードに適用されるまで、すべての子ノードを処理します。
実際にテッセレーション技術を試してみましょう。
フルテッセレータのサンプルコード
main.h
main.cpp
#ifndef MAIN_H #define MAIN_H #include <osg/Geometry> #include <osg/Geode> #include <osgUtil/Tessellator> #include <osgViewer/Viewer> #endif
main.cpp
#include "main.h" int main(int argc, char *argv[]) { /* ----- | _| | |_ | | ----- */ osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array; vertices->push_back( osg::Vec3(0.0f, 0.0f, 0.0f) ); // 0 vertices->push_back( osg::Vec3(2.0f, 0.0f, 0.0f) ); // 1 vertices->push_back( osg::Vec3(2.0f, 0.0f, 1.0f) ); // 2 vertices->push_back( osg::Vec3(1.0f, 0.0f, 1.0f) ); // 3 vertices->push_back( osg::Vec3(1.0f, 0.0f, 2.0f) ); // 4 vertices->push_back( osg::Vec3(2.0f, 0.0f, 2.0f) ); // 5 vertices->push_back( osg::Vec3(2.0f, 0.0f, 3.0f) ); // 6 vertices->push_back( osg::Vec3(0.0f, 0.0f, 3.0f) ); // 7 osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array; normals->push_back( osg::Vec3(0.0f, -1.0f, 0.0f) ); osg::ref_ptr<osg::Geometry> geom = new osg::Geometry; geom->setVertexArray(vertices.get()); geom->setNormalArray(normals.get()); geom->setNormalBinding(osg::Geometry::BIND_OVERALL); geom->addPrimitiveSet(new osg::DrawArrays(GL_POLYGON, 0, 8)); osg::ref_ptr<osg::Geode> root = new osg::Geode; root->addDrawable(geom.get()); osgViewer::Viewer viewer; viewer.setSceneData(root.get()); return viewer.run(); }
この例の頂点の空間位置に基づいて、GL_POLYGONタイプの1つの面の生成を使用して、8つの頂点の非凸多角形を作成しようとしていることがわかります。 この例のアセンブリと実行は、予想される結果が機能しないことを示しています-例は正しく表示されていません
この問題を修正するには、構築されたジオメトリをビューアーに渡す前にテッセレーションする必要があります
osgUtil::Tessellator ts; ts.retessellatePolygons(*geom);
その後、正しい結果が得られます
どのように機能しますか?正しいテッセレーションを使用しない非凸ポリゴンは、期待どおりに表示されません。OpenGLはパフォーマンスを最適化しようとしているため、単純な凸ポリゴンと見なされるか、単に無視されるため、まったく予期しない結果が生じる可能性があります。
osgUtil :: Tessellatorクラスは、アルゴリズムを使用して、凸多角形を一連の非凸多角形に変換します。この例では、ジオメトリをGL_TRIANGLE_STRIPに変換します。
このクラスは、穴ポリゴンと自己交差ポリゴンを処理できます。パブリックsetWindingType()メソッドを使用して、GLU_TESS_WINDING_ODDやGLU_TESS_WINDING_NONZEROなど、複雑なポリゴンの内側と外側の領域を指定するさまざまな処理ルールを定義できます。
おわりに
この記事では、3次元オブジェクトのジオメトリがOSGエンジンでどのように保存および処理されるかについての基本的な理解を得ました。この記事で検討されている単純で印象的な例は、エンジンの能力の限界だとは思わないでください。これらの例は、開発者がOpenSceneGraphの仕組みを理解するのに役立ちます。この理解がなければ、より複雑な作業を想像することは困難です。
この記事は、OpenSceneGraph 3.0本の対応する章のテキストの翻訳と処理に基づいています。ガイドレビューは初心者です。すべての例は私が個人的に確認しており、ソースはここで入手できます。 継続するには...