OSG中几何体的绘制(一)

2023-12-17

本章主要介绍一些几何体的绘制方法。绘制几何体在场景中是非常常见的,也是最基本的。在很多应用程序中可以看到相当复杂的场景,但不管场景有多复杂,它们都是由少数几个基本的图形元素构建而成的。只要想想达芬奇那些伟大的作品也是由铅笔和画刷所完成的,读者就不会为此感到疑惑了。只要有耐心,读者也可以绘制同样复杂的场景。

1. 场景基本绘图类

在 OSG 中创建几何体的方法比较简单,通常有3种处几体的一是使用松散封装的OpenGL绘图基元;二是使用 OSG 中的基本几何体;三是从文件中导入场景模型。使用松散封装的OpenGL绘图基元绘制几何体具有很强的灵活性,但工作量通常非常大,当面对大型场景时,绘制几何体将是一项非常艰巨而富有挑战的工作,因此,通常还是采用读入外部模型的方法。读取外部模型的方法在后面会有专门的介绍。

  • 向量与数组类

在OSG中定义了大量的类来保存数据,数据通常是以向量的形式来表示的,向量数据主要包括顶点坐标、纹理坐标、颜色和法线等。例如,定义osg::Vec2来保存纹标;定义osg::Vec3 来保存点坐标和法线坐标:定义osg::Vec4 保存颜色的 RGBA 值。osg::Vec2、osg::Vec3 和osg::Vec4 是分别用来保存向量的二维数组、三维数组和四维数组,这些类不仅能够保存各种数据,还提供了向量的基本运算机制,如加、减、乘、除四则运算、点积和单位化等相关的操作。

在OSG中还定义了模板数组用来保存对象,例如可以用顶点索引对象(osg::DrawElementsUInt)来保存顶点索引,用颜色索引(osg::TemplatelndexeArray)来保存颜色。但最常用的还是保存向量数据如osg::Vec3Array 来保存众多顶点坐标、osg::Vec2Array 保存纹理标等,这些模板数组都继承自std::Vector,因此,它们具有向量的基本操作方法,例如,可以利用 push_back()添加一个元素,可以利用pop_back()删除一个元素,同样也可以使用 clear()删除所有的元素。

  • Drawable类

Drawable类是一个纯基类,无法实例化。作为可绘制对象基类的osg::Drawable类,它派生了很多它的继承关系图如图4-1 所示。

图4-1 osg::Drawable 的继承关系图

从图4-1可以看出,由osg::Drawable 派生的类有9个分别是 osg::DrawPixes、osg::Geomctry、osg::ShapeDrawable、osgParticle::ParticleSystem、osgParticle::PrecipitationEffect、osgParticle::PrecipitationDrawable、osgShadow::OccluderGeometry、osgShadow::ShadowVolumeGeometry、osgSim::ImpostorSprite 和 osgText::TextBase,其中,从0SG 核心库派生出了3个类分别是osg::DrawPiexels 类(主要封装了OpenGL中glDrawPixels()的功能)、oog::Geometry类(绘制几何体的类,应用比较灵活)和osg:: ShapeDrawable 类(主要封装了一些已经定义好的几何体,不需要设置坐标即可直接调用,如长方体、正方体、球体等)。其他的类中,有两个派生自粒子系统库,有两个派生自阴影,还有两个分别派生自 osgSim库和osgText 文字库,在后面的章节中都会对这些进行介绍。

  • PrimitiveSet类

osg::PrimitiveSet类继承自osg::Object虚基类,但它不具备一般场景中的特性osg::PrimitiveSet类的继承关系图如图4-2所示

图4-2bosg::PrimitiveSet 的继承关系图

该类主要松散封装了OpenGL的绘图基元,通过指定绘图基元来指定几何体顶点将采用哪一种或几种基元绘制。常用的绘图基元包括如下几种:

  1. POINTS =GL_POINTS // 绘制点
  2. LINES =GL_LINES // 绘制线
  3. LINE_STRIP=GL_LINE_STRIP // 绘制多段线
  4. LINE_LOOP=GL_LINE_LOOP // 绘制封闭线
  5. TRIANGLES=GL_TRIANGLES // 绘制一系列的三角形 ( 不共用顶点 )
  6. TRIANGLE_STRIP=GL_TRIANGLE_STRIP // 绘制一系列三角形 ( 共用后面的两个顶点 )
  7. TRIANGLE_FAN =GL_TRIANGLE_FAN // 绘制一系列三角形,顶点顺序与上一条语绘制的三角形不同
  8. QUADS = GL_QUADS/ 绘制四边形
  9. QUAD_STRIP=GL_QUAD_STRIP // 绘制一系列四边形
  10. POLYGON=GL_POLYGON // 绘制多边形

从osg::PrimitiveSet类的继承关系图可以看出,它的派生类主要有如下3个

  • osg::DrawArrays类。继承自osg::PrimitiveSet,它封装了glDrawArrays()顶点数组绘图命令用于指定顶点和绘图基元。
  • osg::DrawElements 类。它又派生出3个子类,分别是osg::DrawElementsUByte、osg::DrawElementsUShort和osg::DrawElementsUInt,封装了glDrawElements()的指令,可以起索引的作用,在后面的示例中会用到。
  • osg::DrawArrayLengths类。它的主要作用是多次绘制,即多次调用glDrawArrays(),且每次均使用不同的长度和索引范围,在绘制过程中用得不是很多。

