OpenSceneGraph:场景几何基础

图片

引言


OpenGL是OpenSceneGraph的后端,它使用几何图元(例如点,线,三角形和多边形面)构造三维世界中的所有对象。

这些图元由有关其顶点的数据定义,这些数据包括顶点的坐标,法线分量,颜色数据和纹理坐标。 此数据存储在特殊数组中。 例如,可以通过为描述基元的对象指定一个顶点索引列表来形成基元。 此方法称为顶点数组方法,它消除了内存中多余顶点的存储,并具有良好的性能。

此外,当在视频存储器中准备好的原语可以被重用时,OpenGL可以使用所谓的显示列表机制,从而显着加快了静态对象的显示速度。

默认情况下,OSG使用顶点数组方法和显示列表方法来渲染几何图形。 但是,可以根据呈现几何数据的方式更改渲染策略。 在本文中,我们将介绍在OSG中使用几何的基本技术。

1.类Geode和Drawable


osg :: Geode类是一个终端,即场景树的所谓“叶”节点。 它不能具有子节点,但是它包含用于渲染几何图形的所有必要信息。 它的名称Geode是几何节点的缩写。

引擎要处理的几何数据存储在osg :: Drawable类的对象集中,该对象集由osg :: Geode类管理。 osg :: Drawable类是纯虚拟类。 它继承了许多子类,它们是三维模型,OpenGL管道处理的图像和文本。 OSG将drawable称为引擎可以绘制的所有元素。

osg :: Geode类提供了许多用于附着和分离可绘制对象的方法:

  • 公共方法addDrawable()-将指针传递给osg :: Geode类实例中的drawable元素。 所有这些元素均由osg :: ref_ptr <>智能指针控制。
  • 公共方法removeDrawable()和removeDrawables()从osg :: Geode中删除对象,并减少其引用计数。 removeDrawable()方法将指向感兴趣元素的指针作为单个参数,而removeDrawables()方法采用两个参数:初始索引和要从osg :: Geode对象数组中删除的元素数。
  • getDrawable()方法返回指向作为参数传递的索引处的元素的指针。
  • getNumDrawables()方法返回附加到osg :: Geode的元素总数。 例如,要从osg :: Geode中删除所有元素,可以使用以下代码

geode->removeDrawables(0, geode->getNumDrawables()); 

2.绘制简单的形状


OSG提供了osg :: ShapeDrawable类,它是osg :: Drawable类的后代,旨在创建简单的三维基元。 此类包括一个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类在三维空间中定义矢量(此外,还提供了描述相应维数的osg :: Vec2和osg :: Vec4类)。

在OSG中,最流行的原语由osg :: Box,osg :: Capsule,osg :: Cone,osg :: Cylinder和osg :: Sphere表示。

考虑该机制的应用示例。

主文件
 #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(); } 

这个例子尤其不需要注释:在程序中创建了三个简单的形状,编译和启动后,我们将看到这样的结果



示例中显示的机制非常简单明了,但这并不是创建几何体的最有效方法,只能用于测试。 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.顶点及其属性


顶点是几何图元的原子单位。 它具有许多描述二维或三维空间中的点的属性。 属性包括:位置,颜色,法线向量,纹理坐标,雾坐标等。 顶部必须始终在空间中具有位置,因为对于其他属性,可以选择存在它们。 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); 

表示每个顶点和顶点的每种颜色都是一对一关联的。 但是,如果您看这样的代码

 geom->setColorBinding(osg::Geometry::BIND_OVERALL); 

然后他将一种颜色应用于整个几何。 同样,可以通过调用setNormalBinding(),setSecondaryColorBinding(),setFogCoordBinding()和setVertexAttribBinding()方法来配置其他属性之间的关系。

5.几何图元集


定义顶点属性数组之后的下一步是描述如何渲染顶点数据。 虚拟osg :: PrimitiveSet类用于控制渲染器从一组顶点生成的几何图元。 osg :: Geometry类提供了几种处理几何图元集的方法:

  • addPrimitiveSet()-将指针传递给osg :: Geometry对象中的一组图元。
  • removePrimitiveSet()-删除一组原语。 作为参数,它使用集合的初始索引和要删除的集合数。
  • getPrimitiveSet()-在作为参数传递的索引处返回一组原始元素。
  • getNumPrimitiveSets()-返回与此几何关联的图元的总数。

osg :: PrimitiveSet类是抽象的,无法实例化,但是封装了与OpenGL一起操作的原始集的几个派生类(例如osg :: DrawArrays和osg :: DrawElementsUInt)从其继承。

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。

第一个和第二个参数指定顶点数组中的第一个索引以及应从中生成几何图形的顶点数。 此外,OSG不会检查指定的顶点数是否足以构建该模式指定的几何,这可能导致应用程序崩溃!

6.示例-绘制一个涂漆的正方形


我们将上述所有内容作为一个简单示例

Quad示例的完整源代码
主文件

 #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)); 

接下来,我们设置法线数组。 在我们的简单情况下,我们不需要为每个顶点创建一个法线-只需描述一个垂直于正方形平面的单位矢量即可

 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()); 

