在上文中分析了攝像機類的實現(xiàn),在計算投影視圖矩陣時需要給攝像機輸入其位置及轉(zhuǎn)動四元數(shù)。這兩個量一般通過鼠標鍵盤來控制,從而達到控制攝像機的目的。本文分析一下其控制原理。
Overload的攝像機控制實現(xiàn)在類CameraController中,其有三個個方法HandleCameraPanning、HandleCameraFPSMouse、HandleCameraOrbit、HandleCameraZoom是鼠標控制攝像機的平移、繞自身轉(zhuǎn)動、繞特定點轉(zhuǎn)動、縮放。還有一個方法,HandleCameraFPSKeyboard是鍵盤控制攝像機。其頭文件如下,已刪除本文不關注的代碼及字段。
namespace OvEditor::Core
{
class CameraController
{
private:
// 控制攝像機的平移
void HandleCameraPanning(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse);
// 控制攝像機繞物體進行旋轉(zhuǎn)
void HandleCameraOrbit(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse);
// 鼠標控制攝像機旋轉(zhuǎn)
void HandleCameraFPSMouse(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse);
// 控制滾輪放大縮小
void HandleCameraZoom();
// 鍵盤控制攝像機
void HandleCameraFPSKeyboard(float p_deltaTime);
void UpdateMouseState();
private:
OvRendering::LowRenderer::Camera& m_camera; // 當前攝像機
OvMaths::FVector3& m_cameraPosition; // 當前攝像機的位置
OvMaths::FQuaternion& m_cameraRotation; // 當前攝像機的旋轉(zhuǎn)四元數(shù)
};
}
這四個函數(shù)就是通過改變m_cameraPosition、m_cameraRotation從而達到控制攝像機的目的。
一、鼠標控制縮放HandleCameraZoom
鼠標控制縮放的代碼如下:
void OvEditor::Core::CameraController::HandleCameraZoom()
{
m_cameraPosition += m_cameraRotation * OvMaths::FVector3::Forward * ImGui::GetIO().MouseWheel;
}
OvMaths::FVector3::Forward是固定矢量(0,0,1),其與m_cameraRotation相乘獲取當前攝像機的Z軸,也叫Forward量,或可稱為攝像機的指向。Imgui可獲取鼠標滾輪的轉(zhuǎn)動量,與Forward相乘,累加到攝像機位置上,產(chǎn)生攝像機拉進或拉遠的效果。在其他軟件中,我還見到過通過改變視口的大小實現(xiàn)縮放的,這種改變攝像機位置方式感覺更直觀。但這種方式對正向相機缺點:滾輪不能縮放物體,因為正交投影相當于將物體放到一個正方形盒子中,在盒子一側(cè)看,這個物體沿深度方向改變是不會有近大遠小的效果的。所以一些工業(yè)軟件中使用改變視口大小,實現(xiàn)物體縮放效果。
二、鼠標控制平動HandleCameraPanning
void OvEditor::Core::CameraController::HandleCameraPanning(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouset)
{
// 根據(jù)設置的拖動速度計算增量
auto mouseOffset = p_mouseOffset * m_cameraDragSpeed;
// 攝像機位置沿著Right、Up軸移動
m_cameraPosition += m_cameraRotation * OvMaths::FVector3::Right * mouseOffset.x;
m_cameraPosition -= m_cameraRotation * OvMaths::FVector3::Up * mouseOffset.y;
}
p_mouseOffset是鼠標移動矢量,是二維向量,但攝像機坐標系有三個軸,所以只能控制兩個軸的平動。
三、鼠標控制繞自身轉(zhuǎn)動HandleCameraFPSMouse
這個函數(shù)實現(xiàn)攝像機繞自身原點轉(zhuǎn)動。p_firstMouse是當鼠標按下是為true,轉(zhuǎn)動過程中為false。當?shù)谝淮无D(zhuǎn)動時,先將轉(zhuǎn)動轉(zhuǎn)換為歐拉角,RemoveRoll是對歐拉角做特殊處理,看著像是為了克服萬向節(jié)死鎖,沒看太明白,有用的時候再來深究吧。
void OvEditor::Core::CameraController::HandleCameraFPSMouse(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse)
{
auto mouseOffset = p_mouseOffset * m_mouseSensitivity;
if (p_firstMouse)
{
m_ypr = OvMaths::FQuaternion::EulerAngles(m_cameraRotation);
m_ypr = RemoveRoll(m_ypr);
}
m_ypr.y -= mouseOffset.x;
m_ypr.x += -mouseOffset.y;
m_ypr.x = std::max(std::min(m_ypr.x, 90.0f), -90.0f);
m_cameraRotation = OvMaths::FQuaternion(m_ypr);
}
鼠標偏移量改變歐拉角,注意其改變的值是x、y分量,最后再轉(zhuǎn)換為四元數(shù)。
四、攝像機繞特殊點旋轉(zhuǎn)HandleCameraOrbit
這個實際軟件中使用也很多。這個相對于繞攝像機原點旋轉(zhuǎn)多了平移分量,會同時改變攝像機的位置與姿態(tài)。
void OvEditor::Core::CameraController::HandleCameraOrbit(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse)
{
auto mouseOffset = p_mouseOffset * m_cameraOrbitSpeed; // 鼠標偏移量
if (p_firstMouse)
{
m_ypr = OvMaths::FQuaternion::EulerAngles(m_cameraRotation); // 轉(zhuǎn)換為歐拉角
m_ypr = RemoveRoll(m_ypr); // 可能是為了解決萬向節(jié)死鎖
m_orbitTarget = &EDITOR_EXEC(GetSelectedActor()).transform.GetFTransform();
m_orbitStartOffset = -OvMaths::FVector3::Forward * OvMaths::FVector3::Distance(m_orbitTarget->GetWorldPosition(), m_cameraPosition); // 攝像機需要平移的量(攝像機局部坐標系下)
}
m_ypr.y += -mouseOffset.x; // 對歐拉角進行改變
m_ypr.x += -mouseOffset.y;
m_ypr.x = std::max(std::min(m_ypr.x, 90.0f), -90.0f);
auto& target = EDITOR_EXEC(GetSelectedActor()).transform.GetFTransform();
OvMaths::FTransform pivotTransform(target.GetWorldPosition());
OvMaths::FTransform cameraTransform(m_orbitStartOffset); // 設置攝像機平移量
cameraTransform.SetParent(pivotTransform);
pivotTransform.RotateLocal(OvMaths::FQuaternion(m_ypr)); // 將繞的點進行旋轉(zhuǎn)
m_cameraPosition = cameraTransform.GetWorldPosition(); // 獲取攝像機位置
m_cameraRotation = cameraTransform.GetWorldRotation(); // 獲取攝像機轉(zhuǎn)角
}
其原理是將圍繞的點進行旋轉(zhuǎn),再平移獲取攝像機的位置及姿態(tài)。
五、鍵盤控制攝像機平動HandleCameraFPSKeyboard
這個函數(shù)原理類似于鼠標平動,都是線用轉(zhuǎn)動四元數(shù)獲取當前軸,給位置一個增量即可,這里就不詳細分析了。文章來源:http://www.zghlxwxcb.cn/news/detail-721480.html
如何使用這些函數(shù)與產(chǎn)品設計相結(jié)合?
上面只是攝像機控制的單個函數(shù),想在產(chǎn)品中集成還需其他的GUI庫(如QT、glfw、imgui等)配合獲取鼠標鍵盤狀態(tài),不斷分情況調(diào)用這些函數(shù)。這些代碼差異較大,跟產(chǎn)品設計密切相關,不具有通用性,不再分析。Overload這部分代碼在CameraController::HandleInputs函數(shù)中,有興趣可以閱讀。文章來源地址http://www.zghlxwxcb.cn/news/detail-721480.html
到了這里,關于【Overload游戲引擎細節(jié)分析】鼠標鍵盤控制攝像機原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!