OSG中几何体的绘制(二)

news2024/11/13 10:20:15

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 时,设置边的长度限制。通过对点的误差或者边时,的长度的限制简化不必要的点和边,然后通过平滑处理和条带化渲染几何体,进而达到提高渲染效率的目的,也可以用于自动生成低层次模型。一般来说,样本比率越大,简化越少;样本比率越小,简化越多。优化可以直接调用下面的函数:

  1. virtual void apply(osg::Gcode &geode)//应用于叶节点  
  2. void simplify(osg::Geometry &geometry)/简化儿何体  

        这两个函数同样可用于osg::Node节点,不过,关联实例时需要使用accept()方法osgUtil::Simplifer 简化采用的是边塌陷算法,对于这个算法笔者并没有深入研究。记得在邮件列表中也提到了 Robert,关于这个算法的文档已经丢失。目前,网上很少有关于这方面的文档。至于算法是如何实现的,有兴趣的朋友可以进行源码研究。如果读者研究出了结果,欢迎到OSG中国官方讨论区(http://bbs.OsgChina.org)发帖。

5.2 简化几何体示例

        简化几何体(osgUtil::Simplifier)示例的代码如程序清单4-5所示。

 

  1. void simplifier_4_5(const string &strDataFolder)  
  2. {  
  3.     // 创建Viewer对象,场景浏览器  
  4.     osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
  5.     osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
  6.     traits->x = 40;  
  7.     traits->y = 40;  
  8.     traits->width = 600;  
  9.     traits->height = 480;  
  10.     traits->windowDecoration = true;  
  11.     traits->doubleBuffer = true;  
  12.     traits->sharedContext = 0;  
  13.   
  14.     osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
  15.     osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
  16.     camera->setGraphicsContext(gc.get());  
  17.     camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
  18.     GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
  19.     camera->setDrawBuffer(buffer);  
  20.     camera->setReadBuffer(buffer);  
  21.     viewer->addSlave(camera.get());  
  22.   
  23.     // 切换网格模式,方便比较模型的区别  
  24.     viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));  
  25.   
  26.     osg::ref_ptr<osg::Group> root = new osg::Group();  
  27.   
  28.     // 设置样本比率,样本比率越大,简化越少  
  29.     float sampleRatio = 0.3f;// 样本比率越小,简化越多   
  30.     float maxError = 4.0f;  // 设置点的最大误差  
  31.   
  32.     // 创建简化对象  
  33.     osgUtil::Simplifier simplifier(sampleRatio, maxError);  
  34.   
  35.     // 读取牛的模型  
  36.     string strDataPath = strDataFolder + "cow.osg";  
  37.     osg::ref_ptr<osg::Node> node1 = osgDB::readNodeFile(strDataPath);  
  38.   
  39.     // 深拷贝牛的模型到node2节点  
  40.     osg::ref_ptr<osg::Node> node2 = (osg::Node*)(node1->clone(osg::CopyOp::DEEP_COPY_ALL));  
  41.   
  42.     // 创建一个位置变换节点  
  43.     osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();    
  44.     pat->setPosition(osg::Vec3(10.0f, 0.0f, 0.0f));// 设置位置  
  45.     pat->addChild(node2.get());// 添加子节点    
  46.     pat->accept(simplifier); // 简化处理  
  47.   
  48.     // 添加到场景  
  49.     root->addChild(node1.get());  
  50.     root->addChild(pat.get());  
  51.   
  52.     // 优化场景数据  
  53.     osgUtil::Optimizer optimizer;  
  54.     optimizer.optimize(root.get());  
  55.   
  56.     viewer->setSceneData(root.get());  
  57.     viewer->realize();  
  58.     viewer->run();  
  59. }  

        运行程序,截图如图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对象,并初始化顶点数组,同时生成三角网,程序代码如下:

  1. bool triangulate()// 开始生成三角网格  

        (3) 创建一个几何体对象,把osgUtil::DelaunayTriangulator 类对象生成的绘制图元加入到几何体中。在生成狄洛尼三角网时,读者还可以添加一些限制条件,限制条件可以是点、线或多边形,例如:

        void addInputConstraint(DelaunayConstraint*dc)//添加限制条件 

        通过这么多的讲解,读者应该清楚了狄洛尼三角网的绘制方法了吧。有兴趣的读者可以研究关于狄洛尼三角网的算法。

