【Overload游戏引擎细节分析】鼠标键盘控制摄像机原理

在上文中分析了摄像机类的实现,在计算投影视图矩阵时需要给摄像机输入其位置及转动四元数。这两个量一般通过鼠标键盘来控制,从而达到控制摄像机的目的。本文分析一下其控制原理。

Overload的摄像机控制实现在类CameraController中,其有三个个方法HandleCameraPanning、HandleCameraFPSMouse、HandleCameraOrbit、HandleCameraZoom是鼠标控制摄像机的平移、绕自身转动、绕特定点转动、缩放。还有一个方法,HandleCameraFPSKeyboard是键盘控制摄像机。其头文件如下,已删除本文不关注的代码及字段。

cpp 复制代码
namespace OvEditor::Core
{
	class CameraController
	{
	private:
		// 控制摄像机的平移
		void HandleCameraPanning(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse);
		// 控制摄像机绕物体进行旋转
		void HandleCameraOrbit(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouse);
		// 鼠标控制摄像机旋转
		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; // 当前摄像机的旋转四元数
	};
}

这四个函数就是通过改变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可获取鼠标滚轮的转动量,与Forward相乘,累加到摄像机位置上,产生摄像机拉进或拉远的效果。在其他软件中,我还见到过通过改变视口的大小实现缩放的,这种改变摄像机位置方式感觉更直观。

二、鼠标控制平动HandleCameraPanning

void OvEditor::Core::CameraController::HandleCameraPanning(const OvMaths::FVector2& p_mouseOffset, bool p_firstMouset)
{
   // 根据设置的拖动速度计算增量
	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是鼠标移动矢量,是二维向量,但摄像机坐标系有三个轴,所以只能控制两个轴的平动。

三、鼠标控制绕自身转动HandleCameraFPSMouse

这个函数实现摄像机绕自身原点转动。p_firstMouse是当鼠标按下是为true,转动过程中为false。当第一次转动时,先将转动转换为欧拉角,RemoveRoll是对欧拉角做特殊处理,看着像是为了克服万向节死锁,没看太明白,有用的时候再来深究吧。

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);
}

鼠标偏移量改变欧拉角,最后再转换为四元数。

四、摄像机绕特殊点旋转HandleCameraOrbit

这个实际软件中使用也很多。这个相对于绕摄像机原点旋转多了平移分量,会同时改变摄像机的位置与姿态。

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); // 转换为欧拉角
		m_ypr = RemoveRoll(m_ypr); // 可能是为了解决万向节死锁
		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)); // 将绕的点进行旋转
	m_cameraPosition = cameraTransform.GetWorldPosition();  // 获取摄像机位置
	m_cameraRotation = cameraTransform.GetWorldRotation(); // 获取摄像机转角
}

其原理是将围绕的点进行旋转,再平移获取摄像机的位置及姿态。

五、键盘控制摄像机平动HandleCameraFPSKeyboard

这个函数原理类似于鼠标平动,都是线用转动四元数获取当前轴,给位置一个增量即可,这里就不详细分析了。

相关推荐
游戏智眼23 分钟前
中国团队发布通用型AI Agent产品Manus;GPT-4.5正式面向Plus用户推出;阿里发布并开源推理模型通义千问QwQ-32B...|游戏智眼日报
人工智能·游戏·游戏引擎·aigc
虾球xz4 小时前
游戏引擎学习第138天
前端·学习·游戏引擎
虾球xz14 小时前
游戏引擎学习第137天
人工智能·学习·游戏引擎
虾球xz16 小时前
游戏引擎学习第133天
java·学习·游戏引擎
虾球xz1 天前
游戏引擎学习第135天
学习·游戏引擎
TWO8572 天前
Unity摄像机跟随物体
unity·游戏引擎
虾球xz2 天前
游戏引擎学习第134天
学习·游戏引擎
虾球xz2 天前
游戏引擎学习第132天
学习·游戏引擎
虾球xz3 天前
游戏引擎学习第131天
c++·学习·游戏引擎
TWO8574 天前
Unity自动旋转物体功能实现
unity·游戏引擎