首页 > 其他分享 >OSG 使用整理(3):自定义漫游器动画

OSG 使用整理(3):自定义漫游器动画

时间:2023-05-02 13:11:27浏览次数:51  
标签:动画 const 自定义 ea osgGA OSG return 坐标系 osg

自定义漫游器动画

1 相机视图矩阵

1.1   坐标系统

 

(1)局部坐标系:以三维物体中的某个原点建立顶点比较方便,事实上一个复杂物体可能有多个局部坐标系,每个局部坐标系用于其某个部位。通过一组平移、旋转和缩放变换的组合,可以将局部坐标系变换到世界坐标系。

(2)世界坐标系:为了定义所有物体之间的空间关系,必须将这些物体变换到这个公共空间中。该变换由模型矩阵(Model Matrix)实现。

(3)摄像机/观察坐标系:观察坐标系就是从摄像机的视角所观察到的空间,而这通常是由一系列的位移和旋转的组合来完成,平移/旋转场景从而使得特定的对象被变换到摄像机的前方。这些组合在一起的变换通常储存在一个观察矩阵(View Matrix)中。

(4)标准化设备坐标系:定义一个投影矩阵(Projection Matrix),它指定了一个范围的坐标,比如在每个维度上的-1000到1000。投影矩阵接着会将在这个指定的范围内的坐标变换为标准化设备坐标的范围(-1.0, 1.0)。所有在范围外的坐标不会被映射到在-1.0到1.0的范围之间,所以会被裁剪掉。

(5)屏幕坐标系:它的原点位于屏幕的坐上角,y 轴正向垂直向下。

在OpenGL中,本地坐标系、世界坐标系和观察坐标系都属于右手坐标系,而最终的裁剪坐标系和标准化设备坐标系属于左手坐标系。

1.2   视图矩阵(View Matrix)详细推导

观察坐标系中,场景中所有顶点坐标经过视图变换到了以摄像机为原点的观察坐标。要定义一个摄像机,需要指定它在世界坐标系上的位置、观察方向、向右和向上方向。将摄像机位置设为eye,朝向的点为lookAt,向上向量为up,创建一个以摄像机为原点的坐标系。

(1)   首先计算方向向量:

dir=(eye-lookAt).normalize()

(2) 然后根据向上向量和方向向量计算得到摄像机右向量:

right=cross(dir,up).normalize()

(3) 然后计算计算单位向上向量:

up=cross(dir,right).normalize()

我们将摄像机放到世界坐标系原点,朝向-z轴方向,向上方向与y轴方向重合,这时候相机坐标系就和世界坐标系重合。这个过程分成两个矩阵,一是把摄像机移动到原点的平移变换(Transform Matrix),二是将摄像机的方向旋转变换(Rotate Matrix)到正确方向。

平移矩阵如下:

 

旋转逆矩阵中的各分量值即为x,y,z三个单位向量旋转后的值:

因为正交矩阵的逆等于转置,得到最终的视图矩阵:

1.1   OSG中视图矩阵

OSG提供osg::MatrixTransform类和osg::PositionAttitudeTransform子类改变节点的模型变换矩阵(Model Matrix)。另外提供了osg::Camera类计算相机视图矩阵(View Maatrix),但实际上相机的视图矩阵是由查看器的内部osgGA::CameraManipulator对象进行控制的。

(1) View::setCameraManipulator方法new 一个漫游器对象。

(2) View::init方法中调用漫游器初始化虚函数init

 1 void View::init()
 2 {
 3     OSG_INFO<<"View::init()"<<std::endl;
 4 
 5     osg::ref_ptr<osgGA::GUIEventAdapter> initEvent = _eventQueue->createEvent();
 6     initEvent->setEventType(osgGA::GUIEventAdapter::FRAME);
 7  if (_cameraManipulator.valid())
 8     {
 9         _cameraManipulator->init(*initEvent, *this);
10     }
11 }

(3) Viewer::eventTraversal方法事件遍历最后调用漫游器handle处理事件响应。

1 for(osgGA::EventQueue::Events::iterator itr = events.begin();itr != events.end();++itr)
2 {
3         osgGA::Event* event = itr->get();
4         if (event && _cameraManipulator.valid())
5         {
6             _cameraManipulator->handle( event, 0, _eventVisitor.get());
7         }
8 }

(4) Viewer::updateTraversal方法更新遍历中调用漫游器updateCamera更新相机视图矩阵。

1 /** update the camera for the current frame, typically called by the viewer classes.
2 Default implementation simply set the camera view matrix. */
3 virtual void updateCamera(osg::Camera& camera) { camera.setViewMatrix(getInverseMatrix()); }

2 OSG漫游器

