空间变换中最重要的是坐标系和矩阵运算了。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) ) ;
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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的作用是,在场景中某时刻,它的某些子节点被隐藏和忽略,而列外一些节点正常显示并完成相应功能。示例中利用开关节点的更新回调实现子节点的切换:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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节点,其基本实现功能是在不影响渲染效果的条件下 ,根据场景对象与观察者的距离,从多个预置方案中选择一种合适的来表达要渲染的物体,从而减轻系统的负担,模型越靠近观察者越精细,而远处只需要较少的多边形类表达。示例如下:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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代理节点是一种用于动态加载其他模型节点的节点类型。这些节点不会立即被解析和加入场景,而是在场景运行过程中逐步载入。示例:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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 }