OSG实现正交投影

2019-08-10

  前段时间群里有人问OSG的正交投影怎么实现,似乎不熟悉的同学都陷入错误的思考方式,从而难以把问题想明白。其实,这个功能很简单。只要想明白了,就会豁然开朗,也能对类似的问题有所帮助。OSG内置的manipulator都是为透视投影准备的。但是,CAD程序多数情况下需要使用正交投影。OSG抽象了一个Manipulator类型,鼠标、键盘驱动camera进行交互浏览。其继承层次如下 :

                                  GUIEventHandler
                     CameraManipulator
               StandardManipulator
          OrbitManipulator        
  TrackballManipulator                             

  所以,我们需要继承osgGA::StandardManipulator,实现一个新的manipulator。对于三维场景的操作,主要有三个:zoom、rotate、pan(平移)。要实现正交投影的manipulator,主要也是实现这三个操作的逻辑。这里要提一下,要明确一点,缩放是滑轮驱动的,关于缩放系数,只能自己决定;拖拽、平移,由窗口坐标系的像素单位移动引起,所以,需要把像素距离转换为三维世界的距离。

                                                        OSG实现正交投影_第1张图片

  缩放。这个实现起来最简单。对于透视投影,想要放大,就需要camera与物体靠近,反之走远。这模拟的是真实世界。对于正交投影,改变camera与物体的距离,只能影响物体剔除。把右侧盒子想象为可以看到物体所在的空间,移动camera就相当于移动盒子。所以,只能改变盒子的宽高,亦即改变projection matrix。当缩小宽高时,相当于这个小人的一部分被投影到了同样大小的归一化平面上,最终仍显示在相同像素大小区域,那不就是放大了小人。
  平移。这个实现也简单。假设camera 位姿不变,亦即view matrix保持不变,把盒子在lookAt 垂直平面上平移。若盒子像右平移,小人图像就会从左侧慢慢消失。问题的关键是偏移的距离如何计算?因为在不同的应用程序中,单位不一样,数值可能相差很大,我们不能使用一种绝对数值,如每个像素对应1.0的距离。最简单的做法就是获取窗口的像素宽度,视野的世界坐标宽度,这样就能获取每像素移动所对应的世界坐标移动。仅仅设置projection matrix新的left, right, bottom, top, znear即可。
  旋转。这个最为困难。实现orthographic manipulator,我们同样需要view matrix,可以继续使用 初学者难以想明白流程。其实只要想明白最核心的一个问题即可。因为缩放、平移都没有改变view matrix,累计的projection matrix变动,会让我不确定它处于什么状态了。而且,旋转最简单分为两种情形:绕视野中心旋转、绕鼠标拖拽点旋转。鼠标拖拽点就是我们选择场景内物体的选择点坐标,其实,与视野中心点,在计算上,并没有什么不同。这个过程的关键,并不是思考camera是如何旋转的,而是,我们需要认为相机是不动的,旋转的是整个世界。旋转一个像素所带来的_rotation累计,直接记录在_rotation = _rotation * new_rotate; 这个量是孤立的。这样,我们便能计算出新的view matrix了:

osg::Matrixd newViewMatrix = getNewViewMatrix(rotateCenter, _rotation, _distance);

  到此时,就到了问题的关键点了。我们假设camera是不变的,那么,在camera坐标系中,旋转中心是不变的,而视野中心与旋转中心的相对位置是固定的,既然已经得到了新的view matrix,那么新的视野中心世界坐标转手即可得:

        osg::Vec3d fixPointView = rotateCenter * viewMatrix;
       osg::Vec3d newCenterView = oldCenterView*2.0 - fixPointView;
      osg::Vec3d newCenterWorld = newCenterView *  newViewMatInv;
      _center = newCenterWorld;

  对于整个三种操作过程,view matrix需要保存在manipulator,且同步到camera,projection matrix算好了,就需要直接写入camera。

如果有任何意见,欢迎留言讨论。 


[ 主页 ]

你可能感兴趣的:(图形,OpenGL)