场景都是由基本的绘图基元构成的,基本的绘图基元构成简单的几何体,简单的几何体构成复杂的几何体,复杂的几何体最终构成复杂的场景。当多个几何体组合时,可能存在多种降低场景渲染效率的原因。在很多3D引擎中,都提供了对场景的几何体进行修改的操作,以达到最优渲染效率。虽然最优渲染效率只是一个理想状态,但一定的几何体操作在相当程度上可以提高渲染效率。
在OSG中,为了获得所需的性能和渲染的效率,osgUtil库提供了一些通用的几何体运算,这些几何体运算主要包括:osgUtil::Simplifier(简化)、ostUtil::SmoothingVisitor(生成法线)、osgUtil::DelaunayTriangulator(生成Delaunay三角网工具)和osgUtil::TriStripVisitor(条带化)等。
1、简化几何体
简化几何体(osgUtil::Simplifier)类继承自osg::NodeVisitor类,它采用访问器的方式遍历几何体并对其进行简化处理。
osgUtil::Simplfier类对几何体的简化主要需要设置两个方面的参数,即当几何体样本比率小于1时,设置点的误差限制;当几何体的样本比率大于1时,设置边的长度限制。通过对点的误差或者边的长度的限制简化不必要的点和边,然后通过平滑处理和条带化渲染几何体,进而达到提高渲染效率的目的,也可以用于自动生成低层次模型。一般情况下,样本比率越大,简化越少;样本比率越小,简化越多。优化可以直接调用下面的函数:
virtual void apply(osg::Geode &geode); // 用于叶节点
void simplify(osg::Geometry &geometry); // 简化几何体
这两个函数同样可用于osg::Node节点,不过,关联实例时需要使用accept()方法。
osgUtil::Simplifier简化采用的是边塌陷算法。
示例代码:
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include osg/PositionAttitudeTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/StateSetManipulator>
#include <osgUtil/Optimizer>
#include <osgUtil/Simplifer>
int main()
[
// 创建Viewer对象,场景浏览器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
// 切换网格模式,方便比较模型
viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
osg::ref_ptr<osg::Group> root = new osg::Group();
// 设置样本比率,样本比率越大,简化越小,反之,简化越多
float sampleRatio = 0.3f;
// 设置点的最大误差
float maxError = 4.0f;
// 创建简化对象
osg::ref_ptr<osg::Node> node1 = osgDB::readNodeFile("cow.org");
// 深拷贝牛的模型到node2节点
osg::ref_ptr<osg::Node> node2 = (osg::Node*)(node1->clone(osg::CopyOp::DEEP_COPY_ALL));
// 创建一个位置变换节点
osg::ref_ptr<osg::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(node2.get());
// 优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
viewer->ruN();
return 0;
}
2、Delaunay三角网绘制
TIN模型
在数字地形建模中,不规则三角网(TIN)通过从不规则离散分布的数据点生成的连续三角面来逼近地形表面。TIN模型的优点是它能以不同层次的分辨率来描述地形表面。与网格数据模型相比,TIN模型在某一特定分辨率下能用更少的空间和时间更精确地表示更加复杂的表面。特别是当地形包含有大量特征,如断裂面、构造线时,TIN模型能更好地顾及这些特征,从而能更精确合理地表达地表形态。
对于TIN模型,其基本要求有以下3点:
(1)TIN是唯一的;
(2)力求最佳三角形几何形状;
(3)保证最近邻的点构成三角形
在所有可能的三角网中,狄洛尼(Delaunay)三角网在地形拟合方面表现最为出色,常用于TIN的生成。当不相交的断裂线等被作为预先定义的限制条件作用于TIN的生成当中时,就必须考虑带约束条件的狄洛尼三角网。
osgUtil::DelaunayTriangulator类
osgUtil::DelaunayTriangulator类直接继承自osg::Referenced类。
创建狄洛尼三角网有如下三个步骤:
(1)创建顶点数组;
(2)创建一个osgUtiil::DelaunayTriangulator对象,并初始化顶点数组,同时生成三角网,程序代码如下:
bool triangulate() // 开始生成三角网格
(3)创建一个几何体对象,把osgUtil::DelaunayTriangulator类对象生成的绘制图元加入到几何体中。在生成狄洛尼三角网时,还可以添加一些限制条件,限制条件可以是点、线或多边形,例如:
void addInputConstraint(DelaunayConstraint *dc); // 添加限制条件
Delaunay三角网绘制示例
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include osg/PositionAttitudeTransform>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgGA/StateSetManipulator>
#include <osgUtil/Optimizer>
#include <osgUtil/DelaunayTriangulator>
#include "Tex.h"
int main()
[
// 创建Viewer对象,场景浏览器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
// 切换网格模式,方便比较模型
viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
osg::ref_ptr<osg::Group> root = new osg::Group();
// 创建顶点数组
osg::ref_ptr<osg::Vec3Array> coords = new osg::Vec3Array();
// 计算顶点数组的大小
unsigned int a = sizeof(vertex) / sizeof(float[3]);
// 添加顶点数据
for(unsigned int i = 0; i <a;++i)
{
coords->push_back(osg::Vec3(vertex[i][0],vertex[i][1],vertex[i][2]));
}
// 创建Delaunay三角网对象
osg::ref_ptr<osgUtil::DelaunayTriangulator> dt = new osgUtil::DelaunayTriangulator(coords.get());
dt->triangulate();
// 创建几何体
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
geometry->setVertexArray(coords.get());
geometry->addPrimitiveSet(dt->getTriangles());
osg::ref_ptr<osg::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();
return 0;
}
3、三角带绘制
三角带绘制(osgTUtil::TriStripVisitor)类继承自osgUtil::BaseOptimizerVIsitor类,它采用访问器的机制遍历场景中的几何体,实现三角带绘制,以提高渲染效率。
下面对osgUtil::TriStrioVisitor的两个常用成员函数予以说明:
void stripify(osg::Geometry &drawable);// 条带化几何体
virtual void apply(osg::Geode &geode); // 应用于叶节点
关联叶节点实例时需要调用accept()方法。大多数图像卡并不直接支持索引三角网。在渲染三角形时,一般是将3个顶点同时提交,这样,共享的顶点会多次提交,三角形用到一次就提交一次。
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
geometry->setVertexArray(v.get());
geometry->setTexCoordArray(vt.get());
geometry->setColorArray(vc.get());
...
osgUtil::TriStripVisitor stripper;
stripper.stripify(*(geometry.get()));
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
geode->addDrawable(geometry.get());
...
4、生成顶点法向量
生成顶点法向量(osgUtil::SmoothingVisitor)类继承自osg::NodeVisitor类,它采用访问器机制,遍历场景中的几何体,生成顶点法向量。
下面对osgUtil::SmoothingVisitor的一个常用成员函数予以说明:
static void smooth(osg::Geometry &geoset);
该函数用于生成顶点法向量,调用时需要注意,这是一个静态函数。
在很多应用程序中,网格上的各个点都需要一个表面法向量,它的用处非常广泛,例如:
> 计算光照
> 背面剔除
> 模拟粒子系统在表面的“弹跳”效果
> 通过只需要正面而加速碰撞检测。
...
osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
...
// 生成点面法向
osgUtil::SmoothingVisitor::smooth(*(geometry.get()));
osg::ref_ptr<osg::Geode> geode = new osg::Geode();
geode->addDrawable(geometry.get());
...