2.1 CameraManipulator虚基类

       osgGA:: CameraManipulator虚基类继承osgGA:: GUIEventHandler,以处理事件消息,为子类提供接口:

 1 /** set the position of the matrix manipulator using a 4x4 Matrix.*/
 2 virtual void setByMatrix(const osg::Matrixd& matrix) = 0;
 3 
 4 /** set the position of the matrix manipulator using a 4x4 Matrix.*/
 5 virtual void setByInverseMatrix(const osg::Matrixd& matrix) = 0;
 6 
 7 /** get the position of the manipulator as 4x4 Matrix.*/
 8 virtual osg::Matrixd getMatrix() const = 0;
 9 
10 /** get the position of the manipulator as a inverse matrix of the manipulator, typically used as a model view matrix.*/
11 virtual osg::Matrixd getInverseMatrix() const = 0;
12 
13 /** Handle events, return true if handled, false otherwise. */
14 virtual bool handle(const GUIEventAdapter& ea,GUIActionAdapter& us);

2.2 StandardManipulator虚基类

  osgGA:: StandardManipulator虚基类继承osgGA:: CameraManipulator,补充了设置计算视图矩阵的接口和初始化方法:

 1 /** Sets manipulator by eye position and eye orientation.*/
 2 virtual void setTransformation( const osg::Vec3d& eye, const osg::Quat& rotation ) = 0;
 3 
 4 /** Sets manipulator by eye position, center of rotation, and up vector.*/
 5 virtual void setTransformation( const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up ) = 0;
 6 
 7 /** Gets manipulator's eye position and eye orientation.*/
 8 virtual void getTransformation( osg::Vec3d& eye, osg::Quat& rotation ) const = 0;
 9 
10 /** Gets manipulator's focal center, eye position, and up vector.*/
11 virtual void getTransformation( osg::Vec3d& eye, osg::Vec3d& center, osg::Vec3d& up ) const = 0;
12 
13 virtual void init( const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& us );

  并且重写了事件响应handle方法,大概有刷新事件、窗口大小重置事件、鼠标事件、键盘事件等类型。

 1 /** Handles events. Returns true if handled, false otherwise.*/
 2 bool StandardManipulator::handle( const GUIEventAdapter& ea, GUIActionAdapter& us )
 3 {
 4     switch( ea.getEventType() )
 5     {
 6 
 7         case GUIEventAdapter::FRAME:
 8             return handleFrame( ea, us );
 9 
10         case GUIEventAdapter::RESIZE:
11             return handleResize( ea, us );
12 
13         default:
14             break;
15    }
16 
17     if( ea.getHandled() )
18         return false;
19 
20     switch( ea.getEventType() )
21     {
22         case GUIEventAdapter::MOVE:
23             return handleMouseMove( ea, us );
24 
25         case GUIEventAdapter::DRAG:
26             return handleMouseDrag( ea, us );
27 
28         case GUIEventAdapter::PUSH:
29             return handleMousePush( ea, us );
30 
31         case GUIEventAdapter::RELEASE:
32             return handleMouseRelease( ea, us );
33 
34         case GUIEventAdapter::KEYDOWN:
35             return handleKeyDown( ea, us );
36 
37         case GUIEventAdapter::KEYUP:
38             return handleKeyUp( ea, us );
39 
40         case GUIEventAdapter::SCROLL:
41             if( _flags & PROCESS_MOUSE_WHEEL )
42             return handleMouseWheel( ea, us );
43             else
44             return false;
45 
46         default:
47             return false;
48     }
49 }

  事件处理函数内部只简单记录了当前时间和屏幕坐标,另外鼠标事件还调用了performMovement函数,细分了左中右键点击处理函数。

2.3 OrbitManipulator类

       osgGA:: OrbitManipulator类为轨道漫游器,可以使得相机围绕目标进行轨道运动,维护了目标中心(osg::Vec3d _center )、旋转四元数(osg::Quat  _rotation)、相机距离(double     _distance)三个关键数据,类似于1.2节中过程可以由此推导出视图矩阵。最终返回的矩阵如下:

 1 /** Get the position of the manipulator as a inverse matrix of the manipulator,
 2 typically used as a model view matrix.*/
 3 osg::Matrixd OrbitManipulator::getInverseMatrix() const
 4 {
 5     return osg::Matrixd::translate( -_center ) *
 6            osg::Matrixd::rotate( _rotation.inverse() ) *
 7            osg::Matrixd::translate( 0.0, 0.0, -_distance );
 8 }
 9 // doc in parent