DrawArrays的基本用法如下

  1. osg::DrawArrays::DrawArrays( GLenum mode, GLint first, GLsizei count );
  2. /* 参数说明 : 第一个参数是指定的绘图基元,即前面所列举的常见绘图元 : 第二个参数是指绘制几何体的第一个顶点数在指定顶点的位置数,第三个参数是使用的顶点的总数 */

还有一点值得注意的是,虽然osg::PrimitiveSet类提供与OpenGL一样的顶点机制,但是在内部渲染上还是有一定区别的。根据渲染环境的不同,渲染的方式也是不一样的,可能会采用顶点、顶点数组、显示列表或者 glBegin/glEnd()来渲染几何体,继承自 Drawable 类的对象(如Geometry)在默认条件下将使用显示列表。其中osg::Drawable:;setUseDisplayList(false)用于手动禁止使用显示列表。

还有一种比较特殊的情况如果设置BIND_PER_PRIMITIVE绑定方式那0SG将采用glBegin()/glEnd()函数进行渲染因为在设置使用绑定方式为 BIND_PER_PRIMITIVE 后它就为每个独立的几何图元设置一种绑定属性。

2.基本几何体的绘制

在前面我们介绍了OSG绘制的一些基础知识,这对以后理解程序有很大的帮助。本节的例子主要是基本图形的绘制,如线段、三角形、圆及四边形等。

我们知道任何复杂的东西都是由一些简单的部分组合构成的,对于 OSG 创建的场景和对象也同样如此,它们是由简单的图元(我们把构成3D 对象的构件称为图元)按照一定的方式排列和组合而成的,OSG中的所有图元都是一维或二维对象,包括单个的点、直线和复杂的多边形。

2.1 几何体类

在前面我们已经简单介绍了几何体(osg::Geometry)类它承自osg::Drawable类。它的继承关系图如图4-3所示。

图4-3 osg::Geomety 的继承关系图

如果读者是一个熟练的OpenGL程序员的话相信osg::Geomety类的定义和作用已经在读者心中有一个完美的定义。它的主要作用是对指定绘制几何体的顶点数及对数据的解析,主要提供了如下3大类方法:

(1) 指定向量数据。就是以前所涉及的点数据、纹理及颜等一系列向量数据,可以通过下面的几个函数来实现:

  1. void setVertexArray(Array *array) // 设置顶点数组
  2. void setVertexData( const AnrayData&arrayData) // 设置顶点数组数据
  3. void setVertexIndices(IndexArrayarray) // 设置顶点索引数组
  4. void setNormalArray(Array*array) // 设置法线数组
  5. void setNormalData( const ArrayData &arrayData) // 设置法线数组数据
  6. void setNormalIndices(IndexArray*array) // 设置法线索引数组
  7. void setColorArray(Array*array) // 设置颜色数组
  8. void setColorData( const ArrayData &arrayData) // 设置颜色数组数据
  9. void setColorIndices(IndexArray &array) // 设置颜色索引数组
  10. void setTexCoordArray(unsigned int unitArray*) // 设置纹理坐标数组,第一个参数是纹理单元,第二个是纹理坐标数组
  11. void setTexCoordData(unsigned int index, const ArrayData &arrayData) // 设置纹理坐标数组数据,第一个参数是纹理单元,第二个是纹理坐标数组数据
  12. void setTexCoordIndices(unsigned int unit, IndexArray *) // 设置纹理坐标索引数组,第一个参数是纹理单元,第二个是纹理索引坐标数组

(2)设置绑定方式。数据绑定主要有两项,即法线及颜色,可以通过下面的两个函数来实现:

  1. void setNormalBinding(AttributeBinding ab) // 设置法线绑定方式
  2. void setColorBinding(AttributeBinding ab) // 设置颜色绑定方式

绑定方式主要有下面几种

  1. BIND_OFF // 不启用绑定
  2. BIND_OVERALL // 绑定全部的顶点
  3. BIND_PER_PRIMITIVE_SET // 单个绘图基元绑定
  4. BIND_PER_PRIMITIVE // 单个独立的绘图基元绑定
  5. BIND_PER_VERTEX // 单个顶点绑定

(3)数据解析。当在指定了各种向量数据和绑定方式之后,采用何种方式来渲染几何体就是最为关键的。不同的方式下,渲染出来的图形是不一样的,即使效果一样,可能面数或内部机制等也是有区别的。数据解析主要通过如下函数来指定:

  1. bool addPrimitiveSet(PrmitiveSet *primitiveset)
  2. /* 参数说明 osg::PrimitiveSet 是无法初始化的虚基类,因此这里主要是调用它的子类来指定数据渲染,最常用的就是前面介绍的 osg:DrawArrays ,用法比较简单,初始化一个对象实例,参数说明见前面 osg;:DrawArrays */

通过前面的讲述可知,绘制并渲染几何体主要有如下3大步骤:

(1)创建各种向量数据,如顶点、纹理坐标、颜色和法线等。需要注意的是,添加顶点数据时主要按照 逆时针顺序添加 ,以确保背面剔除 (backface culling)的正确(后面还会有介绍)。

(2)实例化一个几何体对 (osg::Gemetry),设置点坐标数组、纹理坐标数组、颜色数组、法线数组、绑定方式及数据解析。

(3)加入叶节点绘制并渲染。

通过这么多的介绍,相信读者已经完全明白了。下面的小节中会提供例子来说明如何绘制基本的几何体对象,要仔细理解。

2.2 基本几何体绘制示例

