查看: 5008|回复: 12

世界坐标转屏幕坐标

[复制链接]

该用户从未签到

发表于 2012-8-9 16:41:32 | 显示全部楼层 |阅读模式
按照下面的方法世界坐标转屏幕坐标,转换之后的坐标值不正确,找不到原因,请帮忙看看
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::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;
}

该用户从未签到

发表于 2012-8-10 13:02:45 | 显示全部楼层
我没有细看您的做法,从世界坐标到屏幕坐标只要把世界坐标的点,乘以MVPW矩阵就可以了,也就是ModelView * Projection * Window Matrix

该用户从未签到

 楼主| 发表于 2012-8-10 13:36:26 | 显示全部楼层
array 发表于 2012-8-10 13:02
我没有细看您的做法,从世界坐标到屏幕坐标只要把世界坐标的点,乘以MVPW矩阵就可以了,也就是ModelView *  ...

我尝试了三种方法:1.自己计算 2.世界坐标的点,乘以MVPW矩阵 3. 通过API直接获取 但是三种方法得到的结果都不正确。我做了测试,通过事件处理得到的坐标,和我自己去获得这个坐标不相等。但是我这里又不能通过事件处理这种方式去获取。 有一点我补充一下,我这个不是一个全屏显示,只是用一个窗口区域来显示。
我想实现的功能是:已经加载了一些3D模型,然后有一个HUD区域显示texture信息,当点击某个模型的时候,画一条连接模型和HUD区域的一条直线,当模型旋转的时候,这条线不断地更新,始终连接模型和HUD区域。在实现的过程了因为获取屏幕坐标得到的结果不正确,所以导致画出来的线不准确。请帮忙指点一下小弟。

该用户从未签到

发表于 2012-8-11 00:52:33 | 显示全部楼层
上传你的代码,我帮你改好,很简单。

该用户从未签到

 楼主| 发表于 2012-8-11 12:26:21 | 显示全部楼层
sky11811 发表于 2012-8-11 00:52
上传你的代码,我帮你改好,很简单。