5.4 Delaunay三角网绘制示例

        Delaunay三角网绘制(osgUtil::DelaunayTriangulator)示例的代码如程序清单46所示。

 

  1. void delaunay_4_6()  
  2. {  
  3.     osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
  4.     osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
  5.     traits->x = 40;  
  6.     traits->y = 40;  
  7.     traits->width = 600;  
  8.     traits->height = 480;  
  9.     traits->windowDecoration = true;  
  10.     traits->doubleBuffer = true;  
  11.     traits->sharedContext = 0;  
  12.   
  13.     osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
  14.     osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
  15.     camera->setGraphicsContext(gc.get());  
  16.     camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
  17.     GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
  18.     camera->setDrawBuffer(buffer);  
  19.     camera->setReadBuffer(buffer);  
  20.     viewer->addSlave(camera.get());  
  21.   
  22.     // 方便查看在多边形之间切换,以查看三角网  
  23.     viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));  
  24.   
  25.     osg::ref_ptr<osg::Group> root = new osg::Group();  
  26.   
  27.     // 创建顶点数组  
  28.     osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array();  
  29.   
  30.     // 计算顶点数组的大小  
  31.     unsigned int n = sizeof(vertex) / sizeof(float[3]);  
  32.   
  33.     // 添加顶点数据  
  34.     for (unsigned int i = 0; i < n; i++)  
  35.     {  
  36.         coords->push_back(osg::Vec3(vertex[i][0], vertex[i][1], vertex[i][2]));  
  37.     }  
  38.   
  39.     // 创建Delaunay三角网对象  
  40.     osg::ref_ptr<osgUtil::DelaunayTriangulator> dt = new osgUtil::DelaunayTriangulator(coords.get());  
  41.     dt->triangulate();// 生成三角网  
  42.   
  43.     // 创建几何体  
  44.     osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();     
  45.     geometry->setVertexArray(coords.get());  // 设置顶点数组  
  46.     geometry->addPrimitiveSet(dt->getTriangles());// 加入到绘图基元  
  47.   
  48.     // 添加到叶节点  
  49.     osg::ref_ptr<osg::Geode> geode = new osg::Geode();  
  50.     geode->addDrawable(geometry.get());  
  51.     root->addChild(geode.get());  
  52.   
  53.     // 优化场景数据  
  54.     osgUtil::Optimizer optimizer;  
  55.     optimizer.optimize(root.get());  
  56.   
  57.     viewer->setSceneData(root.get());  
  58.     viewer->realize();  
  59.     viewer->run();  

}  

 

运行程序,截图如图4-17所示

图4-17 Delaunay 三角网绘制示例截图

 5.5 三角带绘制

        三角带绘制(osgUtil::TriStripVisitor)类继承自osgUtil::BaseOptimizerVisitor 类,它采用访问器的机制遍历场景中的几何体,实现三角带绘制,以提高染效率。osgUtil::TriStripVisitor 的继承关系图如图4-18所示。

图4-18 osgUtil::TriStripVisitor 的继承关系图

        下面对osgUtil::TriStripVisitor 的两个常用成员函数予以说明。

  1. void stripify(osg::Geometry &drawable)//条带化几何体  
  2. virtual void apply(osg::Geode &geode)//应用于叶节点  

        从上面可以看出,它同样可应用于叶节点。值得注意的是,关联叶节点实例时需要调用 accept()方法。

        大多数图形卡并不直接支持索引三角网。在渲染三角形时,一般是将3个顶点同时提交,这样共享的顶点会多次提交,三角形用到一次就提交一次。因为内存和图形硬件间的数据传输是瓶颈,所以很多API和硬件支持特殊的三角网格式以减少传输量。基本思想是排序点和面,使显卡中已有的三角形不需要再次传输。

        这里,我们只讨论三角带,三角带是一个三角形列表,其中每个三角形都与前一个三角形共享一边。在一种最理想的状态下,三角带可以用 N+2个顶点表示存个面。N很大时,每个三角形平均发送一个顶点。在实践中,很多情况下,很多网格是一个三角带无法表达的,此时就需要多个三角带来联合绘制。因为我们希望最小化发往图形卡的顶点数,所以,三角带的数目应该尽可能得少,即三角带越长越好。从另一个方面来说,分别渲染两个长度为N的三角带所需要的时间比渲染一个长度为2N 的三角带要长,即使2N 的三角带中三角形的个数多于两个分开的三角带中的三角形个数的和,因为建立各三角带需要额外的处理时间。

        学习了上面的基础知识,读者再去看关于这部分的源代码就会感觉非常易懂了,这些更多是原理的问题。

