OSG開發筆記(三十二):深入理解相機視口、製作支援與主檢視同步變換旋轉的相機HUD

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

前言

  深入理解相機視口,摸索相機視口旋轉功能,背景透明或者不透明。
  本篇,實現了一個左下角旋轉HUD且背景透明的相機視口。

Demo

  請新增圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

HUD相機的座標

  抬頭HUD就是透過投影矩陣來實現,具體可參看《OSG開發筆記(二十):OSG使用HUD顯示文字》

  • Hud要單獨建立一個新相機
  • 注意關閉光照,不受光照影響,所以內容以同一亮度顯示
  • 關閉深度測試
  • 渲染順序設定為POST,否則可能會被場景中的其他圖形所覆蓋。
  • 設定參考貼為絕對型:setReferenceFrame(osg::Transform:ABSOLUTE_RF)
  • 使其不受父節點變換的影響:setMatrix(osg::Matrix::identity())
  • 投影矩陣通常會設定為螢幕尺寸大小

相機(Camera)

  相機(osg::Camera)和視口(Viewport)是兩個核心概念,對於理解OSG中的三維場景渲染至關重要。
  相機在OSG中用於模擬真實世界中的攝影機,它負責捕捉和渲染三維場景。相機類(osg::Camera)繼承自osg::Transform和osg::CullSetting類,用來管理OSG中的模型——檢視矩陣。相機的管理主要是透過各種變換實現的,這些變換包括:

  • 視點變換:設定視點的方向和位置。預設情況下,視點定位為座標原點,指向Y正方向。可以透過調整視點的位置和參考點的位置來改變相機的觀察方向和角度。
  • 投影變換:由於顯示器只能用二維影像顯示三維物體,因此要靠投影來降低維數。投影變換的目的是定義一個視景體,使視景體外多餘的部分被裁減掉,最終進入影像的只是視景體內的有關部分。OSG支援兩種投影方式:透視投影(Perspective Projection)和正視投影(Orthographic Projection)。透視投影能夠模擬人眼的視覺效果,使遠處的物體看起來更小,而正視投影則保持物體的大小不變,不受距離影響。
  • 視口變換:將視景體內投影的物體顯示在二維的視口平面上。即將經過幾何變換、投影變換和裁剪變換後的物體顯示於螢幕視窗內指定的區域內,這個區域通常為矩形,稱為視口。

視口(ViewPort)

  具體來說,視口變換涉及以下幾個引數:

  • 螢幕左下角的座標:定義了視口在螢幕上的左下角位置。
  • 螢幕寬度和高度:定義了視口的寬度和高度,即相機捕捉的場景在螢幕上顯示的區域大小。
      在OSG中,可以透過呼叫相機的setViewport方法來設定視口。例如:
pCamera->setViewport(new osg::Viewport(0, 0, width, height));

  這行程式碼建立了一個新的視口,並將其設定為相機的當前視口。其中,0和0是螢幕左下角的座標,width和height是視口的寬度和高度。

相機與視口的關係

  相機和視口在OSG中緊密相連,共同決定了三維場景的渲染效果。相機負責捕捉和渲染場景,而視口則定義了相機捕捉的場景在螢幕上的顯示位置和大小。透過調整相機的各種變換和設定視口的大小和位置,可以實現豐富的三維視覺效果和互動體驗。

設定相機觀察函式

void setViewMatrixAsLookAt(const osg::Vec3d& eye, const osg::Vec3d& center, const osg::Vec3d& up);
  • eye:表示相機的位置。這是一個三維向量,指定了相機在世界座標系中的位置。
  • center:表示相機觀察的中心點。這也是一個三維向量,指定了相機應該對準的物體或場景的中心位置。
  • up:表示哪個方向是正方向。這同樣是一個三維向量,通常用於指定相機的上方方向(例如,通常設定為 (0,0,1) 表示Y軸正方向為上方)。
      設定相機位置和方向:透過指定 eye、center 和 up 三個引數,你可以精確地控制相機的位置和姿態。eye 和 center 之間的向量表示相機的觀察方向,而 up 向量則用於確定相機的上方方向。
      關閉漫遊器:在使用 setViewMatrixAsLookAt 函式之前,通常需要關閉相機的漫遊器(Camera Manipulator)。這是因為漫遊器會自動更新相機的觀察矩陣,從而覆蓋你透過 setViewMatrixAsLookAt 設定的引數。可以透過呼叫 viewer->setCameraManipulator(NULL) 來關閉漫遊器。
      座標系:OSG 使用右手座標系,其中 X 軸向右,Y 軸向上,Z 軸向前。因此,在設定 eye、center 和 up 引數時,需要確保它們符合右手座標系的規則。
      檢視矩陣:setViewMatrixAsLookAt 函式實際上是透過設定相機的檢視矩陣來實現相機位置和姿態的調整。檢視矩陣是一個 4x4 的矩陣,用於將相機座標系中的點轉換到世界座標系中。
    setViewMatrixAsLookAt 是一個強大的函式,它允許你以直觀的方式設定相機的位置和姿態。透過合理地使用這個函式,你可以建立出各種複雜而逼真的三維場景和視覺效果。