基本几何体绘制(osg::Geometry)示例演示了创建一个几何体的过程示例中创建了最简单的四边形。通过该示例读者将学会如何创建简单的几何体。代码如程序清单4-1所示。

  1. osg::ref_ptr<osg::Node> createQuad() // 创建一个四边形节点
  2. {
  3. // 创建一个叶节点对象
  4. osg::ref_ptr<osg::Geode> geode = new osg::Geode();
  5. // 创建一个几何体对象
  6. osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
  7. // 创建顶点数组,注意顶点的添加顺序是逆时针的
  8. osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
  9. v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); // 添加数据
  10. v->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
  11. v->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
  12. v->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
  13. // 设置顶点数据
  14. geom->setVertexArray(v.get());
  15. // 创建纹理坐标
  16. osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();
  17. vt->push_back(osg::Vec2(0.0f, 0.0f)); // 添加数据
  18. vt->push_back(osg::Vec2(1.0f, 0.0f));
  19. vt->push_back(osg::Vec2(1.0f, 1.0f));
  20. vt->push_back(osg::Vec2(0.0f, 1.0f));
  21. // 设置纹理坐标
  22. geom->setTexCoordArray(0, vt.get());
  23. // 创建颜色数组
  24. osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();
  25. vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); // 添加数据
  26. vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
  27. vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
  28. vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
  29. geom->setColorArray(vc.get()); // 设置颜色数组
  30. geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX); // 设置颜色的绑定方式为单个顶点
  31. // 创建法线数组
  32. osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
  33. nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); // 添加法线
  34. // 设置法线数组
  35. geom->setNormalArray(nc.get());
  36. geom->setNormalBinding(osg::Geometry::BIND_OVERALL); // 设置法线的绑定方式为全部顶点
  37. geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4)); // 添加图元,绘图基元为四边形
  38. geode->addDrawable(geom.get()); // 添加到叶节点
  39. return geode.get();
  40. }
  41. // 基本几何体绘制
  42. void baseGeometry_4_1()
  43. {
  44. // 创建 Viewer 对象,场景浏览器
  45. osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
  46. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  47. traits->x = 40;
  48. traits->y = 40;
  49. traits->width = 600;
  50. traits->height = 480;
  51. traits->windowDecoration = true ;
  52. traits->doubleBuffer = true ;
  53. traits->sharedContext = 0;
  54. osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
  55. osg::ref_ptr<osg::Camera> camera = new osg::Camera;
  56. camera->setGraphicsContext(gc.get());
  57. camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height));
  58. GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
  59. camera->setDrawBuffer(buffer);
  60. camera->setReadBuffer(buffer);
  61. viewer->addSlave(camera.get());
  62. osg::ref_ptr<osg::Group> root = new osg::Group();
  63. // 添加到场景
  64. root->addChild(createQuad());
  65. // 优化场景数据
  66. osgUtil::Optimizer optimizer;
  67. optimizer.optimize(root.get());
  68. viewer->setSceneData(root.get());
  69. viewer->realize();
  70. viewer->run();
  71. }

运行程序,截图如图4-4 所示

图4-4基本几何体绘制示例截图

2.3 索引绑定几何体绘制示例

通过前面的示例,相信读者已经学会了如何创建简单的几何体。索引绑定几何体绘制(osg::Geometry)示例将向读者演示如何利用索引绑定几何体。代码如程序清单4-2所示。

  1. osg::ref_ptr<osg::Node> createQuad_Index() // 创建一个四边形节点
  2. {
  3. // 创建一个叶节点对象
  4. osg::ref_ptr<osg::Geode> geode = new osg::Geode();
  5. // 创建一个几何体对象
  6. osg::ref_ptr<deprecated_osg::Geometry> geom = new deprecated_osg::Geometry();
  7. // 创建顶点数组
  8. osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
  9. v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f)); // 添加数据
  10. v->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));
  11. v->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));
  12. v->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));
  13. v->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
  14. // 设置顶点数据
  15. geom->setVertexArray(v.get());
  16. // 创建四边形顶点索引数组,指定绘图基元为四边形,注意添加顺序
  17. osg::ref_ptr<osg::DrawElementsUInt> quad = new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0);
  18. quad->push_back(0); // 添加数据
  19. quad->push_back(1);
  20. quad->push_back(2);
  21. quad->push_back(3);
  22. // 添加到几何体
  23. geom->addPrimitiveSet(quad.get());
  24. // 创建三角形顶点索引数组,指定绘图基元为三角形,注意添加顺序
  25. osg::ref_ptr<osg::DrawElementsUInt> triangle = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0);
  26. triangle->push_back(4); // 添加数据
  27. triangle->push_back(0);
  28. triangle->push_back(3);
  29. // 添加到几何体
  30. geom->addPrimitiveSet(triangle.get());
  31. // 创建颜色数组
  32. osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();
  33. vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f)); // 添加数据
  34. vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
  35. vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
  36. vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
  37. // 创建颜色索引数组
  38. osg::TemplateIndexArray<unsigned int , osg::Array::UIntArrayType, 4, 4>* colorIndex = new osg::TemplateIndexArray<unsigned int , osg::Array::UIntArrayType, 4, 4>();
  39. colorIndex->push_back(0); // 添加数据,注意添加数据顺序与顶点一一对应
  40. colorIndex->push_back(1);
  41. colorIndex->push_back(2);
  42. colorIndex->push_back(3);
  43. colorIndex->push_back(2);
  44. geom->setColorArray(vc.get()); // 设置颜色数组
  45. geom->setColorIndices(colorIndex); // 设置颜色索引数组
  46. geom->setColorBinding(deprecated_osg::Geometry::BIND_PER_VERTEX); // 设置颜色的绑定方式为单个顶点
  47. // 创建法线数组
  48. osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();
  49. nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); // 添加法线
  50. geom->setNormalArray(nc.get()); // 设置法线数组
  51. geom->setNormalBinding(deprecated_osg::Geometry::BIND_OVERALL); // 设置法线的绑定方式为全部顶点
  52. geode->addDrawable(geom.get()); // 添加到叶节点
  53. return geode.get();
  54. }
  55. void indexGeometry_4_2()
  56. {
  57. // 创建 Viewer 对象,场景浏览器
  58. osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
  59. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  60. traits->x = 40;
  61. traits->y = 40;
  62. traits->width = 600;
  63. traits->height = 480;
  64. traits->windowDecoration = true ;
  65. traits->doubleBuffer = true ;
  66. traits->sharedContext = 0;
  67. osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
  68. osg::ref_ptr<osg::Camera> camera = new osg::Camera;
  69. camera->setGraphicsContext(gc.get());
  70. camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height));
  71. GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
  72. camera->setDrawBuffer(buffer);
  73. camera->setReadBuffer(buffer);
  74. viewer->addSlave(camera.get());
  75. osg::ref_ptr<osg::Group> root = new osg::Group();
  76. // 添加到场景
  77. root->addChild(createQuad_Index());
  78. // 优化场景数据
  79. osgUtil::Optimizer optimizer;
  80. optimizer.optimize(root.get());
  81. viewer->setSceneData(root.get());
  82. viewer->realize();
  83. viewer->run();
  84. }

