OSG中几何体的绘制(一)

发布时间:2023年12月18日

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

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

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

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

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

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

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

文章来源:https://blog.csdn.net/liangfei868/article/details/135039906
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。