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 示例:
代码
1 #include <osg/Node>
2 #include <osg/AutoTransform>
3 #include <osg/MatrixTransform>
4 #include <osg/PositionAttitudeTransform>
5 #include <osgDB/ReadFile>
6 #include <osgViewer/Viewer>
7
8 //library for OSG
9 #pragma comment(lib, "osgd.lib")
10 #pragma comment(lib, "osgDBd.lib")
11 #pragma comment(lib, "osgViewerd.lib")
12
13 osg::Transform* createAutoTransform(double posX, osg::Node* node)
14 {
15 osg::ref_ptr<osg::AutoTransform> at = new osg::AutoTransform();
16 at->setAutoRotateMode( osg::AutoTransform::ROTATE_TO_SCREEN );
17 at->setPosition( osg::Vec3(posX, 0, 0) );
18 at->addChild( node );
19
20 return at.release();
21 }
22
23 osg::Transform* createMatrixTransform(double posX, double rotateZ, osg::Node* node)
24 {
25 osg::ref_ptr<osg::MatrixTransform> mt = new osg::MatrixTransform();
26 mt->setMatrix( osg::Matrix::rotate( rotateZ, osg::Z_AXIS) *
27 osg::Matrix::translate( posX, 0, 0) );
28 mt->addChild( node );
29
30 return mt.release();
31 }
32
33 osg::Transform* createPositionAttitudeTransform(double posX, double rotateZ, osg::Node* node)
34 {
35 osg::ref_ptr<osg::PositionAttitudeTransform> pat = new osg::PositionAttitudeTransform();
36 pat->setAttitude( osg::Quat(rotateZ, osg::Z_AXIS) );
37 pat->setPosition( osg::Vec3(posX, 0, 0) );
38 pat->addChild(node);
39
40 return pat.release();
41 }
42
43 int main(int argc, char** argv)
44 {
45 osg::ArgumentParser argument(&argc, argv);
46 osg::ref_ptr<osg::Node> node = osgDB::readNodeFiles(argument);
47 if( !node.get() ) node = osgDB::readNodeFile( "cow.osg" );
48
49 osg::ref_ptr<osg::Group> root = new osg::Group();
50 root->addChild( createAutoTransform(0.0, node) );
51 root->addChild( createMatrixTransform(-15.0, osg::PI_4, node) );
52 root->addChild( createPositionAttitudeTransform(15.0, -osg::PI_4, node) );
53
54 osgViewer::Viewer viewer;
55 viewer.setSceneData( root.get() );
56 return viewer.run();
57 }
二. 开关节点(osg::Switch)
开关节点Switch的作用是,在场景中某时刻,它的某些子节点被隐藏和忽略,而列外一些节点正常显示并完成相应功能。示例中利用开关节点的更新回调实现子节点的切换:
代码
1 #include <osg/Switch>
2 #include <osg/NodeCallback>
3 #include <osgDB/ReadFile>
4 #include <osgDB/WriteFile>
5 #include <osgViewer/Viewer>
6
7 #pragma comment(lib, "osgd.lib")
8 #pragma comment(lib, "osgDBd.lib")
9 #pragma comment(lib, "osgViewerd.lib")
10
11 class CessnaCallback: public osg::NodeCallback
12 {
13 public:
14 virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
15 {
16 osg::Switch* sw = dynamic_cast<osg::Switch*>(node);
17 if(sw && nv)
18 {
19 const osg::FrameStamp* fs = nv->getFrameStamp();
20 if( fs )
21 {
22 if( _fireStartFrame < fs->getFrameNumber() )
23 {
24 sw->setValue(0, false);
25 sw->setValue(1, true);
26 }
27 }
28 }
29 traverse(node, nv);
30 }
31
32 private:
33 static const int _fireStartFrame = 900;
34 };
35
36 int main(int argc, char** argv)
37 {
38 osg::ref_ptr<osg::Switch> root = new osg::Switch();
39 root->addChild( osgDB::readNodeFile("cessna.osg"), true );
40 root->addChild( osgDB::readNodeFile("cessnafire.osg"), false );
41 root->addUpdateCallback(new CessnaCallback() );
42
43 osgDB::writeNodeFile( *(root.get()), "Switch.osg" );
44 osgViewer::Viewer viewer;
45 viewer.setSceneData( root.get() );
46 return viewer.run();
47 }
三. 细节层次节点(osg::LOD)
LOD节点,其基本实现功能是在不影响渲染效果的条件下 ,根据场景对象与观察者的距离,从多个预置方案中选择一种合适的来表达要渲染的物体,从而减轻系统的负担,模型越靠近观察者越精细,而远处只需要较少的多边形类表达。示例如下:
代码
1 #include <osg/LOD>
2 #include <osgDB/ReadFile>
3 #include <osgViewer/Viewer>
4 #include <osg/Notify>
5
6 #pragma comment(lib, "osgd.lib")
7 #pragma comment(lib, "osgDBd.lib")
8 #pragma comment(lib, "osgViewerd.lib")
9
10 int main(int argc, char** argv)
11 {
12 osg::ref_ptr<osg::Node> node = osgDB::readNodeFile( "bunny-high.ive" );
13
14 if(!node)
15 {
16 osg::notify(osg::NotifySeverity::ALWAYS) << "Cannot open the model bunny!\n" ;
17 return 0;
18 }
19
20 float r = node->getBound().radius();
21
22 osg::ref_ptr<osg::LOD> lod = new osg::LOD();
23 lod->addChild(node.get(), 0.0f, r*3 );
24 lod->addChild( osgDB::readNodeFile("bunny-mid.ive"), r*3, r*7 );
25 lod->addChild( osgDB::readNodeFile("bunny-low.ive"), r*7, FLT_MAX );
26
27 osgViewer::Viewer viewer;
28 viewer.setSceneData( lod.get() );
29 viewer.run();
30 }
四. 代理节点(ProxyNode)
ProxyNode代理节点是一种用于动态加载其他模型节点的节点类型。这些节点不会立即被解析和加入场景,而是在场景运行过程中逐步载入。示例:
代码
1 #include <osg/ProxyNode>
2 #include <osgDB/ReadFile>
3 #include <osg/ArgumentParser>
4 #include <osgViewer/Viewer>
5 #include <osg/Notify>
6 #include <iostream>
7
8 #pragma comment(lib, "osgd.lib")
9 #pragma comment(lib, "osgDBd.lib")
10 #pragma comment(lib, "osgViewerd.lib")
11
12
13 int main(int argc, char** argv)
14 {
15 osg::ArgumentParser argument(&argc, argv);
16 osg::ref_ptr<osg::ProxyNode> pn = new osg::ProxyNode();
17 unsigned int num = 0;
18
19 for(int i = 1; i < argument.argc(); i++)
20 {
21 if( argument.isString(i) )
22 {
23 std::cout << num << ": " << argument[i] << "\n" ;
24 pn->setFileName( num++, argument[i] );
25 }
26 }
27 if( !pn->getNumFileNames() )
28 pn->setFileName(0, "cow.osg" );
29
30 osgViewer::Viewer viewer;
31 viewer.setSceneData( pn.get() );
32 return viewer.run();
33 }