OSG開發筆記(三十三):同時觀察物體不同角度的多檢視從相機技術

长沙红胖子Qt创微智科發表於2024-11-21

前言

  前面的相機hud可以單獨顯示圖形,繼續深入研究相機hud,技術就是子檢視了,實現該功能的直接技術是從相機技術。
  本篇描述osg從相機技術

Demo

  請新增圖片描述

  請新增圖片描述

相機視口的關鍵呼叫

是否清除顏色深度快取(清除)

pCamera->setClearMask(GL_DEPTH_BUFFER_BIT);

  如果不清除顏色快取,渲染的視窗中若無內容,則將其他視窗渲染的內容顯示到當前視窗。

設定渲染順序(最後渲染)

// 設定POST渲染順序(最後渲染)
pCamera->setRenderOrder(osg::Camera::POST_RENDER);

  後渲染的優先順序比較高(最後顯示,顯示優先順序最高)。

設定是否接受事件(不接受)

// 設定為不接收事件,始終得不到焦點
pCamera->setAllowEventFocus(false);

設定視口大小

// 視口就是引擎三維的區域,但是注意區別於螢幕的座標系(螢幕是左上為0,0,而三維引擎是左下為0,0)
pSlaveFrontCamera->setViewport(0,
                             0,
                             rect().width() / 4,
                             rect().height() / 4);

設定從相機故過程

步驟一:新建相機

osg::ref_ptr<osg::Camera> pSlaveFrontCamera = new osg::Camera;

步驟二:設定上下文

pSlaveFrontCamera->setGraphicsContext(_pViewer->getWindow());

步驟三:設定檢視區域

// 視口就是引擎三維的區域,但是注意區別於螢幕的座標系(螢幕是左上為0,0,而三維引擎是左下為0,0)
pSlaveFrontCamera->setViewport(0,
                             0,
                             rect().width() / 4,
                             rect().height() / 4);

步驟四:設定渲染順序

pSlaveFrontCamera->setRenderOrder(osg::Camera::POST_RENDER);

步驟五:關鍵步驟新增從相機

  第二個引數是縮放矩陣,第三個引數是旋轉矩陣

_pViewer->addSlave(pSlaveFrontCamera,
                  osg::Matrix(),
                  osg::Matrix::rotate(osg::DegreesToRadians(0.0), 0.0, 0.0, 0.0),
                  true);

Demo關鍵原始碼

osg::ref_ptr<osg::Node> OsgWidget::getMulViewCameraNode()
{
    // 隱藏整個demo全域性的按鈕皮膚(沒用到按鍵的直接隱藏,不影響此Demo)
    {
        ui->groupBox_pannel->setVisible(false);
        ui->label_cursor->setVisible(false);
        ui->label_cursor_2->setVisible(false);
        ui->label_msg->setVisible(false);
        ui->label_state->setVisible(false);
    }

    osg::ref_ptr<osg::Group> pGroup = new osg::Group;
    // 繪製盒體(立方體、長方體)
    {
        osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
        // 建立專門指明精細度的類osg::TessellationHints,並設定對應精細度
        osg::ref_ptr<osg::TessellationHints> pHints = new osg::TessellationHints;
        pHints->setDetailRatio(0.5);
        // 繪製幾何型別(幾何體)
        qreal width = 5.0f;
        // 函式1
        pGeode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), width), pHints));
#if 1
        // 設定關閉光照:OFF,同時旋轉都能看到了(光照關閉,法向量不起作用)
        {
            osg::StateSet *pStateSet = pGeode->getOrCreateStateSet();
            pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
//            pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
        }
#endif
        pGroup->addChild(pGeode);
    }
    // 建立多視口相機
    {
#if 0
        // 這裡改用了自己視窗已經建立的,這塊廢掉了,但是保留,基本的核心思想是一樣的
        osg::ref_ptr<osg::GraphicsContext::WindowingSystemInterface> pWindowingSystemInterface
                = osg::GraphicsContext::getWindowingSystemInterface();
        if(!pWindowingSystemInterface.get())
        {
            LOG << "if(!pWindowingSystemInterface.get())";
            return pGroup.get();
        }
        unsigned int width = 0;
        unsigned int height = 0;
        pWindowingSystemInterface->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0),
                                                       width,
                                                       height);
        osg::ref_ptr<osg::GraphicsContext::Traits> pTraits = new osg::GraphicsContext::Traits;
        {
            pTraits->x = 0;
            pTraits->y = 0;
            pTraits->width = width;
            pTraits->height = height;
            pTraits->windowDecoration = false;
            pTraits->doubleBuffer = true;
            pTraits->sharedContext = 0;
        }
        LOG << pTraits->x << pTraits->y << pTraits->width << pTraits->height;
        osg::ref_ptr<osg::GraphicsContext> pGraphicsContext = osg::GraphicsContext::createGraphicsContext(pTraits.get());
        if(!pGraphicsContext->valid())
        {
            LOG << "if(!pGraphicsContext->valid())";
            return pGroup.get();
        }
