查看: 3082|回复: 9

发现并分享ViewerMFC窗口比例失调的解决方法,但是在正交投影下还是有问题……

[复制链接]

该用户从未签到

发表于 2009-4-15 17:46:53 | 显示全部楼层 |阅读模式
原帖地址:
http://forum.openscenegraph.org/viewtopic.php?t=2053

改动之处在MFC_OSG.cpp文件InitCameraConfig函数mViewer->realize()之后加入如下的代码手动更改比例:

double fovy,aspectRatio,z1,z2;
mViewer->getCamera()->getProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);
aspectRatio=double(traits->width)/double(traits->height);
mViewer->getCamera()->setProjectionMatrixAsPerspective(fovy,aspectRatio,z1,z2);

这样就透视投影工作正常了。

仍然是这个文件和这个函数位置,在mViewer->realize()之后我加入如下的代码将其更改为正交投影:

double w = ( double )traits->width  / ( 2 * 50 );
double h = ( double )traits->height / ( 2 * 50 );

mViewer->getCamera()->setProjectionMatrixAsOrtho2D( -w, w, -h, h );

后面的50相当于一个缩放参数。我想达到这样一个目的:屏幕上绘制东西的尺寸不随窗口的尺寸改动而变化。

于是我为CMFC_OSG_MDIView类添加了OnSize函数,并写了如下代码:

void CMFC_OSG_MDIView::OnSize(UINT nType, int cx, int cy)
{
        CView::OnSize(nType, cx, cy);

        // 偷懒添加了一个公共变量
        if( !mOSG->bOK )
                return;

        mOSG->getViewer()->getCamera()->setViewport( 0, 0, cx, cy );

        double w = ( double )cx / ( 2 * 50 );
        double h = ( double )cy / ( 2 * 50 );


        mOSG->getViewer()->getCamera()->setProjectionMatrixAsOrtho2D( -w, w, -h, h );
}

运行,但是当我改变窗口尺寸的时候,比例又开始变的离谱了,无论那句注释掉的setViewport用不用都一样,似乎Viewer总要自己去处理高度,在透视投影也一样,窗口的高度最终决定渲染尺寸,但我不想要这样的结果。

朋友们知道这个问题该怎样解决吗,或是我漏掉了什么?谢谢大家!

该用户从未签到

发表于 2009-4-15 19:49:26 | 显示全部楼层
如果您想达到的目的只是“屏幕上绘制东西的尺寸不随窗口的尺寸改动而变化”,那么我推荐一种更简单些的方法:
首先把之前分配的GraphicsContext指针用dynamic_cast<>转换为GraphicsWindowWin32*对象,然后用getHWND()获取新建立的渲染窗口的句柄,用WIN32函数SetParent设置这个窗口的父窗口为另一个独立窗口,这样无论怎么改变那个独立窗口的尺寸也不会影响到渲染窗口了,而渲染窗口本身的尺寸是用户改变不了的。

该用户从未签到

发表于 2009-4-15 19:53:35 | 显示全部楼层
高手,学习了!

该用户从未签到

 楼主| 发表于 2009-4-16 17:46:34 | 显示全部楼层
感谢array的回复!这个办法很方便,感觉上就是换了一个输出目标。

使用这个方法带来了两个问题,第一个是任务栏出现了两个窗口的按钮;第二个就是我仍然没办法对这个视口进行操作:我无法通过mOSG->getViewer()->getCamera()的成员函数setViewport和setProjectionMatrixAsOrtho2D来设置屏幕的中心点,程序对这个语句的调用在这个阶段似乎不去理会……

是否我对osg的使用思路错了呢?

该用户从未签到

发表于 2009-4-16 21:53:48 | 显示全部楼层
建议不要在OnSize中直接设置,可以采用类似osgviewerQt的方法,传入一个改变窗口大小的事件:
_gw->getEventQueue()->windowResize(0, 0, width, height );
_gw->resized(0,0,width,height);

该用户从未签到

 楼主| 发表于 2009-4-17 10:29:43 | 显示全部楼层
还是不行,resized之后屏幕上所有的东西都没有了,并且用户在点击渲染窗口的时候,主窗口会失去焦点,这会让用户很迷惑,下面是我的代码:

void MFC_OSG::InitCameraConfig()
{
        // ......

        m_Viewer = new osgViewer::Viewer();
        m_Viewer->realize();

        osg::GraphicsContext* pGraphicsContext = m_Viewer->getCamera()->getGraphicsContext();

        osgViewer::GraphicsWindowWin32* pGraphicsWindow = dynamic_cast< osgViewer::GraphicsWindowWin32* >( pGraphicsContext );

        SetParent( pGraphicsWindow->getHWND(), m_hWnd );

        // ......
}

void CChildView::OnSize(UINT nType, int cx, int cy)
{
        // ......

        osg::GraphicsContext* pGraphicsContext = mOSG->getViewer()->getCamera()->getGraphicsContext();

        osgViewer::GraphicsWindowWin32* pGraphicsWindow = dynamic_cast< osgViewer::GraphicsWindowWin32* >( pGraphicsContext );

        pGraphicsWindow->getEventQueue()->windowResize( 0, 0, cx, cy );
        pGraphicsWindow->resized( 0, 0, cx, cy );

        mOSG->getViewer()->getCamera()->setProjectionMatrixAsOrtho2D( -cx / 2, cx / 2, -cy / 2, cy / 2 );

        // ......
}

下面是我最终想要的结果示意图,我还是继续找其他办法吧,谢谢array了!

更改窗口尺寸之前:
before.gif

更改窗口尺寸之后:
after.gif

[ 本帖最后由 小行星 于 2009-4-17 17:42 编辑 ]

