[原][osg]解析osg自带左右眼立体成像功能的使用方式

    osg::DisplaySettings::instance()->setStereo(true);
    osg::DisplaySettings::instance()->setStereoMode(osg::DisplaySettings::HORIZONTAL_SPLIT);

打开osg自带的左右眼立体函数,可以直接将现有程序变成左右分屏的视图。

但是默认的视锥关系比较夸张,我想要修改内部参数,因此,在这分析osg此功能的内置算法。

查看关键参数:

在DisplaySettings其默认设置函数中,osg给了几个对应参数默认数值:

 void DisplaySettings::setDefaults() 
{
    _stereo = false;
    _stereoMode = ANAGLYPHIC;
    _eyeSeparation = 0.05f;
    _screenWidth = 0.325f;
    _screenHeight = 0.26f;
    _screenDistance = 0.5f;
  。。。。。。
}

也就是说默认  _stereo  立体参数是关闭的,后面的立体相关的数值也是用不到的,但是一旦打开,就用这个几个默认数值。

后面就查看这几个数值在哪里使用,明确功能的意义,再改变调试数值看看带来的变化。

找了一圈发现是View里面的updateSlave函数使用的以上数值:

void View::StereoSlaveCallback::updateSlave(osg::View& view, osg::View::Slave& slave)
{
    osg::Camera* camera = slave._camera.get();
    osgViewer::View* viewer_view = dynamic_cast<osgViewer::View*>(&view);

    if (_ds.valid() && camera && viewer_view)
    {
        // inherit any settings applied to the master Camera.
        camera->inheritCullSettings(*(view.getCamera()), camera->getInheritanceMask());

        if (_eyeScale<0.0)
        {
            camera->setCullMask(camera->getCullMaskLeft());
        }
        else
        {
            camera->setCullMask(camera->getCullMaskRight());
        }

        // set projection matrix
        if (_eyeScale<0.0)
        {
            camera->setProjectionMatrix(_ds->computeLeftEyeProjectionImplementation(view.getCamera()->getProjectionMatrix()));
        }
        else
        {
            camera->setProjectionMatrix(_ds->computeRightEyeProjectionImplementation(view.getCamera()->getProjectionMatrix()));
        }

        double sd = _ds->getScreenDistance();
        double fusionDistance = sd;
        switch(viewer_view->getFusionDistanceMode())
        {
            case(osgUtil::SceneView::USE_FUSION_DISTANCE_VALUE):
                fusionDistance = viewer_view->getFusionDistanceValue();
                break;
            case(osgUtil::SceneView::PROPORTIONAL_TO_SCREEN_DISTANCE):
                fusionDistance *= viewer_view->getFusionDistanceValue();
                break;
        }
        double eyeScale = osg::absolute(_eyeScale) * (fusionDistance/sd);

        if (_eyeScale<0.0)
        {
            camera->setViewMatrix(_ds->computeLeftEyeViewImplementation(view.getCamera()->getViewMatrix(), eyeScale));
        }
        else
        {
            camera->setViewMatrix(_ds->computeRightEyeViewImplementation(view.getCamera()->getViewMatrix(), eyeScale));
        }
    }
    else
    {
        slave.updateSlaveImplementation(view);
    }
}

主要调用是DisplaySettings里面的四个函数:

        /** helper function for computing the left eye projection matrix.*/
        virtual osg::Matrixd computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const;

        /** helper function for computing the left eye view matrix.*/
        virtual osg::Matrixd computeLeftEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;

        /** helper function for computing the right eye view matrix.*/
        virtual osg::Matrixd computeRightEyeProjectionImplementation(const osg::Matrixd& projection) const;

        /** helper function for computing the right eye view matrix.*/
        virtual osg::Matrixd computeRightEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale=1.0) const;

也就是通过改变左右眼点的  1.投影矩阵  2.视图矩阵  来改变左右立体投影变化。

下面,我们仔细分析一个单眼:左眼的  投影辅助计算矩阵   和   视图辅助计算矩阵

左眼投影矩阵辅助算法:

// Helper funciotns for computing projection and view matrices of left and right eyes
// 辅助函数,用于计算左眼和右眼的投影和视图矩阵
osg::Matrixd DisplaySettings::computeLeftEyeProjectionImplementation(const osg::Matrixd& projection) const
{
    double iod = getEyeSeparation();
    double sd = getScreenDistance();
    double scale_x = 1.0;//左右分开 二选一
    double scale_y = 1.0;//上下分开 二选一

    if (getSplitStereoAutoAdjustAspectRatio())
    {
        switch(getStereoMode())
        {
            case(HORIZONTAL_SPLIT):
                scale_x = 2.0;
                break;
            case(VERTICAL_SPLIT):
                scale_y = 2.0;
                break;
            default:
                break;
        }
    }

    if (getDisplayType()==HEAD_MOUNTED_DISPLAY)
    {
        // head mounted display has the same projection matrix for left and right eyes.
        // 头戴式显示器的左右眼投影矩阵相同
        return osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
    else
    {
        // all other display types assume working like a projected power wall
        // need to shjear projection matrix to account for asymetric frustum due to eye offset.
        // 所有其他显示类型都假定像投影电源墙一样工作-需要晃动投影矩阵,以解决由于眼睛偏移而导致的不对称视锥
        return osg::Matrixd(1.0,0.0,0.0,0.0,
                           0.0,1.0,0.0,0.0,
                           iod/(2.0*sd),0.0,1.0,0.0,
                           0.0,0.0,0.0,1.0) *
               osg::Matrixd::scale(scale_x,scale_y,1.0) *
               projection;
    }
}

 控制投影矩阵的主要是两点:眼睛分离量 _eyeSeparation = 0.05f; 和 离屏距离:_screenDistance = 0.5f;

右眼视图投影辅助算法:

osg::Matrixd DisplaySettings::computeLeftEyeViewImplementation(const osg::Matrixd& view, double eyeSeperationScale) const
{
    double iod = getEyeSeparation();
    double es = 0.5f*iod*eyeSeperationScale;

    return view *
           osg::Matrixd(1.0,0.0,0.0,0.0,
                       0.0,1.0,0.0,0.0,
                       0.0,0.0,1.0,0.0,
                       es,0.0,0.0,1.0);
}

 控制视图矩阵的,主要是: 眼睛分离量 _eyeSeparation = 0.05f; 

所以,我只需要改变:

void setEyeSeparation(float eyeSeparation)

void setScreenDistance(float distance) 

两个函数,就能改变左右眼立体视觉效果了。

原文地址:https://www.cnblogs.com/lyggqm/p/12530171.html