5.6 三角带绘制示例

        三角带绘制(osgUtil::TriStripVisitor)示例的代码如程序清单4-7所示。

  1. osg::ref_ptr<osg::Geometry> createQuad1() // 创建一个四边形节点  
  2. {  
  3.     // 创建一个几何体对象  
  4.     osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();  
  5.   
  6.     // 创建顶点数组,注意顶点的添加顺序是逆时针的  
  7.     osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();      
  8.     v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));// 添加数据  
  9.     v->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));  
  10.     v->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));  
  11.     v->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));     
  12.     geom->setVertexArray(v.get());// 设置顶点数据  
  13.   
  14.     // 创建纹理坐标  
  15.     osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();     
  16.     vt->push_back(osg::Vec2(0.0f, 0.0f));// 添加数据  
  17.     vt->push_back(osg::Vec2(1.0f, 0.0f));  
  18.     vt->push_back(osg::Vec2(1.0f, 1.0f));  
  19.     vt->push_back(osg::Vec2(0.0f, 1.0f));  
  20.     geom->setTexCoordArray(0, vt.get());// 设置纹理坐标  
  21.   
  22.     // 创建颜色数组  
  23.     osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();     
  24.     vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));//添加数据  
  25.     vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));  
  26.     vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));  
  27.     vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));  
  28.       
  29.     geom->setColorArray(vc.get());//设置颜色数组     
  30.     geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);//设置颜色的绑定方式为单个顶点  
  31.   
  32.     // 创建法线数组  
  33.     osg::ref_ptr<osg::Vec3Array> nc = new osg::Vec3Array();     
  34.     nc->push_back(osg::Vec3(0.0f, -1.0f, 0.0f));//添加法线     
  35.     geom->setNormalArray(nc.get());//设置法线数组    
  36.     geom->setNormalBinding(osg::Geometry::BIND_OVERALL);//设置法线的绑定方式为全部顶点   
  37.     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));// 添加图元,绘图基元为四边形  
  38.   
  39.     return geom.get();  
  40. }  
  41.   
  42. void TriStripVisitor_4_7()  
  43. {  
  44.     // 创建Viewer对象,场景浏览器  
  45.     osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
  46.     osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
  47.     traits->x = 40;  
  48.     traits->y = 40;  
  49.     traits->width = 600;  
  50.     traits->height = 480;  
  51.     traits->windowDecoration = true;  
  52.     traits->doubleBuffer = true;  
  53.     traits->sharedContext = 0;  
  54.   
  55.     osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
  56.     osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
  57.     camera->setGraphicsContext(gc.get());  
  58.     camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
  59.     GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
  60.     camera->setDrawBuffer(buffer);  
  61.     camera->setReadBuffer(buffer);  
  62.     viewer->addSlave(camera.get());  
  63.   
  64.     // 方便查看在多边形之间切换,以查看三角网  
  65.     viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));  
  66.   
  67.     osg::ref_ptr<osg::Group> root = new osg::Group();  
  68.   
  69.     // 创建一个几何体对象  
  70.     osg::ref_ptr<osg::Geometry> geometry = createQuad1();  
  71.   
  72.     // 对几何体进行条带化  
  73.     osgUtil::TriStripVisitor stripper;  
  74.     stripper.stripify(*(geometry.get()));  
  75.   
  76.     // 添加到叶节点  
  77.     osg::ref_ptr<osg::Geode> geode = new osg::Geode();  
  78.     geode->addDrawable(geometry.get());  
  79.     root->addChild(geode.get());// 添加到场景  
  80.   
  81.     // 优化场景数据  
  82.     osgUtil::Optimizer optimizer;  
  83.     optimizer.optimize(root.get());  
  84.   
  85.     viewer->setSceneData(root.get());  
  86.     viewer->realize();  
  87.     viewer->run();  
  88. }  

        运行程序,截图如图4-19所示。

图4-19 三角带绘制示例截图

5.7 生成顶点法向量

        生成顶点法向量(osgUtil::SmoothingVisitor)类继承自osg::NodeVisitor 类,它采用访问器机制,遍历场景中的几何体,生成顶点法向量。它的继承关系图如图4-20所示