1. 这个函数做一些初始化相机的工作
void cOSG::InitCameraConfig(void)
{
    // Local Variable to hold window size data
    RECT rect;

    // Create the viewer for this window
    mViewer = new osgViewer::Viewer();

        //Get the width and height of screen
        int nFullWidth=GetSystemMetrics(SM_CXSCREEN);     
        int nFullHeight=GetSystemMetrics(SM_CYSCREEN);

        if(mViewer)
        {
                mViewer->setUpViewAcrossAllScreens();
               
                // Add a Stats Handler to the viewer
                //mViewer->addEventHandler(new osgViewer::StatsHandler);
   
                // Get the current window size
                ::GetWindowRect(m_hWnd, &rect);

                // Init the GraphicsContext Traits
                osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;

                // Init the Windata Variable that holds the handle for the Window to display OSG in.
                osg::ref_ptr<osg::Referenced> windata = new osgViewer::GraphicsWindowWin32::WindowData(m_hWnd);

                // Setup the traits parameters
                traits->x = 0;
                traits->y = 0;
                traits->width = rect.right - rect.left;
                traits->height = rect.bottom - rect.top;
                traits->windowDecoration = false;
                traits->doubleBuffer = true;
                traits->sharedContext = 0;
                traits->setInheritedWindowPixelFormat = true;
                traits->inheritedWindowData = windata;

                // Create the Graphics Context
                osg::GraphicsContext* gc = osg::GraphicsContext::createGraphicsContext(traits.get());

                // Init a new Camera (Master for this View)
                osg::ref_ptr<osg::Camera> camera = new osg::Camera;
       
                // Assign Graphics Context to the Camera
                camera->setGraphicsContext(gc);

                // Set the viewport for the Camera
                camera->setViewport(new osg::Viewport(traits->x, traits->y, traits->width, traits->height));

                // Set projection matrix and camera attribtues
                camera->setClearMask(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
                //camera->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.4f, 1.0f));
                camera->setClearColor(osg::Vec4f(0.0f, 0.0f, 0.0f, 1.0f));
                camera->setProjectionMatrixAsPerspective(
                        30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0, 1000.0);

                std:stringstream viewStr;
                osg::ref_ptr<osg::Geode>geodeText= new osg::Geode();
                std::string timesFont("fonts/arial.ttf");
                // turn lighting off for the text and disable depth test to ensure its always ontop.
                osg::StateSet* stateset = geodeText->getOrCreateStateSet();
                stateset->setMode(GL_LIGHTING,osg::StateAttribute::OFF);
                // Disable depth test, and make sure that the hud is drawn after everything
                // else so that it always appears ontop.
                stateset->setMode(GL_DEPTH_TEST,osg::StateAttribute::OFF);
                stateset->setRenderBinDetails(11,"RenderBin");
   
                osg::ref_ptr<osgText::Text>StatisText = new osgText::Text();
                geodeText->addDrawable(StatisText);
                                osg::Geometry* geom = new osg::Geometry;
                                geodeText->addDrawable(geom);

                // create the hud.
                osg::MatrixTransform* modelview_abs = new osg::MatrixTransform;
                modelview_abs->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
                modelview_abs->setMatrix(osg::Matrix::identity());
                modelview_abs->addChild(geodeText);
               
                osg::ref_ptr<osg:rojection> projectText= new osg::Projection();
                projectText->setMatrix(osg::Matrix::ortho2D(0,nFullWidth,0,nFullHeight));
                //projectText->setMatrix(osg::Matrix::ortho2D(0,traits->width,0,traits->height));
                projectText->addChild(modelview_abs);
                mRoot->addChild(projectText);
                               
                // Add the Camera to the Viewer
                mViewer->setCamera(camera.get());
                // Add the Camera Manipulator to the Viewer
                mViewer->setCameraManipulator(keyswitchManipulator.get());
                // Set the Scene Data
                mViewer->setSceneData(mRoot.get());

                if(nFullWidth != rect.right - rect.left || nFullHeight !=  rect.bottom - rect.top)
                {
                        mViewer->setKeyEventSetsDone(0);
                }
                mViewer->realize();

                messageLog->GetSystemTime();
                messageLog->o_file << "Initialize Camera Config" << endl;
        }
}
2. 这个函数负责根据输出texture信息画HUD区域,然后画我需要的那条直线
bool cOSG:isplayText(const char* pModelName,const char* pText)
{
        string szModelName = pModelName;
        string szText = pText;
        geodeText->removeDrawables(0,geodeText->getNumDrawables());
        std::string timesFont("fonts/arial.ttf");
        osg::Vec4 staticTextColor(199,77,15,1);
        osg::Vec3 position(0.0f, GetSystemMetrics(SM_CYSCREEN)-GetSystemMetrics(SM_CYSCREEN)/50,0.0f);
        //osg::Vec3 position(0.0f, 860,0.0f);
        osg::ref_ptr<osgText::Text>StatisText = new osgText::Text();
        geodeText->addDrawable(StatisText );
        std::ostringstream viewStr;
        viewStr.clear();
        viewStr.setf(std::ios::left, std::ios::adjustfield);
        viewStr.width(14);
        szText = "Testing text";
        viewStr << szText;
        viewStr << "Description" << std::endl;
        viewStr << "" << std::endl;
        viewStr << "Lights" << std::endl;
        viewStr << "Bins" << std::endl;
        viewStr << "Depth" << std::endl;
        
        StatisText->setFont(timesFont);
        StatisText->setColor(staticTextColor);
        StatisText->setCharacterSize(20.0f);
        StatisText->setPosition(position);
        viewStr.setf(std::ios::right,std::ios::adjustfield);
        StatisText->setText(viewStr.str());

        osg::BoundingBox bb;
        int aa = geodeText->getNumDrawables();
               for(unsigned int i=0;i<geodeText->getNumDrawables();++i)
              {
                     bb.expandBy(geodeText->getDrawable(i)->getBound());
              }

    osg::Geometry* geom = new osg::Geometry;

    osg::Vec3Array* vertices = new osg::Vec3Array;
    float depth = bb.zMin()-0.1;
    vertices->push_back(osg::Vec3(bb.xMin(),bb.yMax(),depth));
    vertices->push_back(osg::Vec3(bb.xMin(),bb.yMin(),depth));
    vertices->push_back(osg::Vec3(bb.xMax(),bb.yMin(),depth));
    vertices->push_back(osg::Vec3(bb.xMax(),bb.yMax(),depth));
    geom->setVertexArray(vertices);

    osg::Vec3Array* normals = new osg::Vec3Array;
    normals->push_back(osg::Vec3(0.0f,0.0f,1.0f));
    geom->setNormalArray(normals);
    geom->setNormalBinding(osg::Geometry::BIND_OVERALL);

    osg::Vec4Array* colors = new osg::Vec4Array;
    colors->push_back(osg::Vec4(1.0f,1.0,0.8f,0.2f));
    geom->setColorArray(colors);
    geom->setColorBinding(osg::Geometry::BIND_OVERALL);

    geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS,0,4));

    osg::StateSet* stateset1 = geom->getOrCreateStateSet();
    stateset1->setMode(GL_BLEND,osg::StateAttribute::ON);
    stateset1->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
    geodeText->addDrawable(geom);

        //Convert from World coordinate screen coordinate
        MatrixList matrixList;
        map<string, osg::ref_ptr<osg::MatrixTransform> >::iterator itor;
        for(itor = mapAllModels.begin();itor != mapAllModels.end();itor++)
        {
                string strModelName = itor->first;
                osg::ref_ptr<osg::MatrixTransform>tran = itor->second;
                if(tran)
                {
                        if(0 == strcmp(szModelName.c_str(),strModelName.c_str()))
                        {
                                matrixList = tran->getWorldMatrices(tran.get());
                                break;
                        }
                }
        }
        osg::Vec3 worldpoint = matrixList[0].getTrans();

        int viewPort[4];
        osg::Viewport* myviewPort = mViewer->getCamera()->getViewport();
        viewPort[0] = myviewPort->x();
        viewPort[1] = myviewPort->y();
        viewPort[2] = myviewPort->width();
        viewPort[3] = myviewPort->height();
       
        osg::Vec3 winPos = worldpoint*(mViewer->getCamera()->getViewMatrix())* (mViewer->getCamera()->getProjectionMatrix())* (mViewer->getCamera()->getViewport()->computeWindowMatrix());

        //Draw line
        osg::Geometry* geom2 = new osg::Geometry;
               osg::Vec3Array* vertices2 = new osg::Vec3Array;
              vertices2->push_back(osg::Vec3(bb.xMax()/2,bb.yMin(),depth));
              vertices2->push_back(osg::Vec3(winPos._v[0]*((double)GetSystemMetrics(SM_CXSCREEN)/(double)viewPort[2]),winPos._v[1]*((double)GetSystemMetrics(SM_CYSCREEN)/(double)viewPort[3]),depth));
             geom2->setVertexArray(vertices2);
             osg::Vec4Array* colors2 = new osg::Vec4Array;
             colors2->push_back(osg::Vec4(1.0f,1.0,0.8f,0.2f));
            geom2->setColorArray(colors2);

           geom2->addPrimitiveSet(new osg::DrawArrays(GL_LINES,0,2));
           geodeText->addDrawable(geom2);

        return true;
}