运行程序,截图如图 4-5 所示

图4-5索引绑定几何体绘制示例截图

3. 使用OSG中预定义的几何体

在OSG 中,为了简化场景的绘制,同时也为了方便开发者能够快速地构造一个场景,它本身预定义了一些常用的几何体。下面分别进行介绍。

3.1 osg::Shape类

osg::Shape 类直接继承自osg::Object 基类,承关系图如图4-6 所示。

图4-6 osg::Shape 的继承关系图

osg::Shape 类是各种内嵌几何体的基类,它不但可用于剔除和碰撞检测,还可用于生成预定义的几何体对象。

常用的内嵌几何体包括如下几种:

  1. osg::Box // 正方体
  2. osg::Capsule // 太空舱
  3. osg::Cone // 椎体
  4. osg::Cylinder // 柱体
  5. osg::HeightField // 高度图
  6. osg::InfinitePlane // 无限平面
  7. osg::Sphere // 球体
  8. osg::TriangleMesh // 三角片

3.2  osg::ShapeDrawable类

在第 4.3.1 节中,我们讲到了在 OSG 中内了很多预定义的几何体。如渲染这些内嵌的几何体就必须将其与osg::Drawable 关联。实际应用中可以使用osg::Drawable类的派生类osg::ShapeDrawable来完成这个功能。

osg::ShapeDrawable类在前面已经讲到,它派生自osg::Drawable类,关系继承图如图4-7所示。

图4-7 osg::ShapeDrawable的继承关系图

在osg::ShapeDrawable类的构造函数中提供了关联osg::Shape 的方法:

  1. ShapeDrawable(Shape*shape,TessellationHints *hints-0) // 第一个参数为 sape ,第二个参数默认下不细化

同时,由于它继承自osg::Drawable类,所以它的实例需要被添加到叶节点中才能被实例绘制。

3.3 网格化类

网格化类(osg::TessellationHints)直接继承自osg::Objcct 基类,继承关系图如图4-8 所示。

图4-8 osg::TessellationHints 的继承关系图

osg::TessellationHints类的主要作用是设置预定义几何体对象的精细程度,精细程度越高,表示其细分越详细,但对于不同的预定义几何体对象它的作用是不一样的,例如:

  1. Box( 四棱柱 ): 网格化类对于四棱柱没有意义。
  2. Capsule( 太空舱 ): 太空舱分 3 个部分,上下半球部分和圆侧面部分,默认侧面被细分
  3. Cone( 圆锥 ): 直接细分
  4. Cylinder( 柱体 ): 直接细分。
  5. Sphere( ): 直接细分。

目前,osg::TessellationHints类并不完整,部分类成员函数还没有实现,具体可以参看源码。在内嵌几何体对象中,默认的情况下,网格化类的精细度为0,表示预定义的几何体此时按照原顶点默认绘制,不做任何细化处理。

3.4 预定义几何体示例

