查看: 9707|回复: 10

如何得到 世界坐标系下的点在osg屏幕坐标系下的二维坐标

[复制链接]

该用户从未签到

发表于 2008-6-12 10:28:05 | 显示全部楼层 |阅读模式
最近写一个程序,遇到了这么个问题,如何得到 世界坐标系下的点在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 !

该用户从未签到

发表于 2008-6-12 12:29:55 | 显示全部楼层
:) :) ~~~~~~~~

该用户从未签到

发表于 2008-9-18 20:38:01 | 显示全部楼层
:lol :lol

该用户从未签到

发表于 2011-2-20 13:14:43 | 显示全部楼层
貌似出现过

该用户从未签到

发表于 2011-2-23 17:13:47 | 显示全部楼层
感谢分享啊

该用户从未签到

发表于 2011-3-8 21:50:37 | 显示全部楼层
感谢分享,mark

该用户从未签到

发表于 2011-4-20 10:03:19 | 显示全部楼层
回复 1# heishuijing2222
这时候 屏幕最昨边和最右边的值 不是想象中的值了,最左边是负值 ,最右边-最左边 != ea._ymax !----这一段没明白,这样的话如何转化成屏幕坐标呢,

该用户从未签到

发表于 2011-4-20 10:05:50 | 显示全部楼层
回复 1# heishuijing2222
那咱们编程时如osg::Vec2(1.0,1.0,1.0)产生的顶点,应该是相对于世界坐标系的吧,经过osg处理后变为屏幕坐标显示吗:?

该用户从未签到

发表于 2011-4-20 10:34:21 | 显示全部楼层
对,你变成操作的是局部、或者世界坐标系的三维点,通过OGL管线会自动投影映射到屏幕二维坐标

该用户从未签到

发表于 2011-5-23 15:50:07 | 显示全部楼层
mark一下,最近遇到坐标的问题。。。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

OSG中国官方论坛-有您OSG在中国才更好

网站简介:osgChina是国内首个三维相关技术开源社区,旨在为国内更多的技术开发人员提供最前沿的技术资讯,为更多的三维从业者提供一个学习、交流的技术平台。

联系我们

  • 工作时间:09:00--18:00
  • 反馈邮箱:1315785073@qq.com
快速回复 返回顶部 返回列表