Demo關鍵原始碼

建立Hud相機

    // 步驟一:建立HUD攝像機
//        osg::ref_ptr<osg::Camera> pCamera = new osg::Camera;
    osg::ref_ptr<HudRotateCamera> pCamera = new HudRotateCamera;
    pCamera->setMasterCamera(_pViewer->getCamera());
    // 步驟二:設定投影矩陣
//        pCamera->setProjectionMatrix(osg::Matrix::ortho2D(0, 1280, 0, 800));
    // 步驟三:設定檢視矩陣,同時確保不被場景中其他圖形位置變換影響, 使用絕對幀引用
    pCamera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
    pCamera->setViewMatrix(osg::Matrix::identity());
    // 步驟四:清除深度快取
    pCamera->setClearMask(GL_DEPTH_BUFFER_BIT);
    // 步驟五:設定POST渲染順序(最後渲染)
//        pCamera->setRenderOrder(osg::Camera::PRE_RENDER);       // 渲染不顯示
//        pCamera->setRenderOrder(osg::Camera::NESTED_RENDER);
    pCamera->setRenderOrder(osg::Camera::POST_RENDER);
    // 步驟六:設定為不接收事件,始終得不到焦點
    pCamera->setAllowEventFocus(false);

//        osg::ref_ptr<osg::Geode> pGeode = new osg::Geode();
//        pGeode = new osg::Geode();
    osg::ref_ptr<osg::StateSet> pStateSet = pGeode->getOrCreateStateSet();
    // 步驟七:關閉光照
    pStateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
    // 步驟九:關閉深度測試
    pStateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);

//        pGeode->addDrawable(pGeometry.get());

    pCamera->addChild(pGeode.get());

    pGroup->addChild(pCamera.get());

HudRotateCamera.h

#ifndef HUDROTATECAMERA_H
#define HUDROTATECAMERA_H

#include "osg/Camera"
#include "osg/CopyOp"

class HudRotateCamera : public osg::Camera
{
public:
    HudRotateCamera();

    HudRotateCamera(const HudRotateCamera& copy, const osg::CopyOp &copyOp = osg::CopyOp::SHALLOW_COPY);

    META_Node(osg, HudRotateCamera);

public:
    void setMasterCamera(Camera* camera);

public:
    virtual void traverse(osg::NodeVisitor& nodeVisitor);

protected:
    virtual ~HudRotateCamera();

protected:
    osg::observer_ptr<Camera> _pMasterCamera;    // 新增了相機,主要是用來獲取舉證的
};

#endif // HUDROTATECAMERA_H

HudRotateCamera.cpp

#include "HudRotateCamera.h"

HudRotateCamera::HudRotateCamera(): Camera()
{

}

HudRotateCamera::HudRotateCamera(const HudRotateCamera & copy,  const osg::CopyOp & copyOp)
    : Camera(copy, copyOp),
      _pMasterCamera(copy._pMasterCamera)
{

}

HudRotateCamera::~HudRotateCamera()
{

}

void HudRotateCamera::setMasterCamera(osg::Camera *camera)
{
    _pMasterCamera = camera;
}

void HudRotateCamera::traverse(osg::NodeVisitor &nodeVisitor)
{
    double fovy, aspectRatio, vNear, vFar;
    _pMasterCamera->getProjectionMatrixAsPerspective(fovy, aspectRatio, vNear, vFar);

    // 設定投影矩陣,使縮放不起效果, 改為正投影,正投影不會隨相機的拉近拉遠而放大、縮小,這樣就沒有縮放效果,
    // 放大縮小是根據左右,上下距離,越大就物體越小,越小就物體越大
    this->setProjectionMatrixAsOrtho(-50 * aspectRatio,
                                      50 * aspectRatio,
                                     -50,
                                      50,
                                      vNear,
                                      vFar);
    // 讓座標軸模型位於窗體左下角
    osg::Vec3 vec3(-40, -40, 0);
    if (_pMasterCamera.valid())
    {
        // 改變檢視矩陣, 讓移動位置固定
        osg::Matrix matrix = _pMasterCamera->getViewMatrix();

        // 讓移動固定, 即始終位於窗體右下角,否則滑鼠左鍵按住模型可以拖動或按空格鍵時模型會動
        matrix.setTrans(vec3);
        this->setViewMatrix(matrix);
    }
    osg::Camera::traverse(nodeVisitor);
}

