OSG中几何体的绘制(一)

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

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多边形分格化示例截图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/218928.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

驾龄不足三年如何注册网约车?刘师傅的反击之途!

刘师傅是个勤奋的司机。他想注册成为一名网约车司机&#xff0c;但由于驾龄不够三年&#xff0c;他一直被平台回绝。在多次尝试注册失败后&#xff0c;刘师傅慢慢四处求助。他咨询了多个渠道&#xff0c;但都没有得到有效的解决方案。当他在准备放弃的时候&#xff0c;他得知了…

Redis设计与实现之字符串哈希表列表

目录 一、字符串 1、字符串编码 2、编码的选择 二、哈希表 1、字典编码的哈希表 2、压缩列表编码的哈希表 3、编码的选择 4、哈希命令的实现 三、列表 1、 编码的选择 2、 列表命令的实现 3、阻塞的条件 4、 阻塞 5、 阻塞因 LPUSH 、RPUSH 、LINSERT 等添加命令而…

CSRF(跨站脚本请求)

一、漏洞原理 CSRF&#xff08;Cross-Site Request Forgery&#xff09;是一种网络安全攻击&#xff0c;攻击者通过欺骗用户在不知情的情况下发送请求&#xff0c;从而实现对目标网站的操作。 网站管理员(已经登录网站后台)——黑客构造的恶意服务器(是网站的创建用户请求)——…

Modbus转Profinet网关使用方法

Modbus转Profinet网关&#xff08;XD-MDPN100/200&#xff09;是用于将Modbus协议和Profinet协议进行转换并进行通迅的设备。Modbus转Profinet网关&#xff08;XD-MDPN100/200&#xff09;无论是新项目还是改造项目都可轻松配置完成通迅互联。 正确的安装和配置对于确保设备的正…

mysql的redolog、undo、binlog的作用

概览&#xff1a; MySQL三大日志包括&#xff1a;undolog&#xff0c;redo log&#xff0c;binlog&#xff0c;它们分别有以下作用&#xff1a; undolog&#xff1a;是Innodb存储引擎事务生成的日志。用于事务的回滚和MVCC&#xff0c;保证了事务的原子性。 redo log&#x…

智能优化算法应用:基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于供需算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.供需算法4.实验参数设定5.算法结果6.参考文献7.MA…

KaiwuDB × 国网山东综能 | 分布式储能云边端一体化项目建设

项目背景 济南韩家峪村首个高光伏渗透率台区示范项目因其所处地理位置拥有丰富的光照资源&#xff0c;该区域住户 80% 以上的屋顶都安装了光伏板。仅 2022 年全年&#xff0c;光伏发电总量达到了百万千瓦时。 大量分布式光伏并网&#xff0c;在输出清洁电力的同时&#xff0c…

Leetcode的AC指南 —— 链表:19.删除链表的倒数第N个节点

摘要&#xff1a; Leetcode的AC指南 —— 链表&#xff1a;19.删除链表的倒数第N个节点。题目介绍&#xff1a;给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 文章目录 一、题目二、解析1、滑动窗口/快慢指针&#xff08;傻傻分不清&…

中文编程工具下载,编程工具构件之复选框构件

一、前言 零基础自学编程&#xff0c;中文编程工具下载&#xff0c;中文编程工具构件之扩展系统菜单构件教程 编程系统化教程链接https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&a…

NNDL 循环神经网络-梯度爆炸实验 [HBU]

目录 6.2.1 梯度打印函数 6.2.2 复现梯度爆炸现象 6.2.3 使用梯度截断解决梯度爆炸问题 【思考题】梯度截断解决梯度爆炸问题的原理是什么&#xff1f; 总结 前言&#xff1a; 造成简单循环网络较难建模长程依赖问题的原因有两个&#xff1a;梯度爆炸和梯度消失。 循环…

网络基础(八):路由器的基本原理及配置