该用户从未签到

 楼主| 发表于 2009-10-13 14:59:25 | 显示全部楼层
不好意思啊,我又把这个帖子顶上来……

今天突然想到osgWidget里面的元素就是不随窗口缩放而缩放的那种,于是就找了一下他们是如何做到的,后来就找到了osgWidget\ViewerEventHandlers里面的类ResizeHandler,发现了答案,为了便于阅读我稍作修改,做了一个示例:


#include <osg/ref_ptr>

#include <osg/Geode>
#include <osg/Shape>
#include <osg/ShapeDrawable>

#include <osgGA/GUIEventHandler>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>


using namespace osg;
using namespace osgGA;
using namespace osgViewer;




class ResizeHandler: public GUIEventHandler
{
public:
        ResizeHandler( Camera* pCamera )
        {
                m_Camera = pCamera;
        }

        bool ResizeHandler::handle( const GUIEventAdapter& gea, GUIActionAdapter& gaa, Object* pObject, NodeVisitor* pNodeVisitor )
        {
                GUIEventAdapter::EventType ev = gea.getEventType();

                if( GUIEventAdapter::RESIZE != ev )
                {
                        return false;
                }

                Matrix::value_type w = gea.getWindowWidth();
                Matrix::value_type h = gea.getWindowHeight();

                if( m_Camera.valid() )
                {
                        m_Camera->setProjectionMatrix( Matrix:rtho2D( 0.0f, w, 0.0f, h ) );
                }

                return true;
        }

protected:
        observer_ptr< Camera > m_Camera;
};


int main()
{
        Geode* pGeode = new Geode;

        Box* pBox = new Box( Vec3( 400, 300, 0 ), 300 );

        pGeode->addDrawable( new ShapeDrawable( pBox ) );

        Viewer viewer;

        viewer.addEventHandler( new ResizeHandler( viewer.getCamera() ) );
        viewer.setUpViewInWindow( 50, 50, 800, 600 );
        viewer.setSceneData( pGeode );
        viewer.run();

        return 0;
}

看来这是一个使用思路的问题……

该用户从未签到

发表于 2009-10-14 09:17:39 | 显示全部楼层
默认情况下,主Camera是使用当前显示设置的(DisplaySetting)中的宽、高等信息计算并设置透视投影矩阵的(在osg::View的构造函数中)。而有些情况下,窗口初始大小与DisplaySetting中的宽高比可能不一致,这就导致渲染内容纵横比例失调!lz提到的在使用输出窗口的宽高比重新设置投影矩阵,就解决了渲染结构纵横比失调的问题。

对于自动修正视口和投影矩阵,建议lz仔细读一下 GraphicsContext::resizedImplementation 这个函数的代码,相信会有帮助的。这个函数在WM_SIZE消息处理函数中被调用,用于处理窗口大小变化时的相机视口和投影矩阵的修正。
对于正交投影的相机,resizedImplementation的修正方法是不适合的,需要自己写代码来处理,例如osgWidget::ResizeHandler的处理。这里之所以产生lz所说的“元素就是不随窗口缩放而缩放”,是因为这里只修改了投影矩阵而未修改视口。

该用户从未签到

 楼主| 发表于 2009-10-15 19:23:14 | 显示全部楼层
谢谢indif,resizedImplementation已经看过了,没有发现逻辑上的错误,接触osg时间不长,还需多多请教各位!

但这个mfc例子实在是很郁闷,我摘录MFC_OSG.cpp文件中,104行到113行:

  1. // Init a new Camera (Master for this View)
  2. osg::ref_ptr<osg::Camera> camera = new osg::Camera;

  3. // Assign Graphics Context to the Camera
  4. camera->setGraphicsContext(gc);

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

  7. // Add the Camera to the Viewer
  8. mViewer->addSlave(camera.get());
复制代码
当我作为一个osg新手看到这种跟MFC关联方法的时候,我会下意识的认为必须这么做,我也不太可能一开始就知道osg::Viewer其实自己本身就有一个摄像机,于是我就照搬了,后果就是出现了跟例子一样的错误,比例问题。

于是恶梦就开始了,我知道我将在这上面花费大量的时间去找答案。需求就是一个在Windows平台,MFC框架,Doc-View结构,非全屏3D程序。而设备关联这个最基础的问题肯定是要解决的,没想到关于这个问题,提问者的数量还是很多的,官方解决方案和第三方解决方案也很五花八门。

当然这个过程没有白费,我逐渐的开始形成对osg的理解,也明白了osg的使用思路是靠各种Visitor和handler和其他东西来对数据进行访问,于是就会按照框架的设计思路去解决这个问题。

但其实现在仍是一知半解,虽然知道有个偏移值存在,就是不明白为啥一模一样的相关代码,一跟MFC结合就走样,我也只好归结于我还不知道的某些初始化在MFC中其实没有做,于是我就换了一个思路,按照自己的方式关联设备,比如这个样子:

  1. GraphicsContext* pGraphicsContext = GraphicsContext::createGraphicsContext( traits );

  2. m_Viewer->getCamera()->setGraphicsContext( pGraphicsContext );
复制代码
就目前来看暂时没发现有啥问题,比例完全正常,缩放窗口也没啥问题,各种Manipulator也都ok,百思不得其解为啥示例要以那种方式去做,直接使用主摄像机不好吗?

发了些牢骚,无论如何,也希望对其他人有帮助~

该用户从未签到

发表于 2009-10-15 22:50:38 | 显示全部楼层
MFC例子也是网上的开发者共享的,并且被加入到OSG核心的例子当中。因为OSG的主要开发成员都不是Windows平台的开发者,因此对此不会很关注。如果您有自己的一套完善方案,那么欢迎继续与大家分享和讨论~~谢谢
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

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

联系我们

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