图4-20 osgUtil::SmoothingVisitor的继承关系图

        下面对osgUtil::SmoothingVisitor的一个常用成员函数予以说明

  1. static void smooth(osg::Geometry &geo);  

        该函数用于生成顶点法向量,调用时需要注意,这是一个静态函数。

        在很多应用程序中,网格上的各点都需要一个表面法向量,它的用处非常广泛,例如:

  •  计算光照。
  • 背面剔除。
  • 模拟粒子系统在表面的“弹跳”效果。
  • 通过只需要正面而加速碰撞检测。

        通常我们在绘制几何体时都会指定法线。当得到一个模型,它本身并没有法向量时,则有必要通过现有的数据生成。通常,表面法向量可能保存于三角形级或顶点级,其中的一个技巧就是平均相邻三角形的表面法向量,并将结果标准化。当然,这要求知道三角形的法向量。一般可以这样假设,三角形的顶点按逆时针排列,通过叉积就可以计算外表面的法向量。当然,在有些情况下,顶点的顺序是未知且比较混乱的,这样就比较麻烦了。这个笔者也没有仔细深入研究,推荐读者看一下《计算非固定结构序列的多边形的顶点法线》这篇论文。通过平均三角形法向量求得顶点法向量是一种经验性的方法,不具有通用性。虽然在很多情况下都可以正确地工作,但有些情况下还是无法正常使用的,在使用布告板时,一定要注意这点,这时生成顶点法线的方法就会失效。因为布告板由两个三角形背靠构造而成,它的两个法向量是相反的,其平均值为 0,所以不能初始化,这时只能采用别的方法来处理,如“双面”三角形等。

 5.8 生成顶点法向量示例

        生成顶点法向量(osgUtil::SmoothingVisitor)示例的代码如程序清单4-8所示

 

  1. osg::ref_ptr<osg::Geometry> createQuad2() // 创建一个四边形节点  
  2. {  
  3.     // 创建一个几何体对象  
  4.     osg::ref_ptr<osg::Geometry> geom = new osg::Geometry();  
  5.   
  6.     // 创建顶点数组,注意顶点的添加顺序是逆时针的  
  7.     osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array();      
  8.     v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));//添加数据  
  9.     v->push_back(osg::Vec3(1.0f, 0.0f, 0.0f));  
  10.     v->push_back(osg::Vec3(1.0f, 0.0f, 1.0f));  
  11.     v->push_back(osg::Vec3(0.0f, 0.0f, 1.0f));     
  12.     geom->setVertexArray(v.get());//设置顶点数据  
  13.   
  14.     // 创建纹理坐标  
  15.     osg::ref_ptr<osg::Vec2Array> vt = new osg::Vec2Array();     
  16.     vt->push_back(osg::Vec2(0.0f, 0.0f));//添加数据  
  17.     vt->push_back(osg::Vec2(1.0f, 0.0f));  
  18.     vt->push_back(osg::Vec2(1.0f, 1.0f));  
  19.     vt->push_back(osg::Vec2(0.0f, 1.0f));  
  20.     geom->setTexCoordArray(0, vt.get());// 设置纹理坐标  
  21.   
  22.     // 创建颜色数组  
  23.     osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array();     
  24.     vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));//添加数据  
  25.     vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));  
  26.     vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));  
  27.     vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));  
  28.   
  29.     // 设置颜色数组  
  30.     geom->setColorArray(vc.get());  
  31.     geom->setColorBinding(osg::Geometry::BIND_PER_VERTEX);//设置颜色的绑定方式为单个顶点  
  32.     geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS, 0, 4));//添加图元,绘图基元为四边形  
  33.   
  34.     return geom.get();  
  35. }  
  36.   
  37. void smoothingVisitor_4_8()  
  38. {  
  39.     // 创建Viewer对象,场景浏览器  
  40.     osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();  
  41.     osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;  
  42.     traits->x = 40;  
  43.     traits->y = 40;  
  44.     traits->width = 600;  
  45.     traits->height = 480;  
  46.     traits->windowDecoration = true;  
  47.     traits->doubleBuffer = true;  
  48.     traits->sharedContext = 0;  
  49.   
  50.     osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());  
  51.     osg::ref_ptr<osg::Camera> camera = new osg::Camera;  
  52.     camera->setGraphicsContext(gc.get());  
  53.     camera->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));  
  54.     GLenum buffer = traits->doubleBuffer ? GL_BACK : GL_FRONT;  
  55.     camera->setDrawBuffer(buffer);  
  56.     camera->setReadBuffer(buffer);  
  57.     viewer->addSlave(camera.get());  
  58.   
  59.   
  60.     osg::ref_ptr<osg::Group> root = new osg::Group();  
  61.   
  62.     // 创建一个几何体对象,注意,几何体并没有指定法线  
  63.     // 如果你注释下面生成顶点法线的代码,你就可以看到  
  64.     // 光照的差别  
  65.     osg::ref_ptr<osg::Geometry> geometry = createQuad2();  
  66.   
  67.     // 生成顶点法线  
  68.     osgUtil::SmoothingVisitor::smooth(*(geometry.get()));  
  69.   
  70.     // 添加到叶节点  
  71.     osg::ref_ptr<osg::Geode> geode = new osg::Geode();  
  72.     geode->addDrawable(geometry.get());  
  73.     root->addChild(geode.get());// 添加到场景  
  74.   
  75.     //优化场景数据  
  76.     osgUtil::Optimizer optimizer;  
  77.     optimizer.optimize(root.get());  
  78.   
  79.     viewer->setSceneData(root.get());  
  80.     viewer->realize();  
  81.     viewer->run();  
  82. }  

        运行程序,截图如图4-21所示

 