目录 1、路由概述 2、路由器 2.1路由器的工作原理 2.2路由器的转发原理 3、路由表 3.1路由表的概述 3.2路由表的形成 4、静态路由配置过程&#xff08;使用eNSP软件配置&#xff09; 4.1两个静态路由器配置过程 4.2三个静态路由器配置过程 5、默认路由配置过程 5.…

边缘计算系统设计与实践

随着科技的飞速发展&#xff0c;物联网和人工智能两大领域的不断突破&#xff0c;我们看到了一种新型的计算模型——边缘计算的崛起。这种计算模型在处理大规模数据、实现实时响应和降低延迟需求方面&#xff0c;展现出了巨大的潜力。本文将深入探讨边缘计算系统的设计原理和实…

黑马头条--day01.环境搭建

目录 一.前言 二.环境搭建 1.数据库 2.虚拟机搭建 3.1docker更换源 3.docker安装nacos 4.初始化工程 三.全局异常处理 四.登录加密 五.nacos公共配置数据源和mybatis-plus 六.user模块创建 1.配置文件bootstrap.yml 2.日志文件配置logback.xml 3.登录接口 七.统一结果处…

【INTEL(ALTERA)】 quartus SignalTap 逻辑分析器 – Nios® II 插件 无法检测 Nios® II/f 处理器内核

说明 使用 Nios II 插件将 Nios II/f 处理器内核节点添加到 SignalTap 逻辑分析器时&#xff0c;在 英特尔 Quartus Prime Pro Edition 软件 23.3 版中可能会出现此问题。 错误消息&#xff1a; 无法完成“添加带插件的节点”命令&#xff0c;因为在当前设计中找不到所选 IP。…

ubuntu 自动安装 MKL Intel fortran 编译器 ifort 及完美平替

首先据不完全观察&#xff0c;gfortran 与 openblas是 intel fortran 编译器 ifotr和mkl的非常优秀的平替&#xff0c;openblas连函数名都跟mkl一样&#xff0c;加了一个下划线。 1&#xff0c; 概况 https://www.intel.com/content/www/us/en/developer/tools/oneapi/base-too…

dockerfite创建镜像---INMP+wordpress

搭建dockerfile---lnmp 在192.168.10.201 使用 Docker 构建 LNMP 环境并运行 Wordpress 网站平台 [rootdocker1 opt]# mkdir nginx mysql php [rootdocker1 opt]# ls #分别拖入四个包&#xff1a; nginx-1.22.0.tar.gz mysql-boost-5.7.20.tar.gz php-7.1.10.tar.bz2 wor…

python:import 自定义包或者.py文件时出现:ModuleNotFoundError: no module named 的问题解决

问题&#xff1a; 在以下的示例中&#xff0c;wuHanMoviesSprider.py文件&#xff0c;想要import引用指定目录下的Items类时&#xff0c;出现无法识别module模块的问题(from 的引用处报错)。 原因分析&#xff1a; 正常情况下&#xff0c;被引用的包(或目录)中存在一个空文件…

golang反射(reflect)虽爽,但很贵

标准库 reflect 为 Go 语言提供了运行时动态获取对象的类型和值以及动态创建对象的能力。反射可以帮助抽象和简化代码&#xff0c;提高开发效率。 但是使用反射势必会多出大量的操作指令&#xff0c;导致性能下降 案例 字段赋值方式对比 type Student struct {Name string…

QML 自定义进度条组件开发

一、效果预览 二、介绍&#xff1a; 自定义的QML 屏幕亮度拖动进度条组件CusProgressBar 可跟鼠标移动 更改进度条样式 三、代码 import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Controls.Material 2.12/***author:Zwj*csdn:来份煎蛋吧*date:2023/12/16*…

爬虫工作量由小到大的思维转变---<第十一章 Scrapy之sqlalchemy模版和改造(番外)>

前言: 正常的pymysql当然问题不大,但是我个人还是建议:sqlalchemy! 因为他更能让我们把精力放在表单设计上,而不执着于代码本身了. (-----版权所有。未经作者书面同意&#xff0c;不得转载或用于任何商业用途!----) 正文: 先提供一个基础模版: 表图: 创建表的sql: CREA…