通过指定一组法线,我们通过指定法线BIND_OVAERALL的绑定方法(“绑定”)来通知引擎所有单个顶点将使用一个法线

 quad->setNormalArray(normals.get()); quad->setNormalBinding(osg::Geometry::BIND_OVERALL); 

相反,传递顶点的颜色表示每个顶点将具有自己的颜色

 quad->setColorArray(colors.get()); quad->setColorBinding(osg::Geometry::BIND_PER_VERTEX); 

现在为几何创建一组图元。 我们指示应从顶点数组生成正方形(GL_QUADS)面,将索引为0的顶点作为第一个顶点,并且顶点总数将为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类在直接从数组中无间隙地读取顶点数据时效果很好。 但是,当同一个顶点可以属于一个对象的多个面时,这并不是那么有效。 让我们来看一个例子。



立方体有八个顶点。 但是,从图中可以看出(我们看立方体在平面上的展开),某些顶点属于多个面。 如果您构建一个由12个三角形面组成的立方体,则这些顶点将被重复,而不是由8个顶点组成的数组,而是由36个顶点组成的数组,其中大多数实际上是相同的顶点!

在OSG中,有类osg :: DrawElementsUInt,osg :: DrawElementsUByte和osg :: DrawElementsUShort,它们使用顶点索引数组作为数据来解决上述问题。 索引数组存储描述面和几何图形其他元素的图元顶点的索引。 将这些类应用于多维数据集时,足以存储通过索引数组与面相关联的八个顶点的数组。

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); 

该代码定义了图中所示立方体的正面。

让我们考虑另一个说明性的例子-八面体



有趣的是,它仅包含六个顶点,但是每个顶点最多可以输入四个三角形面! 我们可以使用osg :: DrawArrays创建一个包含24个顶点的数组来显示所有八个面。 但是,否则,我们将执行其他操作-将顶点存储在六个元素的数组中,并使用osg :: DrawElementsUInt类生成面。

八面体示例的完整资料
主文件
 #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(); } 


让我们更详细地分析此代码。 当然,我们要做的第一件事是创建六个顶点的数组

 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); 

我们通过使用指针和operator []运算符的解引用操作访问其坐标的向量来直接初始化每个顶点(我们记得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文件中的该库来构建程序。

八面体

 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支持各种技术来处理场景几何对象的多边形网格。 这些预处理方法(例如多边形缩小和细分)通常用于创建和优化多边形模型。 它们具有一个简单的界面,但是在此过程中,它们执行许多复杂的计算,因此不适合即时执行。

描述的技术包括:

  1. osgUtil ::简化器-减少几何中的三角形数量。 simple()公共方法用于简化模型几何。
  2. osgUtil :: SmootingVisitor-法线的计算。 smooth()方法可用于为模型生成平滑的法线,而不是独立地计算它们并通过法线数组显式设置它们。
  3. osgUtil :: TangentSpaceGenerator-为模型顶点生成切线基础向量。 它通过调用generate()方法启动,并保存getTangentArray(),getNormalArray()和getBinormalArray()方法返回的结果。 在GLSL中编写着色器时,这些结果可用于各种顶点属性。
  4. osgUtil :: Tesselator-执行多边形网格的细分-将复杂的图元拆分为一系列简单的图元(retesselatePolygons()方法)
  5. 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类型的节点中的场景树此部分的所有终端节点为止。

让我们在实践中尝试镶嵌技术。

完整的镶嵌器示例代码
主文件
 #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类型的一个面的生成来创建八个顶点的非凸多边形。 此示例的组装和执行表明,我们预期的结果不起作用-该示例显示不正确



要解决此问题,必须在将构造的几何体传递给查看器之前对其进行细分。

 osgUtil::Tessellator ts; ts.retessellatePolygons(*geom); 

之后我们得到正确的结果



如何运作? 如果未使用正确的细分,则不会显示非凸多边形,因为寻求优化性能的OpenGL会将其视为简单的凸多边形,或者将其忽略,这可能会带来完全出乎意料的结果。

osgUtil :: Tessellator类使用算法将凸多边形转换为一系列非凸多边形-在我们的示例中,它将几何转换为GL_TRIANGLE_STRIP。



此类可以处理孔多边形和自相交多边形。使用公共setWindingType()方法,可以定义各种处理规则,例如GLU_TESS_WINDING_ODD或GLU_TESS_WINDING_NONZERO,它们指定复杂多边形的内部和外部区域。

结论


在本文中,我们对OSG引擎中如何存储和处理三维对象的几何有了基本的了解。不要认为本文中考虑的那些简单但不太令人印象深刻的示例是引擎功能的限制。仅这些示例就可以帮助开发人员了解OpenSceneGraph的机制,如果没有这种了解,就很难想象更复杂的事物的工作。

本文基于对OpenSceneGraph 3.0书中相应章节的文本的翻译和处理入门指南所有示例均由我亲自检查,其源代码可在此处获取待续...

Source: https://habr.com/ru/post/zh-CN430212/


All Articles