geodeText与第一个函数里geodeText是同一个,第一个函数先执行,第二个函数是做一些操作之后会执行,获取到的winPos 屏幕坐标不正确,我屏幕大小是,宽1680,高1050, 显示3D的这个窗口区域大小是840,668。请帮忙看一下怎么修改,非常感谢。这个问题已经困扰我两三天了

该用户从未签到

发表于 2012-8-11 17:43:23 | 显示全部楼层
把你的软件截一张图

该用户从未签到

发表于 2012-8-11 21:37:40 | 显示全部楼层
代码好多,希望楼上的能给找找问题所在~

该用户从未签到

 楼主| 发表于 2012-8-13 13:14:03 | 显示全部楼层
sky11811 发表于 2012-8-11 17:43
把你的软件截一张图

请问从代码能看出是什么问题吗?

该用户从未签到

 楼主| 发表于 2012-8-13 13:15:24 | 显示全部楼层
请高手帮忙看看,为什么从世界坐标转换为屏幕坐标得到的结果不正确?

该用户从未签到

发表于 2012-8-14 02:28:25 | 显示全部楼层
和你的绘图区域在窗口的什么地方相关,所以要看看。
你既然不愿意就算了。

该用户从未签到

发表于 2012-8-14 02:33:53 | 显示全部楼层
我只能说你的代码不完善,一般情况下是对的,没有问题,但是没有考虑全面。

该用户从未签到

发表于 2012-8-14 17:36:12 | 显示全部楼层
楼主截个图好了么。。。

该用户从未签到

 楼主| 发表于 2012-8-15 09:40:06 | 显示全部楼层
huhu_eagle 发表于 2012-8-14 17:36
楼主截个图好了么。。。

问题已经解决了,是计算的时候模型的世界坐标给得不对。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

联系我们

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