10 void OrbitManipulator::setTransformation( const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up )
11 {
12     Vec3d lv( center - eye );
13 
14     Vec3d f( lv );
15     f.normalize();
16     Vec3d s( f^up );
17     s.normalize();
18     Vec3d u( s^f );
19     u.normalize();
20 
21     osg::Matrixd rotation_matrix( s[0], u[0], -f[0], 0.0f,
22                             s[1], u[1], -f[1], 0.0f,
23                             s[2], u[2], -f[2], 0.0f,
24                             0.0f, 0.0f,  0.0f, 1.0f );
25 
26     _center = center;
27     _distance = lv.length();
28     _rotation = rotation_matrix.getRotate().inverse();
29 
30     // fix current rotation
31     if( getVerticalAxisFixed() )
32         fixVerticalAxis( _center, _rotation, true );
33 }

  首先将漫游器移动到目标中心,然后旋转对齐方向向量,最后移动到观察位置。

  osgGA:: OrbitManipulator类重载了鼠标事件处理方法,详细编写了左中右键点击时间处理。其中左键拖动鼠标默认以轨道方式绕着目标旋转漫游器,中键拖动鼠标默认在xy平面平移漫游器,右键拖动鼠标默认在z轴移动漫游器,来达到缩放效果。

  综上分析,想要实现漫游器动画,需要重载函数修改目标中心、旋转四元数、相机距离这三个数据。

2.3 AnimationPathManipulator类

  osgGA:: AnimationPathManipulator类继承osgGA:: CameraManipulator,其内部维护了私有变量动画路径osg::AnimationPath _animationPath。初始化方法中可以直接导入动画路径,也可以外部设置,动画路径_animationPath需要设置动画循环模式、开始结束时间、控制点。osgGA:: AnimationPathManipulator类重载了事件响应方法:

 1 bool AnimationPathManipulator::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& us)
 2 {
 3     if( !valid() ) return false;
 4 
 5     switch( ea.getEventType() )
 6     {
 7     case GUIEventAdapter::FRAME:
 8         if( _isPaused )
 9         {
10             handleFrame( _pauseTime );
11         }
12         else
13         {
14             handleFrame( ea.getTime() );
15         }
16         return false;
17     case GUIEventAdapter::KEYDOWN:
18             if (ea.getKey()==' ')
19             {
20                 _isPaused = false;
21 
22                 home(ea,us);
23                 us.requestRedraw();
24                 us.requestContinuousUpdate(false);
25 
26                 return true;
27             }
28             else if (ea.getKey()==')')
29             {
30                 double time = _isPaused ? _pauseTime : ea.getTime();
31                 double animationTime = (time+_timeOffset)*_timeScale;
32 
33                 _timeScale *= 1.1;
34 
35                 OSG_NOTICE<<"Animation speed = "<<_timeScale*100<<"%"<<std::endl;
36 
37                 // adjust timeOffset so the current animationTime does change.
38                 _timeOffset = animationTime/_timeScale - time;
39 
40                 return true;
41             }
42             else if (ea.getKey()=='(')
43             {
44                 double time = _isPaused ? _pauseTime : ea.getTime();
45                 double animationTime = (time+_timeOffset)*_timeScale;
46 
47                 _timeScale /= 1.1;
48 
49                 OSG_NOTICE<<"Animation speed = "<<_timeScale*100<<"%"<<std::endl;
50 
51                 // adjust timeOffset so the current animationTime does change.
52                 _timeOffset = animationTime/_timeScale - time;
53 
54                 return true;
55             }
56             else if(ea.getKey() == 'p')
57             {
58                 if( _isPaused )
59                 {
60                     _isPaused = false;
61                     _timeOffset -= ea.getTime() - _pauseTime;
62                 }
63                 else
64                 {
65                     _isPaused = true;
66                     _pauseTime = ea.getTime();
67                 }
68                 us.requestRedraw();
69                 us.requestContinuousUpdate(false);
70                 return true;
71             }
72 
73         break;
74         default:
75             break;
76     }
77     return false;
78 }

  handle方法中通过handleFrame更新视图矩阵。

 1 void AnimationPathManipulator::handleFrame( double time )
 2 {
 3     osg::AnimationPath::ControlPoint cp;
 4 
 5     double animTime = (time+_timeOffset)*_timeScale;
 6     _animationPath->getInterpolatedControlPoint( animTime, cp );
 7 
 8     if (_numOfFramesSinceStartOfTimedPeriod==-1)
 9     {
10         _realStartOfTimedPeriod = time;
11         _animStartOfTimedPeriod = animTime;
12 
13     }
14 
15     ++_numOfFramesSinceStartOfTimedPeriod;
16 
17     double animDelta = (animTime-_animStartOfTimedPeriod);
18     if (animDelta>=_animationPath->getPeriod())
19     {
20         if (_animationCompletedCallback.valid())
21         {
22             _animationCompletedCallback->completed(this);
23         }
24 
25         if (_printOutTimingInfo)
26         {
27             double delta = time-_realStartOfTimedPeriod;
28             double frameRate = (double)_numOfFramesSinceStartOfTimedPeriod/delta;
29             OSG_NOTICE <<"AnimatonPath completed in "<<delta<<" seconds, completing "<<_numOfFramesSinceStartOfTimedPeriod<<" frames, average frame rate = "<<frameRate<<std::endl;
30         }
31 
32         // reset counters for next loop.
33         _realStartOfTimedPeriod = time;
34         _animStartOfTimedPeriod = animTime;
35         _numOfFramesSinceStartOfTimedPeriod = 0;
36     }
37 
38     cp.getMatrix( _matrix );
39 }