预定义几何体(osg::ShapeDrawable)示例的代码如程序清单4-3所示。

  1. // 4_3 预定义几何体
  2. osg::ref_ptr<osg::Geode> createShape() // 绘制多个预定义的几何体
  3. {
  4. // 创建一个叶节点
  5. osg::ref_ptr<osg::Geode> geode = new osg::Geode();
  6. // 设置半径和高度
  7. float radius = 0.8f;
  8. float height = 1.0f;
  9. // 创建精细度对象,精细度越高,细分就越多
  10. osg::ref_ptr<osg::TessellationHints> hints = new osg::TessellationHints;
  11. hints->setDetailRatio(0.5f); // 设置精细度为 0.5f
  12. geode->addDrawable( new osg::ShapeDrawable( new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints.get())); // 添加一个球体,第一个参数是预定义几何体对象,第二个是精细度,默认为 0
  13. geode->addDrawable( new osg::ShapeDrawable( new osg::Box(osg::Vec3(2.0f, 0.0f, 0.0f), 2 * radius), hints.get())); // 添加一个正方体
  14. geode->addDrawable( new osg::ShapeDrawable( new osg::Cone(osg::Vec3(4.0f, 0.0f, 0.0f), radius, height), hints.get())); // 添加一个圆锥
  15. geode->addDrawable( new osg::ShapeDrawable( new osg::Cylinder(osg::Vec3(6.0f, 0.0f, 0.0f), radius, height), hints.get())); // 添加一个圆柱体
  16. geode->addDrawable( new osg::ShapeDrawable( new osg::Capsule(osg::Vec3(8.0f, 0.0f, 0.0f), radius, height), hints.get())); // 添加一个太空舱
  17. return geode.get();
  18. }
  19. void shapeDrawable_4_3()
  20. {
  21. // 创建 Viewer 对象,场景浏览器
  22. osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
  23. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  24. traits->x = 40;
  25. traits->y = 40;
  26. traits->width = 600;
  27. traits->height = 480;
  28. traits->windowDecoration = true ;
  29. traits->doubleBuffer = true ;
  30. traits->sharedContext = 0;
  31. osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
  32. osg::ref_ptr<osg::Camera> camera = new osg::Camera;
  33. camera->setGraphicsContext(gc.get());
  34. camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height));
  35. GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
  36. camera->setDrawBuffer(buffer);
  37. camera->setReadBuffer(buffer);
  38. viewer->addSlave(camera.get());
  39. osg::ref_ptr<osg::Group> root = new osg::Group();
  40. root->addChild(createShape()); // 添加到场景
  41. // 优化场景数据
  42. osgUtil::Optimizer optimizer;
  43. optimizer.optimize(root.get());
  44. viewer->setSceneData(root.get());
  45. viewer->realize();
  46. viewer->run();
  47. }

运行程序,截图如图4-9 所示。

图4-9 预定义几何体示例截图

4. 多边形分格化

如果读者对OpenGL有一定了解的话,应该知道OpenGL为了快速渲染多边形,只能直接显示简单的 凸多边形 。所谓简单的凸多边形,就是多边形上任意两点的连线上的点依属于该多边形。对凹多边形或者自交叉多边形的渲染结果将不确定。下面列举一些需要分格化的多边形,如图 4-10 所示。

为了正确显示凹多边形或者自交叉多边形,就必须把它们分解为简单的凸多边形,这种做法就称为多边形的分格化。 OSG是对底层OpenGL API的封装,所以它同样只能直接显示简单的凸多边形,对于凹多边形或者自交叉多边形,渲染也是不确定的。

在OSG中提供了一个多边形分格化的类osgUtil::Tessellator,它继承自osg::Referenced类继承关系图如图4-11所示

在OSG中进行多边形分格化渲染需要如下3个步骤:

(1)创建多边形分格化对象。

(2)设置分格化对象的类型,通常有下面3种类型。

  1. TESS_TYPE_GEOMETRY, // 分格化几何体
  2. TESS_TYPE_DRAWABLE, // 分格化几何体中的 Drawable( 如多边形三形四边形等 )
  3. TESS_TYPE_POLYGONS, // 只分格化几何体中的多边形

(3)根据计算的环绕数指定相应的环绕规则。

1.环绕数

在《OpenGL编程指南》第5版中曾指出“对于一条简单的轮廓线,每个点的环绕数就是环绕这个点的所有轮廓线的代数和(用一个有符号的整数表示,求和规则是: 逆时针环绕的轮廓线为正,顺时针环绕的轮廓线为负 )。这个过程把一个有符号的整数数值与平面上的每个顶点相关联。注意,对于区域中的所有点,它们的环绕数都是相同的。

图4-12为轮廓线与环绕数的计算方法,读者可以通过此图理解环绕数及如何计算环绕数。

图4-12 轮廓线与环绕数的计算方法

2环绕规则

如果一个区域的环绕数属于环绕规则所选择的类型,那么它就是它的内部区域。通常,环绕规则把具有奇数和非零环绕数的区域定义为内部区域。环绕规则主要是针对环绕数来确定的。

几种常用的环绕规则如下:

  1. TESS_WINDING_ODD=GLU_TESS_WINDING_ODD // 环绕数为奇数
  2. TESS_WINDING_NONZERO=GLU_TESS_WINDING_NONZERO // 环绕数为非零数
  3. TESS_WINDING_POSITIVE=GLU_TESS_WINDING_POSITIVE // 环绕数为正数
  4. TESS_WINDING_NEGATIVE=GLU_TESS_WINDING_NEGATIVE // 环绕数为负数
  5. TESS_WINDING_ABS_GEQ_TWO=GLU_TESS_WINDING_ABS_GEQ_TWO // 环绕数为绝对值大于或等于 2

