|
最近写一个程序,遇到了这么个问题,如何得到 世界坐标系下的点在osg屏幕坐标系下的二维坐标,其实 之前在老版本的时候也遇到过 ,不过那时候是用的 osgProducer的 viewer,而且论坛里有个兄弟还指出了 osg本身的计算坐标的函数的问题,现在我针对osg 2.4版本进行了测试,写出了下面一个方法:
//view是 osgViewer::View 类的指针,worldpoint是 世界坐标系下的一个三维点
//返回值是三维点在二维屏幕上的投影点,这里我把z值都设置成0,因为既然是二维坐标系我们只关心x 和y的值
//osg的 二维坐标系和opengl的坐标系是一样的 x 轴朝右 y轴朝上 而且 坐标原点在左下角
osg::Vec3d WorldToScreen(osgViewer::View* view,osg::Vec3 worldpoint)
{
double in[4], out[4];
in[0] = worldpoint._v[0];
in[1] = worldpoint._v[1];
in[2] = worldpoint._v[2];
in[3] = 1.0;
//获得当前的投影矩阵和模型视图矩阵
osg::Matrix projectMatrix= view->getCamera()->getProjectionMatrix();
osg::Matrix viewprojectMatrix = view->getCamera()->getViewMatrix();
//下面计算 模型视图矩阵 * 投影矩阵 * 视口窗口变换矩阵
double modelViewMatrix[16];
memcpy(modelViewMatrix,viewprojectMatrix.ptr(),sizeof(GLdouble) * 16);
Transform_Point(out, modelViewMatrix, in);
double myprojectMatrix[16];
memcpy(myprojectMatrix,projectMatrix.ptr(),sizeof(GLdouble) * 16);
Transform_Point(in, myprojectMatrix, out);
if (in[3] == 0.0)
return osg::Vec3d(0,0,0);
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
int viewPort[4];
osg::Viewport* myviewPort = view->getCamera()->getViewport();
viewPort[0] = myviewPort->x();
viewPort[1] = myviewPort->y();
viewPort[2] = myviewPort->width();
viewPort[3] = myviewPort->height();
//计算 三维点在屏幕上的二维投影点
osg::Vec3d sceenPoint;
sceenPoint._v[0] = (int)(viewPort[0] + (1 + in[0]) * viewPort[2] / 2 + 0.5);
sceenPoint._v[1] = (int)(viewPort[1] + (1 + in[1]) * viewPort[3] / 2 + 0.5);
sceenPoint._v[2] = 0;
return sceenPoint;
}
//上面函数用到了一个自己写的函数 Transform_Point
//这个函数 是计算 矩阵m * 向量 in = 向量 out
void Transform_Point(double out[4], const double m[16], const double in[4])
{
#define M(row,col) m[col*4+row]
out[0] =
M(0, 0) * in[0] + M(0, 1) * in[1] + M(0, 2) * in[2] + M(0, 3) * in[3];
out[1] =
M(1, 0) * in[0] + M(1, 1) * in[1] + M(1, 2) * in[2] + M(1, 3) * in[3];
out[2] =
M(2, 0) * in[0] + M(2, 1) * in[1] + M(2, 2) * in[2] + M(2, 3) * in[3];
out[3] =
M(3, 0) * in[0] + M(3, 1) * in[1] + M(3, 2) * in[2] + M(3, 3) * in[3];
#undef M
}
为了测试上面函数的准确性,我分别针对一个osg的 console工程的例子 和一个 osgMFC的例子做了测试,
测试方法是,我自定义一个世界坐标系下的三维坐标点 并且绘制在场景里,然后我分别将点移动到屏幕的左上,左下,中心,右上,右下角,然后 用我的函数来计算屏幕坐标 ,同时 和 在osgGA::GUIEventHandler 这个类继承类的
virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa);
方法 中 ea.getX();
ea.getY();
方法进行了对比,结果发现:
1 由于osg的console工程例子 本身就只用了osg::Viewer 而且是全屏 ,所以 计算的坐标和 ea得到的坐标完全一样
左上角0,ea._ymax 右上角 ea._xmax,ea._ymax 左下角 0,0 右下角 ea._xmax,0
2 osg的 MFC例子,由于其窗口用的是MFC的 窗口,其坐标系是CDC的坐标系,即坐标系原点在左上角 x朝右 y朝下
因此 要想精确的转换成 窗口坐标系 还需要做一个转换,WorldToScreen 这个函数计算出来的 就不是窗口坐标点了 而是osg的本身窗口坐标点,不过这不影响,因为它的值和
ea.getX();ea.getY(); 得到的值 仍然一样。
需要注意的是,这时候 屏幕最昨边和最右边的值 不是想象中的值了,最左边是负值 ,最右边-最左边 != ea._ymax ! |
|