图4-21生成顶点法向量示例截图

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于linux系统的Tomcat+Mysql+Jdk环境搭建(二)jdk1.8 linux 上传到MobaXterm 工具的已有session里

【JDK安装】 1.首先下载一个JDK版本 官网地址&#xff1a;http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载1.8版本&#xff0c;用红框标注出来了&#xff1a; 也许有的同学看到没有1.8版本&#xff0c;你可以随便下载一个linux的…

最强Pose模型RTMO开源 | 基于YOLO架构再设计,9MB+9ms性能完爆YOLO-Pose

实时多人在图像中的姿态估计面临着在速度和精度之间实现平衡的重大挑战。尽管两阶段的上下文方法在图像中人数增加时会减慢速度&#xff0c;但现有的单阶段方法往往无法同时实现高精度和实时性能。 本文介绍了RTMO&#xff0c;这是一个单阶段姿态估计框架&#xff0c;通过在YOL…

【十】我们都是面向spring注解开发的

spring 注解驱动开发 简介&#xff1a; 前面有写过spring框架的演进历程&#xff0c;得出一个结论就是我们都是面向spring编程的&#xff0c;这里讲另一个结论&#xff1a;我们都是面向spring注解开发的。spring框架的核心设计理念是简化java开发&#xff0c;而这一路的发展越…

超分辨率重建——SAN训练自己数据集及推理测试(详细图文教程)

目录 一、源码包下载二、数据集准备三、预训练权重文件四、训练环境五、训练5.1 超参数修改5.2 训练模型5.2.1 命令方式训练5.2.2 Configuration配置参数方式训练 5.3 模型保存 六、推理测试6.1 超参数修改6.2 测试6.2.1 命令方式测试6.2.2 Configuration配置参数方式测试 6.3 …

TCP报文头(首部)详解

本篇文章基于 RFC 9293: Transmission Control Protocol (TCP) 对TCP报头进行讲解&#xff0c;部分内容会与旧版本有些许区别。 TCP协议传输的数据单元是报文段&#xff0c;一个报文段由TCP首部&#xff08;报文头&#xff09;和TCP数据两部分组成&#xff0c;其中TCP首部尤其重…

node.js mongoose简述

目录 官方文档 mongoose Schema Model Query document 关系 官方文档 Mongoose v8.0.3: Getting Started mongoose Mongoose 是一个 Node.js 环境下 MongoDB 的对象建模工具。它提供了一种在应用程序中与 MongoDB 数据库进行交互的方式&#xff0c;使得开发者能够使用…

怎么使用会声会影?2024年最新使用会声会影的具体步骤

一听说视频剪辑我们就不由得联想到电影、电视等一些高端的视频剪辑技术&#xff0c;大家都觉得视频剪辑是一个非常复杂而且需要很昂贵的设备才可以完成的技术活&#xff0c;这对很多“门外汉”来说都可望而不可及。实际上&#xff0c;使用会声会影剪辑视频不仅是很多人都可以操…

哈希扩展:位图与布隆过滤器

目录 1. 位图1.1 位图引入1.2 位图概念1.3 位图的模拟实现1.4 位图相关问题1.5 位图的应用 2. 布隆过滤器2.1 布隆过滤器概念2.2 模拟实现2.3 布隆过滤器相关问题2.3.1 哈希切分 1. 位图 1.1 位图引入 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&…