多边形分格化(osgUtil::Tessellator)示例的代码如程序清单4-4所示

  1. osg::ref_ptr<osg::Geode> tesslatorGeometry() // 使用分格化绘制凹多边形
  2. {
  3. osg::ref_ptr<osg::Geode> geode = new osg::Geode();
  4. osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();
  5. geode->addDrawable(geom.get());
  6. // 以下是一些顶点数据
  7. const float wall[5][3] = //
  8. { { 2200.0f, 0.0f, 1130.0f },
  9. { 2600.0f, 0.0f, 1130.0f },
  10. { 2600.0f, 0.0f, 1340.0f },
  11. { 2400.0f, 0.0f, 1440.0f },
  12. { 2200.0f, 0.0f, 1340.0f } };
  13. const float door[4][3] = //
  14. { { 2360.0f, 0.0f, 1130.0f },
  15. { 2440.0f, 0.0f, 1130.0f },
  16. { 2440.0f, 0.0f, 1230.0f },
  17. { 2360.0f, 0.0f, 1230.0f } };
  18. const float windows[16][3] = // 四扇窗户
  19. { { 2240.0f, 0.0f, 1180.0f },
  20. { 2330.0f, 0.0f, 1180.0f },
  21. { 2330.0f, 0.0f, 1220.0f },
  22. { 2240.0f, 0.0f, 1220.0f },
  23. { 2460.0f, 0.0f, 1180.0f },
  24. { 2560.0f, 0.0f, 1180.0f },
  25. { 2560.0f, 0.0f, 1220.0f },
  26. { 2460.0f, 0.0f, 1220.0f },
  27. { 2240.0f, 0.0f, 1280.0f },
  28. { 2330.0f, 0.0f, 1280.0f },
  29. { 2330.0f, 0.0f, 1320.0f },
  30. { 2240.0f, 0.0f, 1320.0f },
  31. { 2460.0f, 0.0f, 1280.0f },
  32. { 2560.0f, 0.0f, 1280.0f },
  33. { 2560.0f, 0.0f, 1320.0f },
  34. { 2460.0f, 0.0f, 1320.0f } };
  35. // 设置顶点数据
  36. osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array();
  37. geom->setVertexArray(coords.get());
  38. // 设置法线
  39. osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array();
  40. normal->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));
  41. geom->setNormalArray(normal.get());
  42. geom->setNormalBinding(osg::Geometry::BIND_OVERALL);
  43. // 添加墙
  44. for ( int i = 0; i < 5; i++)
  45. {
  46. coords->push_back(osg::Vec3(wall[i][0], wall[i][1], wall[i][2]));
  47. }
  48. geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 5));
  49. // 添加门
  50. for ( int i = 0; i < 4; i++)
  51. {
  52. coords->push_back(osg::Vec3(door[i][0], door[i][1], door[i][2]));
  53. }
  54. // 添加窗
  55. for ( int i = 0; i < 16; i++)
  56. {
  57. coords->push_back(osg::Vec3(windows[i][0], windows[i][1], windows[i][2]));
  58. }
  59. geom->addPrimitiveSet( new osg::DrawArrays(osg::PrimitiveSet::QUADS, 5, 20));
  60. // 创建分格化对象
  61. osg::ref_ptr<osgUtil::Tessellator> tscx = new osgUtil::Tessellator();
  62. tscx->setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY); // 设置分格类型为几何体
  63. tscx->setBoundaryOnly( false ); // 设置只显示轮廓线为 false ,这里还需要填充
  64. tscx->setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD); // 设置环绕规则
  65. tscx->retessellatePolygons(*(geom.get())); // 使用分格化
  66. return geode.get();
  67. }
  68. void Tessellator_4_4()
  69. {
  70. osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
  71. osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
  72. traits->x = 40;
  73. traits->y = 40;
  74. traits->width = 600;
  75. traits->height = 480;
  76. traits->windowDecoration = true ;
  77. traits->doubleBuffer = true ;
  78. traits->sharedContext = 0;
  79. osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
  80. osg::ref_ptr<osg::Camera> camera = new osg::Camera;
  81. camera->setGraphicsContext(gc.get());
  82. camera->setViewport( new osg::Viewport(0, 0, traits->width, traits->height));
  83. GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;
  84. camera->setDrawBuffer(buffer);
  85. camera->setReadBuffer(buffer);
  86. viewer->addSlave(camera.get());
  87. osg::ref_ptr<osg::Group> root = new osg::Group();
  88. // 添加绘制的多边形
  89. osg::ref_ptr<osg::Geode> geode = tesslatorGeometry();
  90. root->addChild(geode.get());
  91. // 优化场景数据
  92. osgUtil::Optimizer optimizer;
  93. optimizer.optimize(root.get());
  94. viewer->setSceneData(root.get());
  95. viewer->realize();
  96. viewer->run();
  97. }

运行程序,截图如图4-13所示

