osg初级教程.docx
- 文档编号:8537317
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:14
- 大小:22.97KB
osg初级教程.docx
《osg初级教程.docx》由会员分享,可在线阅读,更多相关《osg初级教程.docx(14页珍藏版)》请在冰豆网上搜索。
osg初级教程
几何体的绘制
概述
本章将介绍一些创建几何体元素的方法。
通常我们有这样几种处理几何体的手段:
底层手段是使用松散封装的OpenGL基元;中级手段是使用OpenSceneGraph的基本几何体;高级手段是从文件读入模型。
本章教程将主要介绍底层手段的实现方法。
这种实现具有很强的灵活性,相应的工作量也比较大。
应用于场景图形级别的几何体通常是从文件读入的,因而顶点的跟踪和处理工作将由文件读取插件完成。
背景
下面将对几个常用的类作简要的介绍:
∙Geode类
Geode类派生自Node节点类。
节点类(包括Geode)可以作为场景图形的叶节点添加。
Geode类的实例可以与任意多个可绘制对象Drawable类相关联。
∙Drawable类
作为可绘制对象基类的Drawable类是一个纯虚类,它有六个派生类。
其中Geometry类中可以直接指定顶点数据,或者指定任意数目的几何基元PrimitiveSet类与其关联。
顶点和顶点属性数据(颜色,法线,纹理坐标)是保存在数组中的。
多个顶点可以共享同一种颜色,法线和纹理坐标,同时我们还可以使用索引将顶点数组映射给颜色,法线或纹理坐标的数组。
∙PrimitiveSet类
这个类松散地封装了OpenGL的绘图基元,包括点(POINTS),线(LINES),多段线(LINE_STRIP),封闭线(LINE_LOOP),四边形(QUADS),多边形(POLYGON)等。
代码
下面的代码将设置一个用于显示场景的视窗,一个作为场景图形根节点的Group类实例,一个用于记录可绘制对象(Drawable)的几何体节点(Geode),以及一个记录顶点和顶点相关数据的Geometry类实例。
本例中我们将渲染一个金字塔的形状。
...
intmain()
{
...
osg:
:
Group*root=newosg:
:
Group();
osg:
:
Geode*pyramidGeode=newosg:
:
Geode();
osg:
:
Geometry*pyramidGeometry=newosg:
:
Geometry();
现在我们将金字塔几何体与Geode关联,并将Geode叶节点添加到场景图形的根节点。
pyramidGeode->addDrawable(pyramidGeometry);
root->addChild(pyramidGeode);
声明一个顶点数组。
每个顶点有三个坐标值,也就是一个Vec3类的实例。
osg:
:
Vec3Array类的实例可以用来保存顶点数组。
它派生自STL库的vector模板,因此我们可以使用push_back方法向其中追加数组元素。
该方法的作用是在向量数组的末尾添加一个元素,因此数组中第一个元素的索引值为0,紧随其后的第二个元素为1,以此类推。
我们使用Z轴向上的右手坐标系作为参照,数组元素0-4用于表达金字塔形体的五个顶点。
osg:
:
Vec3Array*pyramidVertices=newosg:
:
Vec3Array;
pyramidVertices->push_back(osg:
:
Vec3(0,0,0));//左前
pyramidVertices->push_back(osg:
:
Vec3(10,0,0));//右前
pyramidVertices->push_back(osg:
:
Vec3(10,10,0));//右后
pyramidVertices->push_back(osg:
:
Vec3(0,10,0));//左后
pyramidVertices->push_back(osg:
:
Vec3(5,5,10));//塔尖
将这一顶点集合关联到Geometry实例上,后者已经与场景的Geode叶节点相关联。
pyramidGeometry->setVertexArray(pyramidVertices);
现在我们创建几何基元类PrimitiveSet的实例并添加到金字塔几何体上。
金字塔底部的四个点组成一个基面,可以使用DrawElementsUint类来实现。
这个类同样继承自STL库的vector模板,我们可以使用push_back顺序向其中添加元素。
为了保证背面剔除(backfacecullling)的正确,我们需要按照逆时针的顺序添加顶点数据。
类的构造函数使用几何基元枚举类型(与OpenGL的几何基元枚举类型相同)作为输入参数,另一个输入参数是作为起始点的顶点索引值。
osg:
:
DrawElementsUInt*pyramidBase=
newosg:
:
DrawElementsUInt(osg:
:
PrimitiveSet:
:
QUADS,0);
pyramidBase->push_back(3);
pyramidBase->push_back
(2);
pyramidBase->push_back
(1);
pyramidBase->push_back(0);
pyramidGeometry->addPrimitiveSet(pyramidBase);
重复这一过程,添加金字塔的每个面。
再次注意,顶点是以逆时针为顺序添加的。
osg:
:
DrawElementsUInt*pyramidFaceOne=
newosg:
:
DrawElementsUInt(osg:
:
PrimitiveSet:
:
TRIANGLES,0);
pyramidFaceOne->push_back(0);
pyramidFaceOne->push_back
(1);
pyramidFaceOne->push_back(4);
pyramidGeometry->addPrimitiveSet(pyramidFaceOne);
osg:
:
DrawElementsUInt*pyramidFaceTwo=
newosg:
:
DrawElementsUInt(osg:
:
PrimitiveSet:
:
TRIANGLES,0);
pyramidFaceTwo->push_back
(1);
pyramidFaceTwo->push_back
(2);
pyramidFaceTwo->push_back(4);
pyramidGeometry->addPrimitiveSet(pyramidFaceTwo);
osg:
:
DrawElementsUInt*pyramidFaceThree=
newosg:
:
DrawElementsUInt(osg:
:
PrimitiveSet:
:
TRIANGLES,0);
pyramidFaceThree->push_back
(2);
pyramidFaceThree->push_back(3);
pyramidFaceThree->push_back(4);
pyramidGeometry->addPrimitiveSet(pyramidFaceThree);
osg:
:
DrawElementsUInt*pyramidFaceFour=
newosg:
:
DrawElementsUInt(osg:
:
PrimitiveSet:
:
TRIANGLES,0);
pyramidFaceFour->push_back(3);
pyramidFaceFour->push_back(0);
pyramidFaceFour->push_back(4);
pyramidGeometry->addPrimitiveSet(pyramidFaceFour)
定义一个Vec4的数组,用于保存颜色值。
osg:
:
Vec4Array*colors=newosg:
:
Vec4Array;
colors->push_back(osg:
:
Vec4(1.0f,0.0f,0.0f,1.0f));//索引0红色
colors->push_back(osg:
:
Vec4(0.0f,1.0f,0.0f,1.0f));//索引1绿色
colors->push_back(osg:
:
Vec4(0.0f,0.0f,1.0f,1.0f));//索引2蓝色
colors->push_back(osg:
:
Vec4(1.0f,1.0f,1.0f,1.0f));//索引3白色
下一步要将顶点数组的元素与颜色数组的元素对应起来。
我们将声明一个与顶点数组有相同个数元素的向量组。
它将负责连接各个顶点与颜色。
该向量组的索引对应顶点数组的元素,其取值对应颜色数组的索引。
如果需要将顶点数组与法线数组或者纹理坐标数组一一对应,那么还需重复这一步骤。
注意在本例中,我们需要将5个顶点对应到4种颜色上。
因此顶点数组的元素0(左下)和元素4(塔尖)都需要对应到颜色数组元素0(红色)上。
osg:
:
TemplateIndexArray
: Array: : UIntArrayType,4,4>*colorIndexArray; colorIndexArray= newosg: : TemplateIndexArray : Array: : UIntArrayType,4,4>; colorIndexArray->push_back(0);//vertex0assignedcolorarrayelement0 colorIndexArray->push_back (1);//vertex1assignedcolorarrayelement1 colorIndexArray->push_back (2);//vertex2assignedcolorarrayelement2 colorIndexArray->push_back(3);//vertex3assignedcolorarrayelement3 colorIndexArray->push_back(0);//vertex4assignedcolorarrayelement0 下一步,我们将颜色数组以及刚才创建的颜色索引数组与几何体相关联,并设置绑定模式为“按顶点绑定”。 pyramidGeometry->setColorArray(colors); pyramidGeometry->setColorIndices(colorIndexArray); pyramidGeometry->setColorBinding(osg: : Geometry: : BIND_PER_VERTEX); osg: : Vec2Array*texcoords=newosg: : Vec2Array(5); (*texcoords)[0].set(0.00f,0.0f); (*texcoords)[1].set(0.25f,0.0f); (*texcoords)[2].set(0.50f,0.0f); (*texcoords)[3].set(0.75f,0.0f); (*texcoords)[4].set(0.50f,1.0f); pyramidGeometry->setTexCoordArray(0,texcoords); 现在我们已经创建了一个几何体节点,并将其添加到场景中。 其中的几何体是可以复用的,例如,如果我们希望在第一个金字塔右侧15个单位再放置第二个金字塔,那么我们可以将这个Geode节点作为位置变换节点的子节点,并再次添加到场景当中。 //初始化位置变换节点 osg: : PositionAttitudeTransform*pyramidTwoXForm= newosg: : PositionAttitudeTransform(); //使用osg: : Group的addChild方法,将位置变换节点添加到根节点的子节点上,并将金字塔节点作为变换节点的子节点 root->addChild(pyramidTwoXForm); pyramidTwoXForm->addChild(pyramidGeode); //初始化一个Vec3实例,用于改变模型在场景中的位置 osg: : Vec3pyramidTwoPosition(15,0,0); pyramidTwoXForm->setPosition(pyramidTwoPosition); 最后,设置视窗类并进入仿真循环。 osgViewer: : Viewerviewer; viewer.setSceneData(root); viewer.run(); 继续到下一节使用StateSet创建带有纹理的几何体 使用StateSet创建带有纹理的几何体 目标 向使用OpenGL基元绘制的基本几何体添加纹理。 背景 上一个教程介绍了使用OpenGL基元创建基本几何体并在视窗中浏览的方法。 本章将介绍如何向几何体添加纹理。 为了使代码易于阅读,我们将有关金字塔绘制的代码封装到一个函数中,并返回一个Geode指针。 代码如下所示: osg: : Geode*createPyramid() { osg: : Geode*pyramidGeode=newosg: : Geode(); osg: : Geometry*pyramidGeometry=newosg: : Geometry(); pyramidGeode->addDrawable(pyramidGeometry); //指定顶点 osg: : Vec3Array*pyramidVertices=newosg: : Vec3Array; pyramidVertices->push_back(osg: : Vec3(0,0,0));//左前 pyramidVertices->push_back(osg: : Vec3(2,0,0));//右前 pyramidVertices->push_back(osg: : Vec3(2,2,0));//右后 pyramidVertices->push_back(osg: : Vec3(0,2,0));//左后 pyramidVertices->push_back(osg: : Vec3(1,1,2));//塔尖 //将顶点数组关联给几何体 pyramidGeometry->setVertexArray(pyramidVertices); //根据底面的四个顶点创建底面四边形(QUAD) osg: : DrawElementsUInt*pyramidBase= newosg: : DrawElementsUInt(osg: : PrimitiveSet: : QUADS,0); pyramidBase->push_back(3); pyramidBase->push_back (2); pyramidBase->push_back (1); pyramidBase->push_back(0); //创建其他面的代码从略 osg: : Vec4Array*colors=newosg: : Vec4Array; colors->push_back(osg: : Vec4(1.0f,0.0f,0.0f,1.0f));//索引0红色 colors->push_back(osg: : Vec4(0.0f,1.0f,0.0f,1.0f));//索引1绿色 colors->push_back(osg: : Vec4(0.0f,0.0f,1.0f,1.0f));//索引2蓝色 colors->push_back(osg: : Vec4(1.0f,1.0f,1.0f,1.0f));//索引3白色 osg: : TemplateIndexArray : Array: : UIntArrayType,4,4>*colorIndexArray; colorIndexArray= newosg: : TemplateIndexArray : Array: : UIntArrayType,4,4>; colorIndexArray->push_back(0);//顶点0对应颜色元素0 colorIndexArray->push_back (1);//顶点1对应颜色元素1 colorIndexArray->push_back (2);//顶点2对应颜色元素2 colorIndexArray->push_back(3);//顶点3对应颜色元素3 colorIndexArray->push_back(0);//顶点4对应颜色元素0 pyramidGeometry->setColorArray(colors); pyramidGeometry->setColorIndices(colorIndexArray); pyramidGeometry->setColorBinding(osg: : Geometry: : BIND_PER_VERTEX); //由于纹理坐标与顶点是一一对应的,因此不需要使用索引数组来进行映射。 我们只需要直接使用setTexCoordArray方法即可.setTexCoordArray方法传递osg: : Vec2的二维坐标数组作为参数,传递的二维坐标数与顶点数相同。 数组中元素的位置与顶点数组中顶点的位置一一对应。 osg: : Vec2Array*texcoords=newosg: : Vec2Array(5); (*texcoords)[0].set(0.00f,0.0f);//顶点0的纹理坐标 (*texcoords)[1].set(0.25f,0.0f);//顶点1的纹理坐标 (*texcoords)[2].set(0.50f,0.0f);//顶点2的纹理坐标 (*texcoords)[3].set(0.75f,0.0f);//顶点3的纹理坐标 (*texcoords)[4].set(0.50f,1.0f);//顶点4的纹理坐标 pyramidGeometry->setTexCoordArray(0,texcoords); returnpyramidGeode; } 加载纹理,创建渲染状态类,关联到节点 我们使用StateSet类来控制几何基元的渲染状态。 下面的代码演示了从文件中读取纹理,创建StateSet类并设置纹理,以及关联StateSet到场景节点的方法。 代码的前一部分与上一章教程相同。 首先我们初始化视窗并创建只有单一金字塔的场景。 intmain() { //声明场景的根节点 osg: : Group*root=newosg: : Group(); osg: : Geode*pyramidGeode=createPyramid(); root->addChild(pyramidGeode); 现在我们添加纹理。 我们定义一个纹理实例并设置其数据变更类型为“DYNAMIC”(否则的话,OSG的优化过程中可能会自动去除这个纹理)。 纹理类封装了OpenGL纹理模式(GL_TEXTURE_WRAP,GL_TEXTURE_FILTER等),以及一个osg: : Image对象。 下面的代码将演示如何从文件读入osg: : Image实例并将其关联到纹理。 osg: : Texture2D*KLN89FaceTexture=newosg: : Texture2D; //避免在优化过程中出错 KLN89FaceTexture->setDataVariance(osg: : Object: : DYNAMIC); //从文件读取图片 osg: : Image*klnFace=osgDB: : readImageFile("KLN89FaceB.tga"); if(! klnFace) { std: : cout<<"couldn'tfindtexture,quiting."< : endl; return-1; } //将图片关联到纹理 KLN89FaceTexture->setImage(klnFace); Texture类可以关联到渲染状态StateSet类。 下一步我们将创建一个StateSet,将纹理关联到这个渲染状态实例并允许使用纹理,最后将StateSet关联到几何体节点上。 //创建StateSet osg: : StateSet*stateOne=newosg: : StateSet(); //将纹理关联给StateSet的纹理单元0 stateOne->setTextureAttributeAndModes (0,KLN89FaceTexture,osg: : StateAttribute: : ON); //将渲染状态关联给金字塔节点 pyramidGeode->setStateSet(stateOne); osgViewer: : Viewerviewer; //最后我们进入仿真循环: viewer.setSceneData(root); returnviewer.run(); } 使用内嵌几何形状(Shape)对象,改变渲染状态 目标 使用osg: : Shape实例构建场景。 使用osg: : StateSet控制几何形状的渲染。 Shape类的使用 Shape类是各种内嵌几何形状的基类。 它可以用于剔除和碰撞检测,或者设计并生成简单几何形体。 下面这些类均派生自Shape类: ∙TriangleMesh ∙Sphere ∙InfinitePlane ∙HeightField ∙Cylinder ∙Cone ∙CompositeShape ∙Box 如果要渲染这些内嵌形体,我们需要首先将其与Drawable类的实例相关联。 我们可以使用ShapeDrawable类来完成这一功能。 它派生自Drawable类,并提供了关联Shape实例的方法。 正因为ShapeDrawable是继承自Drawable的,它的实例因而可以被添加到Geode实例中。 下面的代码演示了在场景中添加单位立方体的方法: //创建场景的根节点 osg: : Group*root=newosg: : Group(); //声明Box类(派生自Shape)的实例。 构造函数的参数为: osg: : Vec3的中心位置,浮点数定义统一的高/宽/深度。 //(我们也可以分别输入不同的高度、宽度、深度值) osg: : Box*unitCube=newosg: : Box(osg: : Vec3(0,0,0),1.0f); //声明ShapeDrawable类的实例,使用刚才创建的unitCube作为传递参数。 这个类派生自D
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- osg 初级教程