3 自定义OSG漫游器视角动画

3.1 四元数与三维旋转

       本篇论述引自Krasjet的博文re:https://krasjet.github.io/quaternion/quaternion.pdf,他从几何和计算机图形学角度上,介绍了四元数的概念和应用,详细讨论了四元数和三维旋转之间的关系。

(1)   定义:

(2)   四则运算:

  加减法各分量相加减

  标量乘法:乘以每一项

  四元数乘法: 

 

 

 

(3)   3D旋转公式 

(4)   设单位四元数  

则它对应的旋转角度、旋转轴为 

θ/2=(cos)^(-1) (a)
u=V/(sin⁡((cos)^(-1) (a)))

同一个3D旋转可以使用两个不同的四元数来表示,如果

标签:动画,const,自定义,ea,osgGA,OSG,return,坐标系,osg
From: https://www.cnblogs.com/wangxydela/p/17367573.html

相关文章

  • AntD框架的upload组件上传图片时使用customRequest方法自定义上传行为
    title:10-AntD框架的upload组件上传图片时使用customRequest方法自定义上传行为publish:true本次做后台管理系统,采用的是AntD框架。涉及到图片的上传,用的是AntD的upload组件。我在上一篇文章《前端AntD框架的upload组件上传图片时遇到的一些坑》中讲到:AntD的upload......
  • 自定义Behavior实现AppBarLayout越界弹性效果
    一、继承AppBarLayout.BehaviorAppBarLayout有一个默认的Behavior,即AppBarLayout.Behavior,AppBarLayout.Behavior已注解的方式设置给AppBarLayout。@CoordinatorLayout.DefaultBehavior(AppBarLayout.Behavior.class)publicclassAppBarLayoutextendsLinearLayout{.........
  • 自定义PopupWindow动画效果
    publicclassRollActivityextendsActivity{ privateViewview; privateButtonbtn; privatePopupWindowmPopupWindow; privateView[]btns;/**Calledwhentheactivityisfirstcreated.*/@OverridepublicvoidonCreate(BundlesavedInstan......
  • 简单位移动画TranslateAnimation
    已不再推荐补间动画,请使用属性动画;动画中的View的点击判断Android动画框架详解http://www.ibm.com/developerworks/cn/opensource/os-cn-android-anmt1/index.html每次点击往前100或往后100.packagecom.ql.app;importandroid.app.Activity;importa......
  • Activity切换动画效果的修改
    Activity的动画效果在\android\frameworks\base\core\res\res\values下的stlyes.xml,themes.xml两个文件中有定义。但是有时这些效果未必能满足你的要求,需要自己定义styles.xml来实现这个功能。Activity去掉默认的动画效果方法:1.重写Activity的Them中的......
  • Android提高第十八篇之自定义PopupWindow实现的Menu(TabMenu)
    用过UCWEB-Android版的人都应该对其特殊的menu有印象,把menu做成Tab-Menu(支持分页的Menu),可以容纳比Android传统的menu更丰富的内容(Android的menu超过6项则缩略在[更多]里),本文参考网上的例子(作者:CoffeeCole,email:[email protected]),对例子进行简化以及封装,使其作为一个复......
  • Android Activity界面切换添加动画特效
    在Android2.0之后有了overridePendingTransition(),其中里面两个参数,一个是前一个activity的退出两一个activity的进入。@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentVi......
  • 【web 开发基础】PHP自定义回调函数之call_user_func_array()
    前言从上一篇文章中我们了解到,回调函数是将一个函数作为参数传递到调用的函数中。如果在函数的格式说明中出现callback类型的参数,则该函数就是回调函数。虽然可以使用变量函数去声明自己的回调函数,不过我们通常大多还是会通过借助 call_user_func_array() 函数去实现。通过借助......
  • 自定义快捷键
    问题:复制粘贴的快捷键是CtrlC和CtrlV,在现实中粘贴值到可见单元格的用处更大,如何将这一功能自定义成快捷键?解决:【文件】》【选项】》【自定义功能区】 输入命令“粘贴值”,点击【请按新快捷键】,依次按下指定的快捷键(假设为Ctrl+Shift+V),点击【指定】 据此法,可以自定义任意命......
  • Android播放GIF动画
    "quality="high"type="application/x-shockwave-flash"pluginspage="http://www.macromedia.com/go/getflashplayer">1.<ImageViewandroid:id="@+id/gifpicture"2.android:layout_width="fill_parent&quo......