#endif

        double angle = 15.0f;

#if 1
        // 前
        osg::ref_ptr<osg::Camera> pSlaveFrontCamera = new osg::Camera;
        pSlaveFrontCamera->setGraphicsContext(_pViewer->getWindow());
        // 視口就是引擎三維的區域,但是注意區別於螢幕的座標系(螢幕是左上為0,0,而三維引擎是左下為0,0)
        pSlaveFrontCamera->setViewport(0,
                                       0,
                                       rect().width() / 4,
                                       rect().height() / 4);
        pSlaveFrontCamera->setRenderOrder(osg::Camera::POST_RENDER);
        _pViewer->addSlave(pSlaveFrontCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(0.0), 0.0, 0.0, 0.0),
                           true);
#endif


#if 1
        // 後
        osg::ref_ptr<osg::Camera> pSlaveBehindCamera = new osg::Camera;
        pSlaveBehindCamera->setGraphicsContext(_pViewer->getWindow());
        // 視口就是引擎三維的區域,但是注意區別於螢幕的座標系(螢幕是左上為0,0,而三維引擎是左下為0,0)
        pSlaveBehindCamera->setViewport(0,
                                        rect().width() / 4 * 3,
                                        rect().width() / 4,
                                        rect().height() / 4);
        pSlaveBehindCamera->setRenderOrder(osg::Camera::POST_RENDER);
        _pViewer->addSlave(pSlaveBehindCamera,
                           osg::Matrix::translate(0, 0, 0),
                           osg::Matrix::rotate(osg::DegreesToRadians(30), 1.0, 0.0, 0.0),
                           true);
#endif

#if 0
        // 左
//        osg::ref_ptr<osg::Camera> pSlaveLeftCamera = new osg::Camera;
//        pSlaveLeftCamera->setGraphicsContext(_pViewer->getWindow());
        osg::ref_ptr<HudRotateCamera> pSlaveLeftCamera = new HudRotateCamera;
        pSlaveLeftCamera->setGraphicsContext(_pViewer->getWindow());
        pSlaveLeftCamera->setMasterCamera(_pViewer->getCamera());
        pSlaveLeftCamera->setViewport(0,
                                      rect().height() / 8 * 3,
                                      rect().width()  / 4,
                                      rect().height() / 4);
        pSlaveLeftCamera->setRenderOrder(osg::Camera::POST_RENDER);

#if 0
        _pViewer->addSlave(pSlaveLeftCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(angle), 0.0, 0.0, 1.0),
                           true);
#endif
#if 0
        // 設定相機位置,觀察目標點和方向
        osg::Vec3f vec3Eye = osg::Vec3f(100, 100, 0);
        osg::Vec3f vec3Center = osg::Vec3f(0, 0, 0);
        osg::Vec3f vec3Up = osg::Vec3f(0, 1, 0);
        pSlaveLeftCamera->setViewMatrixAsLookAt(vec3Eye, vec3Center, vec3Up);
        _pViewer->addSlave(pSlaveLeftCamera);
#endif
#if 1
        // 設定slave相機的位置和方向
        osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform();
        // 設定平移矩陣,根據需要進行調整
        osg::Matrix matrix;
        matrix.makeTranslate(0, 0, 0); // 這裡的x, y, z是你想要平移到的位置
        transform->setMatrix(matrix);
        transform->addChild(pGroup);
        _pViewer->addSlave(pSlaveLeftCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(15.0f), 0.0, 1.0, 1.0),
                           true);
        pSlaveLeftCamera->setProjectionMatrixAsPerspective(
                    100.0f, 1.0, 1.0f, 100.0f);
#endif

#endif

#if 0
        // 右
        osg::ref_ptr<osg::Camera> pSlaveRightCamera = new osg::Camera;
        pSlaveRightCamera->setGraphicsContext(_pViewer->getWindow());
        pSlaveRightCamera->setViewport(rect().width()  / 4 * 3,
                                       rect().height() / 8 * 3,
                                       rect().width()  / 4,
                                       rect().height() / 4);
        pSlaveRightCamera->setRenderOrder(osg::Camera::POST_RENDER);
        _pViewer->addSlave(pSlaveRightCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(-angle), 0.0, 0.0, 1.0),
                           true);
#endif