图4-13多边形分格化示例截图

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OSG中几何体的绘制(一) 的相关文章

  • 在 JavaScript 中引用 C# 变量

    我已经阅读了很多线程 但我不明白为什么这不起作用 我正在创建一个将用作导航栏的 SharePoint Web 部件 一切都很顺利 直到我尝试在 JS 代码中引用 C 变量 这是来自 VisualWebPart1UserControl asc
  • Xamarin 测试记录器选项有错误。无法记录自动化测试

    选项 gt Xamarin gt Xamarin Test Recorder 中的所有设置都有错误 我的桌面上安装了 Visual Studio 2015 企业版 以及 Xamarin 和 Xamarin Test Recorder 插件
  • 基于多线程的 RabbitMQ 消费者

    我们有一个 Windows 服务 它监听单个 RabbitMQ 队列并处理消息 我们希望扩展相同的 Windows 服务 以便它可以监听 RabbitMQ 的多个队列并处理消息 不确定使用多线程是否可以实现这一点 因为每个线程都必须侦听 阻
  • 为基于架构的 XML 文件创建 WPF 编辑器

    这是场景 我们的服务器产品之一使用大型 XML 配置文件 该文件的布局相当好 并且针对 XSD 文件进行了验证 现在是时候构建一个配置 GUI 来维护这个文件了 我想深入研究 WPF 来完成它 我可以为每个配置部分布置一个单独的表单 每次向
  • 如何将 mat 转换为 array2d

    我为dlib http dlib net face landmark detection ex cpp html那里的面部地标代码使用 array2d 来获取图像 但我喜欢使用 Mat 读取图像并转换为 array2d 因为 dlib 仅支
  • 仅针对某些异常类型中断

    我知道异常处理是一件非常重要的事情 我们在所有项目中都在这样做 主要原因是记录客户发生的错误 这工作正常 根本不是问题 但是 当我仍在使用 Visual Studio 编码和运行应用程序时 我根本不需要任何异常处理 我希望调试器正好停在应用
  • 如何在 C# 中创建 PKCS12 .p12 文件?

    这可能是一个n00b问题 但我在这方面确实没有任何经验 我需要创建一个包含 X509 证书和私钥的 p12 捆绑包 我当前有两个对象 X509Certificate2 和包含关键信息的 RSAParameters 对象 如何将它们合并到 p
  • 如何在控制器中使用多个 DBContext

    如何在控制器中使用多个 DBContext 我尝试以不同的方式重载构造函数 一些控制器 public C1 DBContext1 a DBContext2 b DBContext3 c public C1 DBContext1 a publ
  • 无法在 Visual Studio 和 vcpkg 中构建 cmake 项目(致命错误 C1083)

    我今天安装了vcpkg 启用了与Visual Studio的集成 即 vcpkg集成安装 并开始安装库 我基本上安装了 cpprestsdk 并触发了 boost 库的安装 然后我在 Visual Studio CMake 中打开该项目 当
  • WPF ComboBox 中具有本地化名称的枚举

    我有一个列出枚举的组合框 enum StatusEnum Open 1 Closed 2 InProgress 3
  • 使用 (float&)int 进行类型双关可以正常工作,(float const&)int 会像 (float)int 一样转换吗?

    VS2019 发布 x86 template
  • 是否可以在对Where 的调用中调用命名方法?

    我试图从 RedGate 的这本免费电子书中了解 Linq 的一些性能影响ftp support red gate com ebooks under the hood of net memory management part1 pdf f
  • VS C# 中的依赖地狱,找不到依赖项

    我创建了一个图表 C 库 我们称之为chartlibrary 它本身依赖于多个第三方 dll 文件 在另一个可执行项目中 我们称之为chartuser 我参考了chartlibrary项目 两个项目位于 Visual Studio 中的同一
  • 来自用户定义文字的整数字符序列,以字符串作为参数

    目前 只有双精度数可以在用户定义的文字中生成字符模板 template
  • 如何使用 MongoDB 实现 ASP.NET Core 3.1 Identity?

    是一个 API 用于简化后端和逻辑代码来管理用户 密码 个人资料数据 角色 声明 令牌 电子邮件确认等 对于 Visual Studio 来说 支撑脚手架 https learn microsoft com en us aspnet cor
  • 为什么在 C++ 类中的数据成员上使用像 m_ 这样的前缀?

    许多 C 代码使用语法约定来标记数据成员 常见的例子包括 m memberName对于公共成员 在所有使用公共成员的情况下 memberName对于私人会员或所有会员 其他人尝试强制使用this gt member每当使用数据成员时 根据我
  • 提升shared_from_this<>()

    有人可以用几句话概括一下如何提升shared from this lt gt 应该使用智能指针 特别是从使用绑定函数在 io service 中注册处理程序的角度来看 编辑 一些回复要求提供更多背景信息 基本上 我正在寻找 陷阱 即人们使用
  • 为什么 getch 不可移植?

    是什么使得 getch 本质上无法作为标准 C 函数包含在内 对于控制台界面来说 它是如此直观和优雅 如果没有它 要求输入单个字符总是会产生误导 因为用户可以输入多个键 更糟糕的是 您经常需要确保在读取控制台输入后清除标准输入 这甚至不是作
  • SQL Server CE 不兼容的数据库版本

    我有一个 SQL Server CE 4 0 数据库 sdf文件 当我尝试从我的应用程序 WPF 对数据库进行查询时 出现以下错误 数据库版本不兼容 如果这是兼容文件 请运行修复 其他情况请参考文档 数据库版本 4000000 请求的版本
  • 在地图上使用 find

    如何使用 find 和 aconst iterator如果你有一个地图定义为 typedef std pair

