针对透视投影和正交投影的两种不同缩放。
我对场景的缩放操作是写在自定义的漫游器中的,重写handle方法来实现
透视投影
透视投影下缩放采用的方式是原有的handleMouseWheel(ea, us)方法,也就是修改_distance的值,因此要以鼠标为中心的话,需要对缩放前后的_distance做记录,并与角度一起计算需要对中心平移的向量,下图是一个计算时画的简图,向后缩放也是类似的:
以鼠标中心做缩放,eye移动的方向需要沿着与鼠标的方向向量v1,但是因为不打算更改漫游器默认的getInverseMatrix函数,所以为了保证eye看的方向还是朝前,可以移动_center来让视线方向不变,绿色部分就是是需要计算的移动向量,也就是本来eye的位置 eye’ 到 实际eye的位置 eye2 的移动向量,ɵ可以通过v1和eye与模型中心的向量v2的余弦值计算:
//通过变换矩阵获取屏幕上的鼠标点转换到世界坐标下的三维点
osg::Camera* camera = m_pViewer->getCamera();
osg::Matrix mvpw = camera->getViewMatrix() * camera->getProjectionMatrix() * camera->getViewport()->computeWindowMatrix();
osg::Matrix inverseMVPW = osg::Matrix::inverse(mvpw);
osg::Vec3 mouseWorld = osg::Vec3(ea.getX(), ea.getY(), 0) * inverseMVPW;
//获取当前视点到中心的方向向量
osg::Vec3 eye, center, up;
camera->getViewMatrixAsLookAt(eye, center, up);
_direction = center - eye;
_direction.normalize();
//获取视点到鼠标点的方向向量
osg::Vec3 direction = mouseWorld - eye;
direction.normalize();
//记录缩放前的_distance
double old_dis = _distance;
//默认缩放操作
handleMouseWheel(ea, us);
//记录缩放后的_disance
double new_dis = _distance;
if (new_dis == old_dis)
{
return false;
}
//因为是单位向量,所以余弦值等于两向量的点积
double res_dot = direction.x() * _direction.x() +
direction.y() * _direction.y() +
direction.z() * _direction.z();
//两次_distance之差
double d = old_dis - new_dis;
//沿鼠标点方向移动的距离
double diff = (old_dis - new_dis) / res_dot;
//沿鼠标点方向移动后的点
osg::Vec3 new_eye = eye + osg::Vec3(direction.x() * diff, direction.y() * diff, direction.z() * diff);
//沿中心方向移动后的点
osg::Vec3 s = eye + osg::Vec3(_direction.x() * d, _direction.y() * d, _direction.z() * d);
//位移向量
osg::Vec3 pan = new_eye - s;
//对_center做位移变换
_center += pan * osg::Matrix::inverse(m_rotationMat);
m_rotationMat是我自定义的旋转矩阵,是做别的事情时定义的,没有需要替换为默认的_rotation就行。
正交投影
正交投影下采用的方式是用 setProjectionMatrixAsOrtho 修改投影矩阵,修改后的投影矩阵不同会造成两次鼠标点转换到世界坐标中的点不同,这里的差异就是需要位移的部分:
osg::Vec3 ptWorldBefore, ptWorldAfter;
osg::Camera* camera = m_pViewer->getCamera();
//计算缩放前的变换矩阵
{
osg::Matrix mvpw = camera->getViewMatrix() * camera->getProjectionMatrix() * camera->getViewport()->computeWindowMatrix();
osg::Matrix inverseMVPW = osg::Matrix::inverse(mvpw);
ptWorldBefore = osg::Vec3(ea.getX(), ea.getY(), 0) * inverseMVPW;
}
double dFactor = 1.0;
osgGA::GUIEventAdapter::ScrollingMotion sm = ea.getScrollingMotion();
if (sm == osgGA::GUIEventAdapter::SCROLL_UP)
{
dFactor = 1.2;
}
else if (sm == osgGA::GUIEventAdapter::SCROLL_DOWN)
{
dFactor = 0.8;
}
double dL, dR, dT, dB, dZ, dF;
camera->getProjectionMatrixAsOrtho(dL, dR, dB, dT, dZ, dF);
double width, height;
width = dR - dL;
height = dT - dB;
dR = width * dFactor;
dT = height * dFactor;
//这里就完成了缩放
camera->setProjectionMatrixAsOrtho(-dR * 0.5, dR * 0.5, -dT * 0.5, dT * 0.5, dZ, dF);
//计算缩放后的变换矩阵
{
osg::Matrix mvpw = camera->getViewMatrix() * camera->getProjectionMatrix() * camera->getViewport()->computeWindowMatrix();
osg::Matrix inverseMVPW = osg::Matrix::inverse(mvpw);
ptWorldAfter = osg::Vec3(ea.getX(), ea.getY(), 0) * inverseMVPW;
}
osg::Vec3 vecMove = ptWorldBefore - ptWorldAfter;
//平移_center
_center += vecMove * osg::Matrix::inverse(m_rotationMat);