#if 0
        // 上
        osg::ref_ptr<osg::Camera> pSlaveUpCamera = new osg::Camera;
        pSlaveUpCamera->setGraphicsContext(_pViewer->getWindow());
        pSlaveUpCamera->setViewport(rect().width()  / 8 * 3,
                                    0,
                                    rect().width()  / 4,
                                    rect().height() / 4);
        pSlaveUpCamera->setRenderOrder(osg::Camera::POST_RENDER);
        _pViewer->addSlave(pSlaveUpCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(angle), 1.0, 0.0, 0.0),
                           true);
#endif

#if 0
        // 下
        osg::ref_ptr<osg::Camera> pSlaveDownCamera = new osg::Camera;
        pSlaveDownCamera->setGraphicsContext(_pViewer->getWindow());
        pSlaveDownCamera->setViewport(rect().height() / 8 * 3,
                                      rect().height() / 8 * 6,
                                      rect().width() / 4,
                                      rect().height() / 4);
        pSlaveDownCamera->setRenderOrder(osg::Camera::POST_RENDER);
        _pViewer->addSlave(pSlaveDownCamera,
                           osg::Matrix(),
                           osg::Matrix::rotate(osg::DegreesToRadians(-angle), 1.0, 0.0, 0.0),
                           true);
#endif

    }
    return pGroup.get();
}

工程模板v1.36.0

  在這裡插入圖片描述

入坑

入坑一:設定相機就崩潰

問題

  在這裡插入圖片描述

解決過程

  定位到不設定相機就不崩潰,然後這裡是筆者自己造的Qt與OSG結合的,使用了位置,這裡也可以檢視實際列印的建立的區域座標和大小,確實也是不對:
  在這裡插入圖片描述

  那直接把場景裡面的gc賦值給他測試,是可以的,修改的地方有點多,因為這個Qt+OSG是筆者根據原始碼原理進行調整渲染的,與直接編譯出來的qt+osg還是有點區別,總之一句話,就是Qt渲染視窗裡面已經有這個osg::ref_ptrosg::GraphicsContext了,不用去額外建立了:
  刪除以下程式碼:
  在這裡插入圖片描述

  然後再調整相機程式碼,還有從Qt渲染視窗裡面增加拿到這個內容上下文的函式就好了。

解決

  新增獲取函式,原本不能獲取
  在這裡插入圖片描述

  在這裡插入圖片描述

  這裡實際大小為:
  在這裡插入圖片描述

  所以外面程式碼,直接用視窗的寬高好了(筆者是鋪滿的):這裡是要縮小放前面,那就是改為4/1吧:
  在這裡插入圖片描述

  在這裡插入圖片描述

入坑二:左檢視沒有

問題

  左檢視應該顯示,但是沒顯示
  在這裡插入圖片描述

解決過程

  改成一樣的:
  在這裡插入圖片描述

  然後不偏移試試:
  在這裡插入圖片描述

  偏移一個小角度試試:
  在這裡插入圖片描述

  所以是Y軸的中心不對,但是我們也沒有改,測試繞x軸:
  在這裡插入圖片描述

  然後繞z軸,發現就z軸沒有偏移:
  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  嘗試單獨設定新增相機的視口是無效的:
  在這裡插入圖片描述

  在這裡插入圖片描述

  嘗試單獨修改同步旋轉的相機去修改視口,也是無效:
  在這裡插入圖片描述

  繼續嘗試:
  在這裡插入圖片描述

  是否與內建相機的視口有關係,測試也無關:
  在這裡插入圖片描述

  在這裡插入圖片描述

解決(主技術方向未解決)

  從原始從相機技術方面暫時沒有解決,因為也嘗試了更改矩陣、修改相機視角觀看位置都沒什麼變化。
  可以確認的是,應該是相機旋轉的中心不對,並不是場景中心不對,所以滑鼠拽託中間還是在旋轉,而視角旋轉則x和y軸存在偏移。X和y存在偏移就是左右和裡外,若是與螢幕有關也是上下和左右,所以這裡這麼分析推斷也不對。
  程式碼全部放出,讀者有興趣可以提供協助,一起探討。

規避解決方法

  直接在相機中修改偏移旋轉,然後當作結點加入,是可以解決,而且還不能是從相機,需要addChild進入:
  在這裡插入圖片描述

  這時候拉伸有問題:
  在這裡插入圖片描述

  變形了:
  在這裡插入圖片描述

  在這裡插入圖片描述

  終於外掛一個東西解決:
  在這裡插入圖片描述

  在這裡插入圖片描述

  但是滑鼠中鍵按下偏移中心點,會都向右,理論上反向180°的y軸應該向左,但是還是向右,因為是場景偏移,我們規避是對場景下的相機進行旋轉,所以實際是移動場景相機了,相機裡面的正反對外無效。

官方從相機示例(也存在問題,懷疑是osg3.4.0原始碼bug)

  為了再次深入論證是否程式碼問題,筆者又用官方的demo實現:

#include <osg/Camera>
#include <osg/Group>
#include <osg/Geode>
#include <osg/ShapeDrawable>
#include <osgViewer/Viewer>
#include <osg/GraphicsContext>

int main(int argc, char *argv[])
{

    osg::ref_ptr<osg::Group> pGroup = new osg::Group;
    // 繪製盒體(立方體、長方體)
    {
        osg::ref_ptr<osg::Geode> pGeode = new osg::Geode;
        // 建立專門指明精細度的類osg::TessellationHints,並設定對應精細度
        osg::ref_ptr<osg::TessellationHints> pHints = new osg::TessellationHints;
        pHints->setDetailRatio(0.5);
        // 繪製幾何型別(幾何體)
        double width = 5.0f;
        // 函式1
        pGeode->addDrawable(new osg::ShapeDrawable(new osg::Box(osg::Vec3(0, 0, 0), width), pHints));
        // 設定關閉光照:OFF,同時旋轉都能看到了(光照關閉,法向量不起作用)
        {
            osg::StateSet *pStateSet = pGeode->getOrCreateStateSet();
            pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON);
        }
        pGroup->addChild(pGeode);
    }

    osg::GraphicsContext::WindowingSystemInterface * wsi = osg::GraphicsContext::getWindowingSystemInterface();
    if(!wsi)
    {
        return 0;
    }

    osg::ref_ptr<osg::GraphicsContext::Traits > traits = new osg::GraphicsContext::Traits;
    traits->x = 0;
    traits->y = 0;
    traits->width = 800;
    traits->height = 600;
    traits->windowDecoration = false;
    traits->doubleBuffer = true;
    traits->sharedContext = 0;

    osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits);
    if(!gc.valid())
    {
        return 0;
    }


    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;

    osg::ref_ptr<osg::Camera> master = new osg::Camera;
    master->setGraphicsContext(gc);
    master->setViewport(0, 0, 800, 600);
    viewer->addSlave(master.get());

    osg::ref_ptr<osg::Camera> leftcam = new osg::Camera;
    leftcam->setGraphicsContext(gc);
    leftcam->setViewport(0, 0, 800 / 2, 600 / 2);
    leftcam->setRenderOrder(osg::Camera::POST_RENDER);
    viewer->addSlave(leftcam.get(),
                     osg::Matrix(),
                     osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 1.0, 0.0),
                     true);

    viewer->setSceneData(pGroup);

    viewer->run();

    return 0;
}

  在這裡插入圖片描述

  改進後相機程式碼:

    osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;

    osg::ref_ptr<osg::Camera> master = new osg::Camera;
    master->setGraphicsContext(gc);
    master->setViewport(0, 0, 800, 800);
    viewer->addSlave(master.get());

    {
        osg::ref_ptr<osg::Camera> leftcam = new osg::Camera;
        leftcam->setGraphicsContext(gc);
        leftcam->setViewport(0, 0, 100, 100);
        leftcam->setRenderOrder(osg::Camera::POST_RENDER);
        viewer->addSlave(leftcam.get(),
                         osg::Matrix(),
                         osg::Matrix::rotate(osg::DegreesToRadians(15.0), 1.0, 0.0, 0.0),
                         true);
    }

    {
        osg::ref_ptr<osg::Camera> leftcam = new osg::Camera;
        leftcam->setGraphicsContext(gc);
        leftcam->setViewport(100, 0, 100, 100);
        leftcam->setRenderOrder(osg::Camera::POST_RENDER);
        viewer->addSlave(leftcam.get(),
                         osg::Matrix(),
                         osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 1.0, 0.0),
                         true);
    }

    {
        osg::ref_ptr<osg::Camera> leftcam = new osg::Camera;
        leftcam->setGraphicsContext(gc);
        leftcam->setViewport(200, 0, 100, 100);
        leftcam->setRenderOrder(osg::Camera::POST_RENDER);
        viewer->addSlave(leftcam.get(),
                         osg::Matrix(),
                         osg::Matrix::rotate(osg::DegreesToRadians(15.0), 0.0, 0.0, 1.0),
                         true);
    }

    {
        osg::ref_ptr<osg::Camera> leftcam = new osg::Camera;
        leftcam->setGraphicsContext(gc);
        leftcam->setViewport(300, 0, 100, 100);
        leftcam->setRenderOrder(osg::Camera::POST_RENDER);
        viewer->addSlave(leftcam.get(),
                         osg::Matrix(),
                         osg::Matrix::rotate(osg::DegreesToRadians(180.0), 0.0, 0.0, 1.0),
                         true);
    }

  所以是osg3.4.0的原始碼這塊就有問題:
  在這裡插入圖片描述

相關文章