随机推荐

  • 中文星期几&十二时辰

    输入年月日输出中文星期败 输入时间字符串 输出十二时辰 笔记模板由python脚本于2023年12月16日 23 39 04创建 本篇笔记适合 熟悉python字符串类型str 并可以熟练应用 的coder翻阅 学习的细节是欢悦的历程 Py
  • 时序预测 | Python实现GRU电力需求预测

    时序预测 Python实现GRU电力需求预测 目录 时序预测 Python实现GRU电力需求预测 预测效果 基本描述 程序设计 参考资料
  • 软件工程期末复习+数据仓库ETL

    一 软件工程 请用基本路径测试方法为下列程序设计测试用例 并写明中间过程 第1步 画出流程图 1 菱形用于条件判断 用在有分支的地方 2 矩形表示一个基本操作 3 圆形是连接点 第2步 计算程序环路复杂性 流图G的环路复杂度V G 定义为
  • 十七、如何将MapReduce程序提交到YARN运行

    1 启动某个节点的某一个用户 hadoop node1 jps 13025 Jps hadoop node1 yarn daemon start resourcemanager hadoop node1 jps 13170 Resource
  • ES6 面试题 | 14.精选 ES6 面试题

    前端开发工程师 主业 技术博主 副业 已过CET6 阿珊和她的猫 CSDN个人主页 牛客高级专题作者 在牛客打造高质量专栏 前端面试必备 蓝桥云课签约作者 已在蓝桥云课上架的前后端实战课程 Vue js 和 Egg js 开发企业级健康管理
  • 你好,C++(3)2.1 一个C++程序的自白

    第2部分 与C 第一次亲密接触 在浏览了C 三分天下 的世界版图之后 便对C 有了基本的了解 算是一只脚跨入了C 世界的大门 那么 怎样将我们的另外一只脚也跨入C 世界的大门呢 是该即刻开始编写C 程序 还是 正在我们犹豫的时候 便看到前面
  • 剑指 Offer(第2版)面试题 34:二叉树中和为某一值的路径

    剑指 Offer 第2版 面试题 34 二叉树中和为某一值的路径 剑指 Offer 第2版 面试题 34 二叉树中和为某一值的路径 解法1 深度优先搜索 剑指 Offer 第2版 面试题 34 二叉树中和为某一值的路径 题目来源 47 二叉
  • ES6 面试题 | 13.精选 ES6 面试题

    前端开发工程师 主业 技术博主 副业 已过CET6 阿珊和她的猫 CSDN个人主页 牛客高级专题作者 在牛客打造高质量专栏 前端面试必备 蓝桥云课签约作者 已在蓝桥云课上架的前后端实战课程 Vue js 和 Egg js 开发企业级健康管理
  • 时序预测 | Python实现GRU-XGBoost组合模型电力需求预测

    时序预测 Python实现GRU XGBoost组合模型电力需求预测 目录 时序预测 Python实现GRU XGBoost组合模型电力需求预测 预测效果 基本描述 程序设计 参考资料
  • DeepCache

    这里介绍一种名为DeepCache的无需训练的方法 它利用了去噪过程中模型深层特征的相似性 通过缓存 Cache 来避免重新计算网络中的深层特征 仅计算网络的浅层 从而减少计算量 实验展示了DeepCache相较于需要重新训练的现有剪枝和蒸
  • 时序预测 | Python实现LSTM-Attention电力需求预测

    时序预测 Python实现LSTM Attention电力需求预测 目录 时序预测 Python实现LSTM Attention电力需求预测 预测效果 基本描述 程序设计 参考资料
  • 时序预测 | Python实现LSTM-Attention-XGBoost组合模型电力需求预测

    时序预测 Python实现LSTM Attention XGBoost组合模型电力需求预测 目录 时序预测 Python实现LSTM Attention XGBoost组合模型电力需求预测 预测效果 基本描述 程序设计 参考资料
  • 目标检测YOLO系列从入门到精通技术详解100篇-【目标检测】机器视觉(基础篇)(十三)

    目录 前言 几个高频面试题目 人工智能 机器学习 计算机视觉之间的关系 知识储备 计算机视觉 计算机图像学 图像处理基础知识 光学棱镜
  • 工业缺陷检测~

    这里介绍算法在图像处理中的应用 同时还介绍了常用的图像处理算法和现有可用的视觉检测软件库 文章旨在帮助读者更好地了解算法在图像处理中的应用 提高图像处理的效果和效率 算法 预处理算法 检测算法 常用的图像处理算法 1 图像变换 空域与频域
  • WPF-UI HandyControl 控件简单实战

    文章目录 前言 UserControl简单使用 新建项目 直接新建项目 初始化UserControl Geometry 矢量图形 额外Icon导入
  • 关于“Python”的核心知识点整理大全24

    10 1 6 包含一百万位的大型文件 前面我们分析的都是一个只有三行的文本文件 但这些代码示例也可处理大得多的文件 如果我们有一个文本文件 其中包含精确到小数点后1 000 000位而不是30位的圆周率值 也可 创建一个包含所有这些数字的字
  • 知识图谱之关键实体数据爬取

    目录 爬取实体概览 爬取技术介绍 requests html Selenium 两者比较 学习路径 代码结构 高可用爬取策略 基于文件记录位点 请求失败指数退避重试 爬取代码 品牌数据 车系数据 车型数据 车型配置数据 代码地址 爬取实体概
  • 在Windows上通过cmake-gui及VS2019来 编译OpenCV-4.5.3源码

    文章目录 下载OpenCV 4 5 3源码 下载opencv contrib 4 5 3源码 打开cmake gui 选择生成器 通过 Visual Studio 2019 打开构建好的 sln工程文件 执行编译操作 执行安装操作
  • 屏幕超时休眠-Android13

    屏幕超时休眠 Android13 1 设置界面 1 2 属性值 1 2 1 默认值 1 2 2 最小值限制 1 3 属性值疑问 Settings System SCREEN OFF TIMEOUT 2 超时灭屏
  • OSG中几何体的绘制(一)

    本章主要介绍一些几何体的绘制方法 绘制几何体在场景中是非常常见的 也是最基本的 在很多应用程序中可以看到相当复杂的场景 但不管场景有多复杂 它们都是由少数几个基本的图形元素构建而成的 只要想想达芬奇那些伟大的作品也是由铅笔和画刷所完成的 读