反钓鱼网站建设期,查看公司股票的网站,wordpress图片存,查网站访问量本章主要介绍一些几何体的绘制方法。绘制几何体在场景中是非常常见的#xff0c;也是最基本的。在很多应用程序中可以看到相当复杂的场景#xff0c;但不管场景有多复杂#xff0c;它们都是由少数几个基本的图形元素构建而成的。只要想想达芬奇那些伟大的作品也是由铅笔和画… 本章主要介绍一些几何体的绘制方法。绘制几何体在场景中是非常常见的也是最基本的。在很多应用程序中可以看到相当复杂的场景但不管场景有多复杂它们都是由少数几个基本的图形元素构建而成的。只要想想达芬奇那些伟大的作品也是由铅笔和画刷所完成的读者就不会为此感到疑惑了。只要有耐心读者也可以绘制同样复杂的场景。
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的绘图基元通过指定绘图基元来指定几何体顶点将采用哪一种或几种基元绘制。常用的绘图基元包括如下几种: POINTS GL_POINTS//绘制点 LINES GL_LINES//绘制线 LINE_STRIPGL_LINE_STRIP//绘制多段线 LINE_LOOPGL_LINE_LOOP//绘制封闭线 TRIANGLESGL_TRIANGLES//绘制一系列的三角形(不共用顶点) TRIANGLE_STRIPGL_TRIANGLE_STRIP//绘制一系列三角形(共用后面的两个顶点) TRIANGLE_FAN GL_TRIANGLE_FAN//绘制一系列三角形顶点顺序与上一条语绘制的三角形不同 QUADS GL_QUADS/绘制四边形 QUAD_STRIPGL_QUAD_STRIP//绘制一系列四边形 POLYGONGL_POLYGON//绘制多边形 从osg::PrimitiveSet类的继承关系图可以看出它的派生类主要有如下3个
osg::DrawArrays类。继承自osg::PrimitiveSet它封装了glDrawArrays()顶点数组绘图命令用于指定顶点和绘图基元。osg::DrawElements 类。它又派生出3个子类分别是osg::DrawElementsUByte、osg::DrawElementsUShort和osg::DrawElementsUInt封装了glDrawElements()的指令可以起索引的作用在后面的示例中会用到。osg::DrawArrayLengths类。它的主要作用是多次绘制即多次调用glDrawArrays()且每次均使用不同的长度和索引范围在绘制过程中用得不是很多。 DrawArrays的基本用法如下 osg::DrawArrays::DrawArrays( GLenum mode, GLint first, GLsizei count ); /*参数说明:第一个参数是指定的绘图基元即前面所列举的常见绘图元:第二个参数是指绘制几何体的第一个顶点数在指定顶点的位置数第三个参数是使用的顶点的总数*/ 还有一点值得注意的是虽然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) 指定向量数据。就是以前所涉及的点数据、纹理及颜等一系列向量数据可以通过下面的几个函数来实现: void setVertexArray(Array *array)// 设置顶点数组 void setVertexData(const AnrayDataarrayData) // 设置顶点数组数据 void setVertexIndices(IndexArrayarray)//设置顶点索引数组 void setNormalArray(Array*array)//设置法线数组 void setNormalData(const ArrayData arrayData) //设置法线数组数据 void setNormalIndices(IndexArray*array)//设置法线索引数组 void setColorArray(Array*array)//设置颜色数组 void setColorData(const ArrayData arrayData) //设置颜色数组数据 void setColorIndices(IndexArray array)//设置颜色索引数组 void setTexCoordArray(unsigned int unitArray*)//设置纹理坐标数组第一个参数是纹理单元第二个是纹理坐标数组 void setTexCoordData(unsigned int index,const ArrayData arrayData)//设置纹理坐标数组数据第一个参数是纹理单元第二个是纹理坐标数组数据 void setTexCoordIndices(unsigned int unit, IndexArray *)//设置纹理坐标索引数组第一个参数是纹理单元第二个是纹理索引坐标数组 (2)设置绑定方式。数据绑定主要有两项即法线及颜色可以通过下面的两个函数来实现: void setNormalBinding(AttributeBinding ab) //设置法线绑定方式 void setColorBinding(AttributeBinding ab) //设置颜色绑定方式 绑定方式主要有下面几种 BIND_OFF//不启用绑定 BIND_OVERALL//绑定全部的顶点 BIND_PER_PRIMITIVE_SET//单个绘图基元绑定 BIND_PER_PRIMITIVE//单个独立的绘图基元绑定 BIND_PER_VERTEX//单个顶点绑定 (3)数据解析。当在指定了各种向量数据和绑定方式之后采用何种方式来渲染几何体就是最为关键的。不同的方式下渲染出来的图形是不一样的即使效果一样可能面数或内部机制等也是有区别的。数据解析主要通过如下函数来指定: bool addPrimitiveSet(PrmitiveSet *primitiveset) /*参数说明osg::PrimitiveSet 是无法初始化的虚基类因此这里主要是调用它的子类来指定数据渲染最常用的就是前面介绍的osg:DrawArrays用法比较简单初始化一个对象实例参数说明见前面osg;:DrawArrays类*/ 通过前面的讲述可知绘制并渲染几何体主要有如下3大步骤: (1)创建各种向量数据如顶点、纹理坐标、颜色和法线等。需要注意的是添加顶点数据时主要按照逆时针顺序添加以确保背面剔除 (backface culling)的正确(后面还会有介绍)。 (2)实例化一个几何体对 (osg::Gemetry)设置点坐标数组、纹理坐标数组、颜色数组、法线数组、绑定方式及数据解析。 (3)加入叶节点绘制并渲染。 通过这么多的介绍相信读者已经完全明白了。下面的小节中会提供例子来说明如何绘制基本的几何体对象要仔细理解。
2.2 基本几何体绘制示例 基本几何体绘制(osg::Geometry)示例演示了创建一个几何体的过程示例中创建了最简单的四边形。通过该示例读者将学会如何创建简单的几何体。代码如程序清单4-1所示。 osg::ref_ptrosg::Node createQuad()//创建一个四边形节点 { // 创建一个叶节点对象 osg::ref_ptrosg::Geode geode new osg::Geode(); // 创建一个几何体对象 osg::ref_ptrosg::Geometry geom new osg::Geometry(); // 创建顶点数组注意顶点的添加顺序是逆时针的 osg::ref_ptrosg::Vec3Array v new osg::Vec3Array(); v-push_back(osg::Vec3(0.0f, 0.0f, 0.0f));// 添加数据 v-push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); v-push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); v-push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); // 设置顶点数据 geom-setVertexArray(v.get()); // 创建纹理坐标 osg::ref_ptrosg::Vec2Array vt new osg::Vec2Array(); vt-push_back(osg::Vec2(0.0f, 0.0f));//添加数据 vt-push_back(osg::Vec2(1.0f, 0.0f)); vt-push_back(osg::Vec2(1.0f, 1.0f)); vt-push_back(osg::Vec2(0.0f, 1.0f)); // 设置纹理坐标 geom-setTexCoordArray(0, vt.get()); //创建颜色数组 osg::ref_ptrosg::Vec4Array vc new osg::Vec4Array(); vc-push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));//添加数据 vc-push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); vc-push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); vc-push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); geom-setColorArray(vc.get());//设置颜色数组 geom-setColorBinding(osg::Geometry::BIND_PER_VERTEX);// 设置颜色的绑定方式为单个顶点 // 创建法线数组 osg::ref_ptrosg::Vec3Array nc new osg::Vec3Array(); nc-push_back(osg::Vec3(0.0f, -1.0f, 0.0f));// 添加法线 // 设置法线数组 geom-setNormalArray(nc.get()); geom-setNormalBinding(osg::Geometry::BIND_OVERALL);// 设置法线的绑定方式为全部顶点 geom-addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));// 添加图元绘图基元为四边形 geode-addDrawable(geom.get());// 添加到叶节点 return geode.get(); } // 基本几何体绘制 void baseGeometry_4_1() { // 创建Viewer对象场景浏览器 osg::ref_ptrosgViewer::Viewer viewer new osgViewer::Viewer(); osg::ref_ptrosg::GraphicsContext::Traits traits new osg::GraphicsContext::Traits; traits-x 40; traits-y 40; traits-width 600; traits-height 480; traits-windowDecoration true; traits-doubleBuffer true; traits-sharedContext 0; osg::ref_ptrosg::GraphicsContext gc osg::GraphicsContext::createGraphicsContext(traits.get()); osg::ref_ptrosg::Camera camera new osg::Camera; camera-setGraphicsContext(gc.get()); camera-setViewport(new osg::Viewport(0, 0, traits-width, traits-height)); GLenum buffer traits-doubleBuffer ? GL_BACK : GL_FRONT; camera-setDrawBuffer(buffer); camera-setReadBuffer(buffer); viewer-addSlave(camera.get()); osg::ref_ptrosg::Group root new osg::Group(); // 添加到场景 root-addChild(createQuad()); // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-4 所示 图4-4基本几何体绘制示例截图 2.3 索引绑定几何体绘制示例 通过前面的示例相信读者已经学会了如何创建简单的几何体。索引绑定几何体绘制(osg::Geometry)示例将向读者演示如何利用索引绑定几何体。代码如程序清单4-2所示。 osg::ref_ptrosg::Node createQuad_Index()// 创建一个四边形节点 { // 创建一个叶节点对象 osg::ref_ptrosg::Geode geode new osg::Geode(); // 创建一个几何体对象 osg::ref_ptrdeprecated_osg::Geometry geom new deprecated_osg::Geometry(); // 创建顶点数组 osg::ref_ptrosg::Vec3Array v new osg::Vec3Array(); v-push_back(osg::Vec3(0.0f, 0.0f, 0.0f));// 添加数据 v-push_back(osg::Vec3(1.0f, 0.0f, 0.0f)); v-push_back(osg::Vec3(1.0f, 0.0f, 1.0f)); v-push_back(osg::Vec3(0.0f, 0.0f, 1.0f)); v-push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); // 设置顶点数据 geom-setVertexArray(v.get()); // 创建四边形顶点索引数组指定绘图基元为四边形注意添加顺序 osg::ref_ptrosg::DrawElementsUInt quad new osg::DrawElementsUInt(osg::PrimitiveSet::QUADS, 0); quad-push_back(0);// 添加数据 quad-push_back(1); quad-push_back(2); quad-push_back(3); // 添加到几何体 geom-addPrimitiveSet(quad.get()); // 创建三角形顶点索引数组指定绘图基元为三角形注意添加顺序 osg::ref_ptrosg::DrawElementsUInt triangle new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLES, 0); triangle-push_back(4);//添加数据 triangle-push_back(0); triangle-push_back(3); // 添加到几何体 geom-addPrimitiveSet(triangle.get()); // 创建颜色数组 osg::ref_ptrosg::Vec4Array vc new osg::Vec4Array(); vc-push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));//添加数据 vc-push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f)); vc-push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f)); vc-push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f)); // 创建颜色索引数组 osg::TemplateIndexArrayunsigned int, osg::Array::UIntArrayType, 4, 4* colorIndex new osg::TemplateIndexArrayunsigned int, osg::Array::UIntArrayType, 4, 4(); colorIndex-push_back(0);// 添加数据注意添加数据顺序与顶点一一对应 colorIndex-push_back(1); colorIndex-push_back(2); colorIndex-push_back(3); colorIndex-push_back(2); geom-setColorArray(vc.get());//设置颜色数组 geom-setColorIndices(colorIndex);//设置颜色索引数组 geom-setColorBinding(deprecated_osg::Geometry::BIND_PER_VERTEX);//设置颜色的绑定方式为单个顶点 // 创建法线数组 osg::ref_ptrosg::Vec3Array nc new osg::Vec3Array(); nc-push_back(osg::Vec3(0.0f, -1.0f, 0.0f));// 添加法线 geom-setNormalArray(nc.get());// 设置法线数组 geom-setNormalBinding(deprecated_osg::Geometry::BIND_OVERALL);// 设置法线的绑定方式为全部顶点 geode-addDrawable(geom.get());// 添加到叶节点 return geode.get(); } void indexGeometry_4_2() { // 创建Viewer对象场景浏览器 osg::ref_ptrosgViewer::Viewer viewer new osgViewer::Viewer(); osg::ref_ptrosg::GraphicsContext::Traits traits new osg::GraphicsContext::Traits; traits-x 40; traits-y 40; traits-width 600; traits-height 480; traits-windowDecoration true; traits-doubleBuffer true; traits-sharedContext 0; osg::ref_ptrosg::GraphicsContext gc osg::GraphicsContext::createGraphicsContext(traits.get()); osg::ref_ptrosg::Camera camera new osg::Camera; camera-setGraphicsContext(gc.get()); camera-setViewport(new osg::Viewport(0, 0, traits-width, traits-height)); GLenum buffer traits-doubleBuffer ? GL_BACK : GL_FRONT; camera-setDrawBuffer(buffer); camera-setReadBuffer(buffer); viewer-addSlave(camera.get()); osg::ref_ptrosg::Group root new osg::Group(); // 添加到场景 root-addChild(createQuad_Index()); // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图 4-5 所示 图4-5索引绑定几何体绘制示例截图
3. 使用OSG中预定义的几何体 在OSG 中为了简化场景的绘制同时也为了方便开发者能够快速地构造一个场景它本身预定义了一些常用的几何体。下面分别进行介绍。
3.1 osg::Shape类 osg::Shape 类直接继承自osg::Object 基类承关系图如图4-6 所示。 图4-6 osg::Shape 的继承关系图 osg::Shape 类是各种内嵌几何体的基类它不但可用于剔除和碰撞检测还可用于生成预定义的几何体对象。 常用的内嵌几何体包括如下几种: osg::Box//正方体 osg::Capsule//太空舱 osg::Cone//椎体 osg::Cylinder//柱体 osg::HeightField//高度图 osg::InfinitePlane//无限平面 osg::Sphere//球体 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 的方法 ShapeDrawable(Shape*shape,TessellationHints *hints-0)//第一个参数为sape第二个参数默认下不细化 同时由于它继承自osg::Drawable类所以它的实例需要被添加到叶节点中才能被实例绘制。
3.3 网格化类 网格化类(osg::TessellationHints)直接继承自osg::Objcct 基类继承关系图如图4-8 所示。 图4-8 osg::TessellationHints 的继承关系图 osg::TessellationHints类的主要作用是设置预定义几何体对象的精细程度精细程度越高表示其细分越详细但对于不同的预定义几何体对象它的作用是不一样的例如: Box(四棱柱):网格化类对于四棱柱没有意义。 Capsule(太空舱):太空舱分3个部分上下半球部分和圆侧面部分默认侧面被细分 Cone(圆锥):直接细分 Cylinder(柱体):直接细分。 Sphere(球):直接细分。 目前osg::TessellationHints类并不完整部分类成员函数还没有实现具体可以参看源码。在内嵌几何体对象中默认的情况下网格化类的精细度为0表示预定义的几何体此时按照原顶点默认绘制不做任何细化处理。 3.4 预定义几何体示例 预定义几何体(osg::ShapeDrawable)示例的代码如程序清单4-3所示。 // 4_3 预定义几何体 osg::ref_ptrosg::Geode createShape()// 绘制多个预定义的几何体 { // 创建一个叶节点 osg::ref_ptrosg::Geode geode new osg::Geode(); // 设置半径和高度 float radius 0.8f; float height 1.0f; // 创建精细度对象精细度越高细分就越多 osg::ref_ptrosg::TessellationHints hints new osg::TessellationHints; hints-setDetailRatio(0.5f);// 设置精细度为0.5f geode-addDrawable(new osg::ShapeDrawable(new osg::Sphere(osg::Vec3(0.0f, 0.0f, 0.0f), radius), hints.get()));// 添加一个球体第一个参数是预定义几何体对象第二个是精细度默认为0 geode-addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(2.0f, 0.0f, 0.0f), 2 * radius), hints.get()));// 添加一个正方体 geode-addDrawable(new osg::ShapeDrawable(new osg::Cone(osg::Vec3(4.0f, 0.0f, 0.0f), radius, height), hints.get()));// 添加一个圆锥 geode-addDrawable(new osg::ShapeDrawable(new osg::Cylinder(osg::Vec3(6.0f, 0.0f, 0.0f), radius, height), hints.get()));// 添加一个圆柱体 geode-addDrawable(new osg::ShapeDrawable(new osg::Capsule(osg::Vec3(8.0f, 0.0f, 0.0f), radius, height), hints.get())); // 添加一个太空舱 return geode.get(); } void shapeDrawable_4_3() { // 创建Viewer对象场景浏览器 osg::ref_ptrosgViewer::Viewer viewer new osgViewer::Viewer(); osg::ref_ptrosg::GraphicsContext::Traits traits new osg::GraphicsContext::Traits; traits-x 40; traits-y 40; traits-width 600; traits-height 480; traits-windowDecoration true; traits-doubleBuffer true; traits-sharedContext 0; osg::ref_ptrosg::GraphicsContext gc osg::GraphicsContext::createGraphicsContext(traits.get()); osg::ref_ptrosg::Camera camera new osg::Camera; camera-setGraphicsContext(gc.get()); camera-setViewport(new osg::Viewport(0, 0, traits-width, traits-height)); GLenum buffer traits-doubleBuffer ? GL_BACK : GL_FRONT; camera-setDrawBuffer(buffer); camera-setReadBuffer(buffer); viewer-addSlave(camera.get()); osg::ref_ptrosg::Group root new osg::Group(); root-addChild(createShape());// 添加到场景 // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-9 所示。 图4-9 预定义几何体示例截图 4. 多边形分格化 如果读者对OpenGL有一定了解的话应该知道OpenGL为了快速渲染多边形只能直接显示简单的凸多边形。所谓简单的凸多边形就是多边形上任意两点的连线上的点依属于该多边形。对凹多边形或者自交叉多边形的渲染结果将不确定。下面列举一些需要分格化的多边形如图 4-10 所示。 为了正确显示凹多边形或者自交叉多边形就必须把它们分解为简单的凸多边形这种做法就称为多边形的分格化。OSG是对底层OpenGL API的封装所以它同样只能直接显示简单的凸多边形对于凹多边形或者自交叉多边形渲染也是不确定的。 在OSG中提供了一个多边形分格化的类osgUtil::Tessellator它继承自osg::Referenced类继承关系图如图4-11所示 在OSG中进行多边形分格化渲染需要如下3个步骤 (1)创建多边形分格化对象。 (2)设置分格化对象的类型通常有下面3种类型。 TESS_TYPE_GEOMETRY,//分格化几何体 TESS_TYPE_DRAWABLE,//分格化几何体中的Drawable(如多边形三形四边形等) TESS_TYPE_POLYGONS,//只分格化几何体中的多边形 (3)根据计算的环绕数指定相应的环绕规则。 1.环绕数 在《OpenGL编程指南》第5版中曾指出“对于一条简单的轮廓线每个点的环绕数就是环绕这个点的所有轮廓线的代数和(用一个有符号的整数表示求和规则是:逆时针环绕的轮廓线为正顺时针环绕的轮廓线为负)。这个过程把一个有符号的整数数值与平面上的每个顶点相关联。注意对于区域中的所有点它们的环绕数都是相同的。 图4-12为轮廓线与环绕数的计算方法读者可以通过此图理解环绕数及如何计算环绕数。 图4-12 轮廓线与环绕数的计算方法 2环绕规则 如果一个区域的环绕数属于环绕规则所选择的类型那么它就是它的内部区域。通常环绕规则把具有奇数和非零环绕数的区域定义为内部区域。环绕规则主要是针对环绕数来确定的。 几种常用的环绕规则如下: TESS_WINDING_ODDGLU_TESS_WINDING_ODD//环绕数为奇数 TESS_WINDING_NONZEROGLU_TESS_WINDING_NONZERO//环绕数为非零数 TESS_WINDING_POSITIVEGLU_TESS_WINDING_POSITIVE //环绕数为正数 TESS_WINDING_NEGATIVEGLU_TESS_WINDING_NEGATIVE //环绕数为负数 TESS_WINDING_ABS_GEQ_TWOGLU_TESS_WINDING_ABS_GEQ_TWO//环绕数为绝对值大于或等于2 多边形分格化(osgUtil::Tessellator)示例的代码如程序清单4-4所示 osg::ref_ptrosg::Geode tesslatorGeometry() // 使用分格化绘制凹多边形 { osg::ref_ptrosg::Geode geode new osg::Geode(); osg::ref_ptrosg::Geometry geom new osg::Geometry(); geode-addDrawable(geom.get()); // 以下是一些顶点数据 const float wall[5][3] //墙 { { 2200.0f, 0.0f, 1130.0f }, { 2600.0f, 0.0f, 1130.0f }, { 2600.0f, 0.0f, 1340.0f }, { 2400.0f, 0.0f, 1440.0f }, { 2200.0f, 0.0f, 1340.0f } }; const float door[4][3] // 门 { { 2360.0f, 0.0f, 1130.0f }, { 2440.0f, 0.0f, 1130.0f }, { 2440.0f, 0.0f, 1230.0f }, { 2360.0f, 0.0f, 1230.0f } }; const float windows[16][3] // 四扇窗户 { { 2240.0f, 0.0f, 1180.0f }, { 2330.0f, 0.0f, 1180.0f }, { 2330.0f, 0.0f, 1220.0f }, { 2240.0f, 0.0f, 1220.0f }, { 2460.0f, 0.0f, 1180.0f }, { 2560.0f, 0.0f, 1180.0f }, { 2560.0f, 0.0f, 1220.0f }, { 2460.0f, 0.0f, 1220.0f }, { 2240.0f, 0.0f, 1280.0f }, { 2330.0f, 0.0f, 1280.0f }, { 2330.0f, 0.0f, 1320.0f }, { 2240.0f, 0.0f, 1320.0f }, { 2460.0f, 0.0f, 1280.0f }, { 2560.0f, 0.0f, 1280.0f }, { 2560.0f, 0.0f, 1320.0f }, { 2460.0f, 0.0f, 1320.0f } }; // 设置顶点数据 osg::ref_ptrosg::Vec3Array coords new osg::Vec3Array(); geom-setVertexArray(coords.get()); // 设置法线 osg::ref_ptrosg::Vec3Array normal new osg::Vec3Array(); normal-push_back(osg::Vec3(0.0f, -1.0f, 0.0f)); geom-setNormalArray(normal.get()); geom-setNormalBinding(osg::Geometry::BIND_OVERALL); // 添加墙 for (int i 0; i 5; i) { coords-push_back(osg::Vec3(wall[i][0], wall[i][1], wall[i][2])); } geom-addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::POLYGON, 0, 5)); // 添加门 for (int i 0; i 4; i) { coords-push_back(osg::Vec3(door[i][0], door[i][1], door[i][2])); } // 添加窗 for (int i 0; i 16; i) { coords-push_back(osg::Vec3(windows[i][0], windows[i][1], windows[i][2])); } geom-addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 5, 20)); // 创建分格化对象 osg::ref_ptrosgUtil::Tessellator tscx new osgUtil::Tessellator(); tscx-setTessellationType(osgUtil::Tessellator::TESS_TYPE_GEOMETRY);// 设置分格类型为几何体 tscx-setBoundaryOnly(false);// 设置只显示轮廓线为false这里还需要填充 tscx-setWindingType(osgUtil::Tessellator::TESS_WINDING_ODD); // 设置环绕规则 tscx-retessellatePolygons(*(geom.get()));// 使用分格化 return geode.get(); } void Tessellator_4_4() { osg::ref_ptrosgViewer::Viewer viewer new osgViewer::Viewer(); osg::ref_ptrosg::GraphicsContext::Traits traits new osg::GraphicsContext::Traits; traits-x 40; traits-y 40; traits-width 600; traits-height 480; traits-windowDecoration true; traits-doubleBuffer true; traits-sharedContext 0; osg::ref_ptrosg::GraphicsContext gc osg::GraphicsContext::createGraphicsContext(traits.get()); osg::ref_ptrosg::Camera camera new osg::Camera; camera-setGraphicsContext(gc.get()); camera-setViewport(new osg::Viewport(0, 0, traits-width, traits-height)); GLenum buffer traits-doubleBuffer ? GL_BACK : GL_FRONT; camera-setDrawBuffer(buffer); camera-setReadBuffer(buffer); viewer-addSlave(camera.get()); osg::ref_ptrosg::Group root new osg::Group(); // 添加绘制的多边形 osg::ref_ptrosg::Geode geode tesslatorGeometry(); root-addChild(geode.get()); // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-13所示 图4-13多边形分格化示例截图