· 在OSG 中存在两棵树,即场景树和渲染树。场景树是一棵由 Node组成的树,这些Node可能是矩阵变换、状态切换或真正的可绘制对象,它反映了场景的空间结构,也反映了对象的状态。本章重点介绍场景树,在第 5章将会对渲染树作详细的介绍。
1. OSG场景树
1.1 OSG场景树节点
场景图形采用一种自顶向下的、分层的树状数据结构来组织空间数据集,以提升渲染的效率。例如,有一个房子,房子里面有窗户、门和家具等,这时场景图就可以描述为如图3-1所示:。
图3-1场景组织图
上面的场景中包含一个组节点——房子和3个子节点——窗、门、家具,其中,每个子节点包含所需要绘制的信息。
通过图 3-1 可以看到,场景图形树结构的顶部是一个根节点,从根节点向下延伸,各个组节点中均包含了几何信息和用于控制其外观的渲染状态信息。根节点和各个组节点都可以有0个或多个子成员。在场景图形的最底部,各个叶节点包含了构成场景中物体的实际几何信息。由此,可以这样来描述场景树:OSG程序使用组节点来组织和排列场景中的几何体。
场景树通常包括了多种类型的节点,以执行各种各样的用户功能,例如,开关节点可以设置其子节点可用或不可用;细节层次(LOD)节点可以根据观察者的距离调用不同的子节点,变换节点可以改变子节点几何体的坐标变换状态。面向对象的场景图形使用继承的机制来提供这种多样性,所有的节点类都有一个共有的基类,同时各自派生出实现特定功能的方法。
1.2 OSG中的父节点与子节点
OSG主要包含3大基本类节点,即Node、Geode(叶节点)和Group(组节点)。OSG中其他的大部分节点都继承自 Group节点,少部分继承自 Node节点及Geode 节点,但Geode和Group 均继承自Node 节点。
Geode(叶节点),顾名思义,作为叶节点,它不会再有其他的子节点,但它可以包含几何体信息。Geode的英文全称为 Geometry Node,即几何体节点。Node、Group和Geode 的继承关系图如图3-2、图3-3和图3-4所示。
图3-2 Node 的继承关系图
图3-3 Group 的继承关系图
图3-4 Geode的继承关系图
这里没有列举节点所有的继承关系,除了继承自 Node 和Geode 以外的所有节点均继承自 Group节点。
osg:Node类继承于osg::Objcct 类osg::Object类是应用程序无法直接实例化的虚基类,它提供了一系列接口,用于保存与获取节点名称,指定保存数据是静态的还是动态更改的,还继承osg::Referenced类的内存管理机制。
通常情况下,一个子节点至少有一个父节点,要获得该子节点的父节点指针,可以使用 getParent()函数。
通过图3-4可知,OSG中的节点直接或间接地以osg::Objcct和osg::Referenced 为基类,继承它的一系列的接口,用于保存和获取名称,同时定义自己特定的方法来执行不同功能。
2. Geode
osg:Geode是OSG中的叶节点,它用于保存几何信息以便渲染。同时,作为叶节点,它就不会再包含子节点。在应用程序中,所有相关的几何体的渲染都必须与Geode 节点相关联。在osgGeode类中,也提供了addDrawable()函数来关联应用程序中需要渲染的几何体信息。
2.1 Billboard节点
Billboard的继承关系图如图3-5所示。
从图3-5可以看出,Billboard 节点继承自 Geode 节点,因此它也是一个叶节点,不可再包含其他的子节点,只能像叶节点那样通过添加Drawable 来绘制信息。Billboard 有下面3种模式:
- enum Mode
- {
- POINT_ROT_EYE,// 绕视点
- POINT_ROT_WORLD,// 绕世界坐标
- AXIAL_ROT// 绕轴
- };
Billboard,从英文名可以看出是一种布告板技术,这是一种非常实用的技术,很多特殊效果都利用它来实现。布告板实现的原理是: 将图形绘制在朝向视点的多边形表面上,根据视点的观察方向来确定多边形的方向,随着观察角度的变化,多边形的方向也随之变换。osg::Billboard 布告板与 Alpha纹理和动画融合技术相结合可以实现很多没有实心表面的现象,如后面会讲到的粒子系统中的烟、火.雾、爆炸、雨、雪及云朵等。
在OSG 中使用了几种常见的布告板技术,主要包括面向世界的布告板和轴向布告板。其中,面向世界的布告板包括面向视平面的布告板和面向视点的布告板技术。图3-6表示了这两种技术的区别。
在轴向布告板技术中,经过纹理贴图的多边形通常不直接朝向视点,而是允许多边形围绕某个固定世界空间轴旋转,但调整多边形使其在此范围内尽可能朝向视点。这种布告板技术通常用来在场景中显示树木,它不是用实心表面来显示树木,而是使用一个四边形表面纹理显示树木。此时,一个树木布告板以树干为轴心作为世界的向上向量,保持世界的向上向量固定,视点观察方向作为树木旋转的第二个调整向量,一旦视点旋转矩阵生成,就可以对树木位置进行旋转变换。
但轴向布告板技术存在一个问题,当观察者处于空中飞行模式下,从树木顶部飞过并且垂直向下看时,看到的树木就像一个切面一样,非常难看。似乎对于这样的问题,前面的功夫都白费了。可以使用细节层次LOD模型或者替代模型弥补效果,或者可以使用一个球形树木表面来改善。
2.2 布告板示例
通过上面的解释,读者应该已经明白了什么是 Billboard 技术。现在就通过一个使用 Billboard的例子来讲解如何在程序中使用它。
在下面的程序中会使用到如何绘制几何体、PositionAttitudeTransform 节点以及纹理贴图,在后面的章节都会讲到这些,读者主要的任务是学习如何使用Billboard技术代码如程序清单3-1所示。
- osg::ref_ptr<osg::Node> createBillboardTree(osg::ref_ptr<osg::Image> image)
- {
- //创建四边形
- osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
- //设置顶点
- osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();
- v->push_back(osg::Vec3(-0.5f, 0.0f, -0.5f));
- v->push_back(osg::Vec3(0.5f, 0.0f, -0.5f));
- v->push_back(osg::Vec3(0.5f, 0.0f, 0.5f));
- v->push_back(osg::Vec3(-0.5f, 0.0f, 0.5f));
- geometry->setVertexArray(v.get());
- //设置法线
- osg::ref_ptr<osg::Vec3Array> normal = new osg::Vec3Array();
- normal->push_back(osg::Vec3(1.0f, 0.0f, 0.0f) ^ osg::Vec3(0.0f, 0.0f, 1.0f));
- geometry->setNormalArray(normal.get());
- geometry->setNormalBinding(osg::Geometry::BIND_OVERALL);
- //设置纹理坐标
- osg::ref_ptr<osg::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));
- geometry->setTexCoordArray(0, vt.get());
- //绘制四边形
- geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));
- if (image.get())
- {
- //状态属性对象
- osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();
- //创建一个Texture2D属性对象
- osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D();
- //关联image
- texture->setImage(image.get());
- //关联Texture2D纹理对象,第三个参数默认为ON
- stateset->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
- //启用混合
- stateset->setMode(GL_BLEND, osg::StateAttribute::ON);
- //关闭光照
- stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
- geometry->setStateSet(stateset.get());
- }
- //创建Billboard对象一
- osg::ref_ptr<osg::Billboard> billboard1 = new osg::Billboard();
- //设置旋转模式为绕视点
- billboard1->setMode(osg::Billboard::POINT_ROT_EYE);
- //添加Drawable,并设置其位置,默认位置为osg::Vec3(0.0f,0.0f,0.0f) ;
- billboard1->addDrawable(geometry.get(), osg::Vec3(5.0f, 0.0f, 0.0f));
- osg::ref_ptr<osg::Billboard> billboard2 = new osg::Billboard();
- //设置旋转模式为绕轴转,因此还需要设置转轴
- billboard2->setMode(osg::Billboard::AXIAL_ROT);
- //设置旋转轴
- billboard2->setAxis(osg::Vec3(0.0f, 0.0f, 1.0f));
- billboard2->addDrawable(geometry.get(), osg::Vec3(10.0f, 0.0f, 0.0f));
- osg::ref_ptr<osg::Group> billboard = new osg::Group();
- billboard->addChild(billboard1.get());
- billboard->addChild(billboard2.get());
- return billboard.get();
- }
- void billBoard_3_1(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- // 读取图像
- string strImagePath = strDataFolder + "Images/tree0.rgba";
- osg::ref_ptr<osg::Image> image = osgDB::readImageFile(strImagePath);
- //缩放一下,以达到合适的大小
- osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
- pat->setScale(osg::Vec3(5.0f, 5.0f, 5.0f));
- pat->addChild(createBillboardTree(image.get()));
- root->addChild(pat.get());
- //读取cow的模型,以对比
- string strDataPath = strDataFolder + "cow.osg";
- root->addChild(osgDB::readNodeFile(strDataPath));
- //优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-7所示。
图3-7布告板示例截图
通过上面简单的示例,相信读者已经学会使用 Billboard 技术关于Biboard类的更多方法,请参考函数帮助。
3. Group
osg::Group 类是OSG的组节点,它允许用户程序为其添加任意数量的子节点,子节点也可以继续分发子节点。osg::Group 作为一个基类,派生出了很多实用的节点类,如 osg::LOD、osg::Switch、osg::Transform、osg::PositionAttitudeTransform、osg::PagedLOD、osg::lmpostor 和 osg::AutoTransform等,后面的几节会对它们逐一进行介绍。
osg::Group 类是从osg::Referenced 类派生出来的,在通常情况下,只有父节点引用了这个Group对象,所以当释放场景图的根节点时,会引发连锁的内存释放动作,这样就不会产生内存的泄露。
osg::Group类作为OSG的核心部分,可以使用户程序通过Group 来有效地组织场景图中的数据。osg::Group类的强大之处在于管理子节点的接口,同时,osg::Group 类还从osg::Node类继承了用于管理父节点的接口,OSG允许节点有多个父节点。
3.1 位置变换节点
位置变换节点(osg::PositionAttitudeTransform)是一个位置变换节点,继承自osg::Transform,主要作用是提供模型的位置变换、大小缩放、原点位置的设置以及坐标系的变换。
osg::PositionAttitudeTransform 继承关系图如图3-8所示。
图3-8 osg::PositionAttitudeTransform 的继承关系图
osg::PositionAttitudeTransform的常用主要成员函数如下
- void setPosition(const Vec3d &pos) // 设置位置
- const Vec3d& getPosition() const // 得到位置
- void setAttitude(constQuat &quat) // 设置姿态,参数为四元数
- const Quat& getAttitude() const // 得到姿态
- void setScale(const Vec3d &scale) // 设置缩放
- const Vec3d &getScale()const // 得到缩放
- void setPivotPoint(const Vec3d &pivot) // 设置原点(自定义原点位置,一旦设置以后,所有变换均基于该原点)
- const Vec3d&getPivotPoint() const // 得到原点
3.2 位置变换节点示例
下面通过例子来讲解如何使用osg::PositionAttitudeTransform节点,代码如程序清单3-2所示。
- void positionAttitudeTransform(const string &strDataFolder)
- {
- // 创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- // 创建一个节点,读取牛的模型
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);
- // 创建位置变换节点pat1
- osg::ref_ptr<osg::PositionAttitudeTransform> pat1 = new osg::PositionAttitudeTransform();
- // 设置位置为osg::Vec3(-10.0f,0.0f,0.0f)
- pat1->setPosition(osg::Vec3(-10.0f, 0.0f, 0.0f));
- // 设置缩放,在X,Y,Z方向都缩小一倍
- pat1->setScale(osg::Vec3(0.5f, 0.5f, 0.5f));
- // 添加子节点
- pat1->addChild(node.get());
- // 创建位置变换节点pat2
- osg::ref_ptr<osg::PositionAttitudeTransform> pat2 = new osg::PositionAttitudeTransform();
- // 设置位置为osg::Vec3(10.0f,0.0f,0.0f)
- pat2->setPosition(osg::Vec3(10.0f, 0.0f, 0.0f));
- // 添加子节点
- pat2->addChild(node.get());
- // 添加到场景
- root->addChild(pat1.get());
- root->addChild(pat2.get());
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- // 设置场景数据
- viewer->setSceneData(root.get());
- // 初始化并创建窗口
- viewer->realize();
- // 开始渲染
- viewer->run();
- }
运行程序,截图如图3-9所示
图3-9位置变换节点示例截图
3.3 矩阵变换节点
矩阵变换节点(osg::MatrixTransform)同样继承自osg::Transform,其主要作用是负责场景中的知阵变换、矩阵的运算及坐标系的变换。后面要讲的动画更新也是用 MatrixTransform来设置移动和旋转通过使用矩阵变换节点(osg::MatrixTransform)可以对场景中的模型进行转平移等操作.
osg::MatrixTransform的继承关系图如图3-10所示。
图3-10 osg::MatrixTransform 的继承关系图
osg::MatrixTransform的常用主要成员函数如下
- void setMatrix(constMatrix &mat)// 设置矩阵
- const Matrix &getMatrix() const // 得到矩阵
- void preMult(constMatrix &mat) // 递乘,比较类似于++a
- void postMult(constMatrix&mat) // 递乘,比较类似于a++
- const Matrix &getInverseMatrix() const// 得到逆短阵
3.4 矩阵变换节点示例
矩阵变换节点(osg::MatrixTransform)示例的代码如程序清单3-3所示。
- void matrixTransform(const string &strDataFolder)
- {
- //创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- //创建一个节点,读取牛的模型
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strDataPath);
- //创建矩阵变换节点mt1
- osg::ref_ptr<osg::MatrixTransform> mt1 = new osg::MatrixTransform();
- //创建一个矩阵
- osg::Matrix m;
- //在X方向平移10个单位
- m.makeTranslate(osg::Vec3(10.0f, 0.0f, 0.0f));
- //绕X轴旋转45度
- m.makeRotate(45.0f, 1.0f, 0.0f, 0.0f);
- //设置矩阵
- mt1->setMatrix(m);
- //添加子节点
- mt1->addChild(node.get());
- //创建矩阵变换节点mt2
- osg::ref_ptr<osg::MatrixTransform> mt2 = new osg::MatrixTransform();
- //创建一个矩阵
- osg::Matrix t;
- //在X负方向上平移10个单位
- t.makeTranslate(osg::Vec3(-10.0f, 0.0f, 0.0f));
- //设置矩阵
- mt2->setMatrix(t);
- //添加子节点
- mt2->addChild(node.get());
- //添加到场景
- root->addChild(mt1.get());
- root->addChild(mt2.get());
- //优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- //设置场景数据
- viewer->setSceneData(root.get());
- //初始化并创建窗口
- viewer->realize();
- //开始渲染
- viewer->run();
- }
运行程序,截图如图3-11所示。
图 3-11矩阵变换节点示例截图
3.5 自动对齐节点
自动对齐节点(osg::AutoTransform)也继承自og::Transform,它的主要作用是使节点自动对齐于摄像机或者屏幕。在实际应用中,通常可以用来显示一些不变化的文字或者其他的标识。
osg::AutoTransform有如下3种对齐模式。
- enum AutoRotateMode
- {
- NO_ROTATION, // 无旋转
- ROTATE_TO_SCREEN, // 自动朝向屏幕
- ROTATE_TO_CAMERA // 自动朝向相机
- };
osg::AutoTransform的继承关系图如图3-12所示。
图3-12 osg::AutoTransform 的继承关系图
3.6 自动对齐节点示例
自动对齐节点(osg::AutoTransform)的示例代码如程序清单 3-4 所示(这用到了文字的显示,后面还有详细的讲解)。
- osg::ref_ptr<osg::Node> createAutoTransform(const string &strDataFolder, osg::Vec3& position, float size, std::string& label,
- osg::AutoTransform::AutoRotateMode autoMode, osgText::Text::AxisAlignment axisAlignment)
- {
- osg::ref_ptr<osg::Geode> geode = new osg::Geode();
- string strFontPath = strDataFolder + "fonts/cour.ttf";// 字体
- std::string font(strFontPath);
- // 创建Text对象
- osg::ref_ptr<osgText::Text> text = new osgText::Text();
- geode->addDrawable(text.get());
- text->setFont(font);// 设置字体
- text->setFontResolution(128.0f, 128.0f);// 设置字体的分辨率,默认为32*32
- text->setCharacterSize(size);// 设置字体的大小
- text->setAlignment(osgText::Text::CENTER_CENTER);// 设置对齐方式
- text->setAxisAlignment(axisAlignment);// 设置方向
- text->setText(label);// 设置文字
- geode->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF);// 关闭光照
- // 创建自动变换节点
- osg::ref_ptr<osg::AutoTransform> at = new osg::AutoTransform();
- at->addChild(geode.get()); // 添加子节点
- at->setAutoRotateMode(autoMode);// 设置自动变换方式
- at->setAutoScaleToScreen(false);// 根据屏幕大小来缩放节点,默认为false,设置为true时,节点无法缩放
- //at->setAutoScaleToScreen(true) ;
- at->setMinimumScale(0.0f);// 设置缩放的最大和最小比例
- at->setMaximumScale(5.0f);
- at->setPosition(position); // 设置位置
- return at.get();
- }
- void autoTransform_3_4(const string &strDataFolder)
- {
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- std::string text("Fly To Sky");
- /*
- 三种变换模式:
- ROTATE_TO_SCREEN 自动朝向屏幕
- ROTATE_TO_CAMERA 自动朝向相机
- NO_ROTATION 无
- */
- // 添加ROTATE_TO_SCEREEN模式变换节点
- root->addChild(createAutoTransform(strDataFolder, osg::Vec3(0.0f, 0.0f, 0.0f), 60.0f, text,
- osg::AutoTransform::ROTATE_TO_SCREEN, osgText::Text::XY_PLANE));
- //添加NO_ROTATION模式变换节点
- root->addChild(createAutoTransform(strDataFolder, osg::Vec3(0.0f, 0.0f, 0.0f), 60.0f, text,
- osg::AutoTransform::NO_ROTATION, osgText::Text::YZ_PLANE));
- // 添加ROTATE_TO_CAMERA模式变换节点
- //root->addChild(createAutoTransform(osg::Vec3(0.0f,0.0f,0.0f),60.0f,text,
- // osg::AutoTransform::ROTATE_TO_CAMERA,osgText::Text::XY_PLANE)) ;
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
}
运行程序,截图如图3-13 所示。
图3-13自动对齐节点示例截图
3.7 开关节点
开关节点(osg::Switch)继承自osg::Group节点使用开关节点可以渲染或者跳过指定的子节点。Switch 的优势在于能够根据当前渲染的负荷有选择地渲染子场景以实现渲染性能的均衡,或者在游戏的界面和层级之间有选择地切换。
如果想在应用程序中实现允许或者禁止各种开关节点的渲染,需要调用节点更新回调或者节点访问器来控制其渲染状态。
osg::Switch 的继承关系图如图3-14所示。
图3-14 osg::Switch 的继承关系图
osg::Switch的主要常用成员函数如下
- void setNewChildDefaultValue(bool value) // 设置新加节点的初始值
- bool getNewChildDefaultValue()const // 得到新加节点的初始值
- void setValue(unsigned int pos,bool value)// 设置索引为pos的值
- bool getValue(unsigncdint pos)const //得到索引为 pos 的值
- void setChildValue(const Node *child, bool value/设置 child 的值
- bool getChildValue(const Node *child) const //得到 child 的值
- bool setAllChildrenOff()//设置所有子节点不显示
- bool setAllChildrenOn()//设置所有子节点显示
- bool setSingleChildOn(unsigned int pos)//设置案引pos单个节点显示
3.8 开关节点示例
开关节点(osg::Switch)示例的代码如程序清单3-5所示。
- // 3_5 开关节点
- void switch_3_5(const string &strDataFolder)
- {
- //创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- // 创建一个节点,读取牛的模型
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node1 = new osg::Node();
- node1 = osgDB::readNodeFile(strDataFolder);
- // 创建一个节点,读取滑翔机模型
- strDataPath = strDataFolder + "glider.osg";
- osg::ref_ptr<osg::Node> node2 = osgDB::readNodeFile(strDataPath);
- // 创建一个开关节点,只渲染滑翔机而不渲染牛
- osg::ref_ptr<osg::Switch> swtich = new osg::Switch();
- swtich->addChild(node1.get(), false);// 添加牛的模型,但不渲染,默认为true
- swtich->addChild(node2.get(), true);// 添加滑翔机模型,状态为渲染
- root->addChild(swtich.get());
- //优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-15 所示(开关节点的开关渲染)
图3-15开关节点示例截图
3.9 细节层次节点
细节层次节点(osg::LOD)继承自osg::Group。使用细节层次节点可以实现不同层次下物体的渲染。细节层次(Level of Detail,LOD)的基本思想是使用物体的一种简单形式表达物体,这样可以使绘制的图形尽量简洁。当视点靠近物体时,用详细的细节表示;当视点远离物体时,用简化模型来表示。由于距离原因,简化后的模型与细节详细的模型看上去很接近,这样就可以获得一个比较好的加速效果。在后面的章节中还会讲到粒子系统中的雾效,可以与LOD 一起使用,当一个物体进入不透明的雾化区域时,可以完全不用绘制这个物体。此外,还可以使用雾化机制实现时间临界绘制,通过将远平面移近,可以对物体进行提前裁剪,这样可以取得比较快的绘制速度,从而提高帧率。
在OSG中,对于一个LOD 模型,它是一次性载入内存的,而且只是有选择地绘制渲染。osg::LOD的继承关系图如图3-16所示
图3-16 osg::LOD的继承关系图
LOD计算的是视点到物体包围盒中心的距离,在应用程序中也可以改变这种方式,它有两种中心模式。LOD的切换同样是根据距离来确定的,应用程序中同样可以根据屏幕像素大小来切换,它也有两种变换模式。
- enum CenterMode
- {
- USE_BOUNDING_SPHERE_CENTER,//包围盒中心
- USE_RDEFINED_CENTER//自定义中心,需要自己调用 setCentcr()
- };
- enum RangeMode
- {
- DISTANCE_FROM_EYE_POINT, // 距视点的距离
- PIXEL_SIZE_ON_SCREEN // 屏幕像素的大小
- };
3.10 细节层次节点示例
细节层次节点(osg::LOD)示例的代码如程序清单3-6所示。
- void LOD_3_6(const string &strDataFolder)
- {
- //创建Viewer对象,场景浏览器
- osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
- osg::ref_ptr<osg::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_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
- osg::ref_ptr<osg::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_ptr<osg::Group> root = new osg::Group();
- // 创建一个节点,读取一个牛的模型
- string strDataPath = strDataFolder + "cow.osg";
- osg::ref_ptr<osg::Node> node1 = osgDB::readNodeFile(strDataPath);
- // 创建一个节点,读取滑翔机模型
- strDataPath = strDataFolder + "glider.osg";
- osg::ref_ptr<osg::Node> node2 = osgDB::readNodeFile(strDataPath);
- osg::ref_ptr<osg::LOD> lode = new osg::LOD();// 创建一个细节层次LOD节点
- lode->addChild(node1.get(), 0.0f, 30.0f); // 添加子节点,在0-30范围内显示牛
- lode->addChild(node2.get(), 30.0f, 100.0f); //添加子节点,在30-100范围内显示滑翔机
- // 写入lode.osg文件
- strDataPath = strDataFolder + "lode.osg";
- osgDB::writeNodeFile(*(lode.get()), strDataPath);
- root->addChild(lode.get());// 添加到场景
- // 优化场景数据
- osgUtil::Optimizer optimizer;
- optimizer.optimize(root.get());
- viewer->setSceneData(root.get());
- viewer->realize();
- viewer->run();
- }
运行程序,截图如图3-17 所示(可以进行各细节层次间的切换)。
图3-17 细节层次节点示例截图