大连网站制作选择ls15227,站长工具查询域名信息,中国航天空间站最新消息,做英语趣味教具的网站5. 几何体操作 在本章的前言中就讲到#xff0c;场景都是由基本的绘图基元构成的#xff0c;基本的绘图基元构成简单的几何体,简单的几何体构成复杂的几何体#xff0c;复杂的几何体最终构造成复杂的场景。当多个几何体组合时#xff0c;可能存在多种降低场景渲染效率的原因…5. 几何体操作 在本章的前言中就讲到场景都是由基本的绘图基元构成的基本的绘图基元构成简单的几何体,简单的几何体构成复杂的几何体复杂的几何体最终构造成复杂的场景。当多个几何体组合时可能存在多种降低场景渲染效率的原因。在很多3D引擎中都提供了对场景的几何体进行修改的操作以达到最优渲染效率。虽然最优渲染效率只是一个理想状态但一定的几何体操作在相当程度上可以提高渲染效率。 在OSG中为了获得所需的性能和渲染的效率osgUtil库提供了一些通用的几何体运算这些几何体运算主要包括 osgUtil::Simplifier (简化)、osgUtil::SmoothingVisitor(生成法线)、osgUtil::DelaunayTriangulator (生成 Delaunay 三角网工具)和osgUtil::TriStripVisitor (条带化)等。 下面就来介绍几种常用的几何体操作。
5.1 简化几何体 简化几何体(osgUtil::Simplifier)类继承自osg::NodeVisitor类它采用访问器的方式遍历几何体并对其进行简化处理在后面的章节将会详细说明访问器的工作机制。 osgUtil::Simplifier 的继承关系图如图4-14 所示 图4-14 osgUtil::Simplifier 的继承关系图 osgUtil::Simplifier 类对几何体的简化主要需要设置两个方面的参数即当几何体样本比率小于1设置点的误差限制;当几何体的样本比率大于 1 时设置边的长度限制。通过对点的误差或者边时的长度的限制简化不必要的点和边然后通过平滑处理和条带化渲染几何体进而达到提高渲染效率的目的也可以用于自动生成低层次模型。一般来说样本比率越大简化越少;样本比率越小简化越多。优化可以直接调用下面的函数: virtual void apply(osg::Gcode geode)//应用于叶节点 void simplify(osg::Geometry geometry)/简化儿何体 这两个函数同样可用于osg::Node节点不过关联实例时需要使用accept()方法osgUtil::Simplifer 简化采用的是边塌陷算法对于这个算法笔者并没有深入研究。记得在邮件列表中也提到了 Robert关于这个算法的文档已经丢失。目前网上很少有关于这方面的文档。至于算法是如何实现的有兴趣的朋友可以进行源码研究。如果读者研究出了结果欢迎到OSG中国官方讨论区(http://bbs.OsgChina.org)发帖。
5.2 简化几何体示例 简化几何体(osgUtil::Simplifier)示例的代码如程序清单4-5所示。 void simplifier_4_5(const string strDataFolder) { // 创建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()); // 切换网格模式方便比较模型的区别 viewer-addEventHandler(new osgGA::StateSetManipulator(viewer-getCamera()-getOrCreateStateSet())); osg::ref_ptrosg::Group root new osg::Group(); // 设置样本比率样本比率越大简化越少 float sampleRatio 0.3f;// 样本比率越小简化越多 float maxError 4.0f; // 设置点的最大误差 // 创建简化对象 osgUtil::Simplifier simplifier(sampleRatio, maxError); // 读取牛的模型 string strDataPath strDataFolder cow.osg; osg::ref_ptrosg::Node node1 osgDB::readNodeFile(strDataPath); // 深拷贝牛的模型到node2节点 osg::ref_ptrosg::Node node2 (osg::Node*)(node1-clone(osg::CopyOp::DEEP_COPY_ALL)); // 创建一个位置变换节点 osg::ref_ptrosg::PositionAttitudeTransform pat new osg::PositionAttitudeTransform(); pat-setPosition(osg::Vec3(10.0f, 0.0f, 0.0f));// 设置位置 pat-addChild(node2.get());// 添加子节点 pat-accept(simplifier); // 简化处理 // 添加到场景 root-addChild(node1.get()); root-addChild(pat.get()); // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-15所示 图4-15简化几何体示例截图
5.3 Delaunay三角网绘制 1.TIN 模型 在数字地形建模中不规则三角网(TIN)通过从不规则离散分布的数据点生成的连续三角面来逼近地形表面。就表达地形信息的角度而言TIN 模型的优点是它能以不同层次的分辨率来描述地形表面。与格网数据模型相比TIN 模型在某一特定分辨率下能用更少的空间和时间更精确地表示更加复杂的表面。特别是当地形包含有大量特征如断裂线、构造线时TIN 模型能更好地顾及这些特征从而能更精确合理地表达地表形态。 对于TIN模型其基本要求有以下3点 (1)TIN 是唯一的。 (2)力求最佳三角形几何形状。 (3)保证最邻近的点构成三角形。 在所有可能的三角网中狄洛尼(Delaunay)三角网在地形拟合方面表现最为出色常用于 TIN的生成。当不相交的断裂线等被作为预先定义的限制条件作用于 TIN 的生成当中时就必须考虑带约束条件的狄洛尼三角网。 目前狄洛尼三角网生成的算法比较多传统的算法主要包括两种即 Lawson 算法以及Bowyer-Watson 算法。后来经过该传统算法改进的算法就多了这里不专门对这些算法予以介绍可参考相关论文。 2.osgUtil::DelaunayTriangulator 类 上面介绍了狄洛尼(Delaunay)三角网的特性、生成算法及应用下面来看一下OSG中的狄洛尼三角网。 osgUtil::DelaunayTriangulator 类直接继承自osg::Referenced 类继承关系图如图4-16所示。 图4-16 osgUtil::DelaunayTriangulator 的继承关系图 创建狄洛尼三角网有如下3个步骤: (1) 创建顶点数组。 (2) 创建一个osgUtil::DelaunayTriangulator对象并初始化顶点数组同时生成三角网程序代码如下: bool triangulate()// 开始生成三角网格 (3) 创建一个几何体对象把osgUtil::DelaunayTriangulator 类对象生成的绘制图元加入到几何体中。在生成狄洛尼三角网时读者还可以添加一些限制条件限制条件可以是点、线或多边形例如: void addInputConstraint(DelaunayConstraint*dc)//添加限制条件 通过这么多的讲解读者应该清楚了狄洛尼三角网的绘制方法了吧。有兴趣的读者可以研究关于狄洛尼三角网的算法。
5.4 Delaunay三角网绘制示例 Delaunay三角网绘制(osgUtil::DelaunayTriangulator)示例的代码如程序清单46所示。 void delaunay_4_6() { 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()); // 方便查看在多边形之间切换以查看三角网 viewer-addEventHandler(new osgGA::StateSetManipulator(viewer-getCamera()-getOrCreateStateSet())); osg::ref_ptrosg::Group root new osg::Group(); // 创建顶点数组 osg::ref_ptrosg::Vec3Array coords new osg::Vec3Array(); // 计算顶点数组的大小 unsigned int n sizeof(vertex) / sizeof(float[3]); // 添加顶点数据 for (unsigned int i 0; i n; i) { coords-push_back(osg::Vec3(vertex[i][0], vertex[i][1], vertex[i][2])); } // 创建Delaunay三角网对象 osg::ref_ptrosgUtil::DelaunayTriangulator dt new osgUtil::DelaunayTriangulator(coords.get()); dt-triangulate();// 生成三角网 // 创建几何体 osg::ref_ptrosg::Geometry geometry new osg::Geometry(); geometry-setVertexArray(coords.get()); // 设置顶点数组 geometry-addPrimitiveSet(dt-getTriangles());// 加入到绘图基元 // 添加到叶节点 osg::ref_ptrosg::Geode geode new osg::Geode(); geode-addDrawable(geometry.get()); root-addChild(geode.get()); // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-17所示 图4-17 Delaunay 三角网绘制示例截图 5.5 三角带绘制 三角带绘制(osgUtil::TriStripVisitor)类继承自osgUtil::BaseOptimizerVisitor 类它采用访问器的机制遍历场景中的几何体实现三角带绘制以提高染效率。osgUtil::TriStripVisitor 的继承关系图如图4-18所示。 图4-18 osgUtil::TriStripVisitor 的继承关系图 下面对osgUtil::TriStripVisitor 的两个常用成员函数予以说明。 void stripify(osg::Geometry drawable)//条带化几何体 virtual void apply(osg::Geode geode)//应用于叶节点 从上面可以看出它同样可应用于叶节点。值得注意的是关联叶节点实例时需要调用 accept()方法。 大多数图形卡并不直接支持索引三角网。在渲染三角形时一般是将3个顶点同时提交这样共享的顶点会多次提交三角形用到一次就提交一次。因为内存和图形硬件间的数据传输是瓶颈所以很多API和硬件支持特殊的三角网格式以减少传输量。基本思想是排序点和面使显卡中已有的三角形不需要再次传输。 这里我们只讨论三角带三角带是一个三角形列表其中每个三角形都与前一个三角形共享一边。在一种最理想的状态下三角带可以用 N2个顶点表示存个面。N很大时每个三角形平均发送一个顶点。在实践中很多情况下很多网格是一个三角带无法表达的此时就需要多个三角带来联合绘制。因为我们希望最小化发往图形卡的顶点数所以三角带的数目应该尽可能得少即三角带越长越好。从另一个方面来说分别渲染两个长度为N的三角带所需要的时间比渲染一个长度为2N 的三角带要长即使2N 的三角带中三角形的个数多于两个分开的三角带中的三角形个数的和因为建立各三角带需要额外的处理时间。 学习了上面的基础知识读者再去看关于这部分的源代码就会感觉非常易懂了这些更多是原理的问题。
5.6 三角带绘制示例 三角带绘制(osgUtil::TriStripVisitor)示例的代码如程序清单4-7所示。 osg::ref_ptrosg::Geometry createQuad1() // 创建一个四边形节点 { // 创建一个几何体对象 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));// 添加图元绘图基元为四边形 return geom.get(); } void TriStripVisitor_4_7() { // 创建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()); // 方便查看在多边形之间切换以查看三角网 viewer-addEventHandler(new osgGA::StateSetManipulator(viewer-getCamera()-getOrCreateStateSet())); osg::ref_ptrosg::Group root new osg::Group(); // 创建一个几何体对象 osg::ref_ptrosg::Geometry geometry createQuad1(); // 对几何体进行条带化 osgUtil::TriStripVisitor stripper; stripper.stripify(*(geometry.get())); // 添加到叶节点 osg::ref_ptrosg::Geode geode new osg::Geode(); geode-addDrawable(geometry.get()); root-addChild(geode.get());// 添加到场景 // 优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-19所示。 图4-19 三角带绘制示例截图
5.7 生成顶点法向量 生成顶点法向量(osgUtil::SmoothingVisitor)类继承自osg::NodeVisitor 类它采用访问器机制遍历场景中的几何体生成顶点法向量。它的继承关系图如图4-20所示 图4-20 osgUtil::SmoothingVisitor的继承关系图 下面对osgUtil::SmoothingVisitor的一个常用成员函数予以说明 static void smooth(osg::Geometry geo); 该函数用于生成顶点法向量调用时需要注意这是一个静态函数。 在很多应用程序中网格上的各点都需要一个表面法向量它的用处非常广泛例如 计算光照。背面剔除。模拟粒子系统在表面的“弹跳”效果。通过只需要正面而加速碰撞检测。 通常我们在绘制几何体时都会指定法线。当得到一个模型它本身并没有法向量时则有必要通过现有的数据生成。通常表面法向量可能保存于三角形级或顶点级其中的一个技巧就是平均相邻三角形的表面法向量并将结果标准化。当然这要求知道三角形的法向量。一般可以这样假设三角形的顶点按逆时针排列通过叉积就可以计算外表面的法向量。当然在有些情况下顶点的顺序是未知且比较混乱的这样就比较麻烦了。这个笔者也没有仔细深入研究推荐读者看一下《计算非固定结构序列的多边形的顶点法线》这篇论文。通过平均三角形法向量求得顶点法向量是一种经验性的方法不具有通用性。虽然在很多情况下都可以正确地工作但有些情况下还是无法正常使用的在使用布告板时一定要注意这点这时生成顶点法线的方法就会失效。因为布告板由两个三角形背靠构造而成它的两个法向量是相反的其平均值为 0所以不能初始化这时只能采用别的方法来处理如“双面”三角形等。 5.8 生成顶点法向量示例 生成顶点法向量(osgUtil::SmoothingVisitor)示例的代码如程序清单4-8所示 osg::ref_ptrosg::Geometry createQuad2() // 创建一个四边形节点 { // 创建一个几何体对象 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);//设置颜色的绑定方式为单个顶点 geom-addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));//添加图元绘图基元为四边形 return geom.get(); } void smoothingVisitor_4_8() { // 创建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(); // 创建一个几何体对象,注意几何体并没有指定法线 // 如果你注释下面生成顶点法线的代码你就可以看到 // 光照的差别 osg::ref_ptrosg::Geometry geometry createQuad2(); // 生成顶点法线 osgUtil::SmoothingVisitor::smooth(*(geometry.get())); // 添加到叶节点 osg::ref_ptrosg::Geode geode new osg::Geode(); geode-addDrawable(geometry.get()); root-addChild(geode.get());// 添加到场景 //优化场景数据 osgUtil::Optimizer optimizer; optimizer.optimize(root.get()); viewer-setSceneData(root.get()); viewer-realize(); viewer-run(); } 运行程序截图如图4-21所示 图4-21生成顶点法向量示例截图