Java 基础学习(十二)文本I/O、日期与时间API

1 文本 I/O 1.1 字符流 1.1.1 什么是字符流 在Java中&#xff0c;字符流是指提供了基于字符的I/O能力的API。 Java 1.0中提供的基于字节的I/O流API只能支持8位字节流&#xff0c;无法妥善地处理16位Unicode字符。由于需要支持Unicode处理国际化字符&#xff0c;因此Java 1.…

网络安全——Iptables防DDoS攻击实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 一、实验目的要求&#xff1a; 1、掌握常见DDoS攻击SYN Flood的攻击…

Toyota Programming Contest 2023#8(AtCoder Beginner Contest 333)

A - Three Threes 题目大意&#xff1a;给你一个整数n&#xff0c;将这个数n输出n次。 呃呃 B - Pentagon 题目大意&#xff1a;给你一个正五边形ABCDE&#xff0c;给你任意两条边&#xff0c;判断是否相等 主要问题要判断一下内边&#xff1a;AD&#xff0c;AC&#xff0c;…

小 cookie,大作用:探索网站中的隐私追踪器(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

用Flask搭建简单的web模型部署服务

目录结构如下&#xff1a; 分类模型web部署 classification.py import os import cv2 import numpy as np import onnxruntime from flask import Flask, render_template, request, jsonifyapp Flask(__name__)onnx_session onnxruntime.InferenceSession("mobilen…

【图的应用一:最小生成树】- 用 C 语言实现普里姆算法

目录 一、最小生成树 二、普里姆算法的构造过程 三、普里姆算法的实现 一、最小生成树 假设要在 n 个城市之间建立通信联络网&#xff0c;则连通 n 个城市只需要 n - 1 条线路。这时&#xff0c;自然会考虑这样一个问题&#xff0c;如何在最节省经费的前提下建立这个通信…

云仓酒庄的品牌雷盛红酒分享红酒里加二氧化硫有害吗?

雷盛葡萄酒是广州万豪酒业有限公司旗下主力葡萄酒品牌&#xff0c;该品牌由云仓酒庄负责全国运营。雷盛&#xff08;LEESON&#xff09;品牌系列葡萄酒有幸邀请著名导演张纪中先生担任品牌代言人。采用多国家采购、多葡萄酒品种、多价位区间的全系列整体品牌形式&#xff0c;让…

谷达冠楠科技:抖音网店到底怎么做靠谱

随着互联网的发展&#xff0c;越来越多的人开始尝试在网上开设自己的店铺。而在众多的电商平台中&#xff0c;抖音网店无疑是近年来最受年轻人欢迎的一种方式。那么&#xff0c;抖音网店到底怎么做才能靠谱呢? 首先&#xff0c;我们需要明确一点&#xff0c;无论是在哪个平台上…

JS对象笔记

对象声明 对象也只是一种数据类型/字面值。写对象这个字面值有两种写法&#xff0c;一种是普通的对象&#xff0c;这种对象用new 构造函数&#xff08;&#xff09;&#xff0c;另一种是JS内特有的json对象。这个对象是直接{}就代表对象。且也是在堆内。 对象的构成 无论是上…

Pytorch当中的.detach()操作是什么意思

.detach() 是 PyTorch 中用于从计算图中分离张量的方法。当我们在PyTorch中进行张量运算时&#xff0c;操作会构建一个计算图来跟踪计算历史&#xff0c;这个计算图用于自动求导和反向传播来计算梯度。 使用.detach()方法可以将一个张量从当前的计算图中分离出来&#xff0c;使…

System作为系统进程陔如何关闭?

一、简介 system进程是不可以关闭的&#xff0c;它是用来运行一些系统命令的&#xff0c;比如reboot、shutdown等&#xff0c;以及用来运行一些后台程序&#xff0c;比如ntfs-3g、v4l2loopback等。system进程也被用于运行一些内核模块&#xff0c;比如nvidia、atd等。system进程…

太阳能电池特性测试用太阳光模拟器24H光源

概述 太阳能模拟器是一种在室内模拟太阳光的设备&#xff0c;能够较为准确地模拟太阳辐射的准直性、均匀性和光谱特性。它的基本原理是利用人工光源模拟太阳光辐射&#xff0c;以克服太阳光辐射受环境、时间和气候等因素影响&#xff0c;并且总辐照度不能调节等缺点&#xff0c…