OpenSceneGraph几个重要功能节点练习
一. 空间变换节点
空间变换中最重要的是坐标系和矩阵运算了。OSG坐标系中使用右手系,Z轴垂直向上,X轴水平向右,Y轴垂直屏幕向里,与OpenGL和DirectX都不同。
相关缩放、旋转和平移主要由osg::Matrix, osg::Vec3, osg::Quat几个类来完成。
局部坐标系向世界坐标系转换规则是:设在局部坐标系下顶点 V 转换成世界坐标系坐标 V‘:
V‘ = V * Mn* Mn-1*……* M3* M2* M1* M0
其中M0到Mn一次为各个矩阵变换。从世界坐标系坐标下顶点 V‘ 转换成局部坐标系 V:
V = V‘ * M0-1 * M1-1 * M2-1 * M3-1 *……* Mn-1-1 * Mn-1
对于空间变换而言,无论是OpenGL,DirectX还是OSG,一般都会遵守SRT(Scale/Rotate/Translate)的运算顺序来完成符合矩阵的构建:
其公式为:
M =Ms * Mr * Mt
osg::Matrix mt = osg::Matrix::scale( osg::Vex3(sx, sy, sz) ) * osg::Matrix::rotate( osg::Quat(angle, axis) ) * osg::Matrix::translate( osg::Vec3(tx, ty, tz) ) ;
osg::MatrixTransform, osg::PositionAttitudeTransform, osg::AutoTransform 示例:
代码 #include <osg/Node> #include <osg/AutoTransform> #include <osg/MatrixTransform> #include <osg/PositionAttitudeTransform> #include <osgDB/ReadFile> #include <osgViewer/Viewer> //library for OSG #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgViewerd.lib") osg::Transform* createAutoTransform(double posX, osg::Node* node) { osg::ref_ptr<osg::AutoTransform> at = new osg::AutoTransform(); at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN ); at->setPosition( osg::Vec3(posX, 0, 0) ); at->addChild( node ); return at.release(); } osg::Transform* createMatrixTransform(double posX, double rotateZ, osg::Node* node) { osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform(); mt->setMatrix( osg::Matrix::rotate( rotateZ, osg::Z_AXIS) * osg::Matrix::translate( posX, 0, 0) ); mt->addChild( node ); return mt.release(); } osg::Transform* createPositionAttitudeTransform(double posX, double rotateZ, osg::Node* node) { osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform(); pat->setAttitude( osg::Quat(rotateZ, osg::Z_AXIS) ); pat->setPosition( osg::Vec3(posX, 0, 0) ); pat->addChild(node); return pat.release(); } int main(int argc, char** argv) { osg::ArgumentParser argument(&argc, argv); osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles(argument); if( !node.get() ) node = osgDB::readNodeFile( "cow.osg" ); osg::ref_ptr<osg::Group> root = new osg::Group(); root->addChild( createAutoTransform(0.0, node) ); root->addChild( createMatrixTransform(-15.0, osg::PI_4, node) ); root->addChild( createPositionAttitudeTransform(15.0, -osg::PI_4, node) ); osgViewer::Viewer viewer; viewer.setSceneData( root.get() ); return viewer.run(); }
二. 开关节点(osg::Switch)
开关节点Switch的作用是,在场景中某时刻,它的某些子节点被隐藏和忽略,而列外一些节点正常显示并完成相应功能。示例中利用开关节点的更新回调实现子节点的切换:
代码 #include <osg/Switch> #include <osg/NodeCallback> #include <osgDB/ReadFile> #include <osgDB/WriteFile> #include <osgViewer/Viewer> #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgViewerd.lib") class CessnaCallback: public osg::NodeCallback { public: virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) { osg::Switch* sw = dynamic_cast<osg::Switch*>(node); if(sw && nv) { const osg::FrameStamp* fs = nv->getFrameStamp(); if( fs ) { if( _fireStartFrame < fs->getFrameNumber() ) { sw->setValue(0, false); sw->setValue(1, true); } } } traverse(node, nv); } private: static const int _fireStartFrame = 900; }; int main(int argc, char** argv) { osg::ref_ptr<osg::Switch> root = new osg::Switch(); root->addChild( osgDB::readNodeFile("cessna.osg"), true ); root->addChild( osgDB::readNodeFile("cessnafire.osg"), false ); root->addUpdateCallback(new CessnaCallback() ); osgDB::writeNodeFile( *(root.get()), "Switch.osg" ); osgViewer::Viewer viewer; viewer.setSceneData( root.get() ); return viewer.run(); }
三. 细节层次节点(osg::LOD)
LOD节点,其基本实现功能是在不影响渲染效果的条件下 ,根据场景对象与观察者的距离,从多个预置方案中选择一种合适的来表达要渲染的物体,从而减轻系统的负担,模型越靠近观察者越精细,而远处只需要较少的多边形类表达。示例如下:
代码 #include <osg/LOD> #include <osgDB/ReadFile> #include <osgViewer/Viewer> #include <osg/Notify> #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgViewerd.lib") int main(int argc, char** argv) { osg::ref_ptr<osg::Node> node = osgDB::readNodeFile( "bunny-high.ive" ); if(!node) { osg::notify(osg::NotifySeverity::ALWAYS) << "Cannot open the model bunny!\n" ; return 0; } float r = node->getBound().radius(); osg::ref_ptr<osg::LOD> lod = new osg::LOD(); lod->addChild(node.get(), 0.0f, r*3 ); lod->addChild( osgDB::readNodeFile("bunny-mid.ive"), r*3, r*7 ); lod->addChild( osgDB::readNodeFile("bunny-low.ive"), r*7, FLT_MAX ); osgViewer::Viewer viewer; viewer.setSceneData( lod.get() ); viewer.run(); }
四. 代理节点(ProxyNode)
ProxyNode代理节点是一种用于动态加载其他模型节点的节点类型。这些节点不会立即被解析和加入场景,而是在场景运行过程中逐步载入。示例:
代码 #include <osg/ProxyNode> #include <osgDB/ReadFile> #include <osg/ArgumentParser> #include <osgViewer/Viewer> #include <osg/Notify> #include <iostream> #pragma comment(lib, "osgd.lib") #pragma comment(lib, "osgDBd.lib") #pragma comment(lib, "osgViewerd.lib") int main(int argc, char** argv) { osg::ArgumentParser argument(&argc, argv); osg::ref_ptr<osg::ProxyNode> pn = new osg::ProxyNode(); unsigned int num = 0; for(int i = 1; i < argument.argc(); i++) { if( argument.isString(i) ) { std::cout << num << ": " << argument[i] << "\n" ; pn->setFileName( num++, argument[i] ); } } if( !pn->getNumFileNames() ) pn->setFileName(0, "cow.osg" ); osgViewer::Viewer viewer; viewer.setSceneData( pn.get() ); return viewer.run(); }