工程模板v1.35.0

  在這裡插入圖片描述

入坑

入坑一:沒有按照預期的方式全屏顯示在正中間

問題

  想一直顯示在中間,且能旋轉,移動中心,但是實際效果如下,方格100x100,間距1.0,測試:
  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

嘗試

  將面方格縮小為10x10,線放小,測試:
  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  縮小正交投影:
  在這裡插入圖片描述
  在這裡插入圖片描述

  可能跟相機檢視位置有關,新增相機位置和方向等資訊:
  在這裡插入圖片描述

  沒什麼影響:
  在這裡插入圖片描述

  這裡可能理解有問題,我們需要區域投影到視口,那麼一個是投影的區域三維區域的大小,一個是投影到桌面2D他的大小,這裡其實類似於HUD,透過HUD的方式,新增了幾行程式碼設定投影矩陣:
  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  經過測試,可以跳過相機調整視口、中心改變後也會移動,所以他一種在其位置區域。
  然後回到前面,發現也可以,再次摸索,發現如下特點:
  在這裡插入圖片描述

  所以此時,縱橫都是10,所以視窗大小要是符合1:1(10:10=1:1)的比例,改為400,400測試,還是一樣:
  在這裡插入圖片描述

  但是調整為800x800就好了:
  在這裡插入圖片描述

  放最大也不會擷取少了:
  在這裡插入圖片描述

  所以這個有點搞不明白了,總之是解決了,且投影矩陣和正交矩陣都可以解決,測試投影矩陣和正交舉證都收視口大小影響,但是影響具體不知,就好像800x800是最小一樣(其他的沒測了,只測了400x400、600x600不行,看比例800x800是最小正好滿視窗了)。
  在這裡插入圖片描述

  又懷疑投過去的區域小了,將區域放大,其位置反倒縮小,所以跟理解不一樣:

  • 一種是理解直接投射過去,投射過去區域變大所以變大(不是的);
  • 一種是投射過去區域不變,那麼區域變大檢視區域可見空間範圍變大(實際是這樣,但是視口對螢幕的大小未變);
      綜合以上,又測試了加大視口,也正常,所以懷疑有可能是qt和osg結合的時候這個地方設定了一個最小值,而可能吧,歡迎探討,這裡深究暫時也沒結果,且解決了,所以不繼續深究了。

解決

  修改相機視口大小為最小800x800。
  在這裡插入圖片描述

後續補充

  後續檢視做的這個qtosg相容類,做的時候,自己設定的800x800,就是這個原因了:
  在這裡插入圖片描述

入坑二:相機視口區域不透明

問題

  當作最前面的文字hud,是可以透明,但是這裡進行調整之後,無法透明。
  在這裡插入圖片描述

嘗試

  修改了語句,可以透明瞭部分,但是沒了。
  在這裡插入圖片描述

  在這裡插入圖片描述

解決

  相機是一個投影矩陣,沒有透明,但是文字hud為什麼透明呢?。

入坑三:內建幾何體關閉光照後純白色

問題

  關閉光照後,幾何體白色
  在這裡插入圖片描述

原理

  光照關閉要設定顏色,不想設定顏色,就單獨給體開放關照。

解決

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

入坑四:文字看不到

問題

  文字看不到,旋轉後發現是太大了。
  在這裡插入圖片描述

  位置較大。

嘗試

  測試下是把整個座標區域的展示範圍擴大,那麼實際看起來就是縮小。

解決

  在這裡插入圖片描述

  在這裡插入圖片描述

入坑五:hud旋轉中心不對

問題

  Hud旋轉中心不對
  在這裡插入圖片描述

  這時旋轉中心還不對,可能需要調整旋轉中心
  在這裡插入圖片描述

原理

  開始去修改矩陣,發現都不對,其實中間點一直是0,0,0,其就是中心,那麼我們設定文字的顯示點不是從0,0開始即可。
  下面將四邊形的角點改為0,0,0來標識,然後修改文字的中心點:
  在這裡插入圖片描述

解決

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

  在這裡插入圖片描述

入坑六:hud旋轉反向了

問題

  旋轉是反的,然後光照也反了,變換矩陣有問題
  在這裡插入圖片描述

原理

  資料幾何變換算來算去很費勁,直接測試的結論:
  在這裡插入圖片描述

  程式碼寫反了,讓上下反向了,應該是-50~50

解決

  在這裡插入圖片描述

  在這裡插入圖片描述

  還剩下光照問題,這個不好咋弄了,反正是關閉光照,或者是自己手動新增光源,用系統的可能有點問題。
  在這裡插入圖片描述

  在這裡插入圖片描述

  這個暫時沒解決,實際使用就是用一個,可以從長度單獨給這個相機設定一個光源,這裡因為需求本身不需要投影,不做測試了。

相關文章