【Overload游戏引擎细节分析】鼠标键盘控制摄像机原理
在上文中分析了摄像机类的实现,在计算投影视图矩阵时需要给摄像机输入其位置及转动四元数。这两个量一般通过鼠标键盘来控制,从而达到控制摄像机的目的。本文分析一下其控制原理。
Overload的摄像机控制实现在类CameraController中,其有三个个方法HandleCameraPanning、HandleCameraFPSMouse、HandleCameraOrbit、HandleCameraZoom是鼠标控制摄像机的平移、绕自身转动、绕特定点转动、缩放。还有一个方法,HandleCameraFPSKeyboard是键盘控制摄像机。其头文件如下,已删除本文不关注的代码及字段。
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);
}
鼠标偏移量改变欧拉角,注意其改变的值是x、y分量,最后再转换为四元数。
四、摄像机绕特殊点旋转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
这个函数原理类似于鼠标平动,都是线用转动四元数获取当前轴,给位置一个增量即可,这里就不详细分析了。
相关文章:
【Overload游戏引擎细节分析】鼠标键盘控制摄像机原理
在上文中分析了摄像机类的实现,在计算投影视图矩阵时需要给摄像机输入其位置及转动四元数。这两个量一般通过鼠标键盘来控制,从而达到控制摄像机的目的。本文分析一下其控制原理。 Overload的摄像机控制实现在类CameraController中,其有三个个…...

VScode运行SVN拉下来的项目
安装依赖包 pnpm install 启动程序 查看package.json文件中的serve,根据这个启动 pnpm dev 在浏览器使用http://localhost:8848/访问...
jmeter集成kafka测试
Kafka的使用 查看kafka的topic ./kafka-topics --bootstrap-server 10.1.9.84:9092 --list 查看topic信息 ./kafka-topics --bootstrap-server 10.1.9.84:9092 --describe --topic topic_example_1 创建topic 创建topic名为test,分区数为8,副本数为…...

Java面试题-UDP\TCP\HTTP
UDP UDP特性 (1)UDP是无连接的:发送数据之前不需要像TCP一样建立连接,也不需要释放连接,所以减少了发送和接收数据的开销 (2)UDP 使用尽最大努力交付:即不保证可靠交付 ࿰…...

使用WPF模仿Windows记事本界面
本次仅模仿Windows记事本的模样,并未实现其功能。 所有代码如下: <Window x:Class"控件的基础使用.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/…...

【目标检测】Co-DETR:ATSS+Faster RCNN+DETR协作的先进检测器(ICCV 2023)
论文:DETRs with Collaborative Hybrid Assignments Training 代码**:https://github.com/Sense-X/Co-DETR 文章目录 摘要一、简介二、本文方法2.1.概述2.2.协同混合分配训练2.3. 定制的正 Query 生成2.4. Co-DETR为何有效1、丰富编码器的监督2、通过减少…...

iOS QQ登录SDK升级后报错Duplicate interface definition for class ‘TencentOAuth‘修复
起因 最近发现QQ登录SDK sdk-Lite3.3.8 TencentOpenAPI 在部分手机上会崩溃到初始化位置_tencentOAuth [[TencentOAuth alloc] initWithAppId:appid andDelegate:self];, 比如:iPhone6p 版本12.5.4,iPhone8p 版本14.1,iPad版本1…...

laravel中锁以及事务的简单使用
一、首先来说一下什么是共享锁?什么是排他锁? 共享:我可以读 写 加锁 , 别人可以 读 加锁。 排他:只有我 才 可以 读 写 加锁 , 也就是说,必须要等我提交事务,其他的才可以操作。 二、简单例子实现加锁 锁…...

Vue+openlayers+projs4实现坐标转换
一、背景 有一堆点数据,需要在地图上标记,只知参考北京54坐标系或西安80坐标系,但具体是哪种不清楚,这时候就需要坐标转换。ps:EPSG:3857(openlayers参照的坐标系) 二、思路 1、研…...

09 创建型模式-建造者模式
1.建造者模式介绍: 建造者模式 (builder pattern), 也被称为生成器模式 , 是一种创建型设计模式 定义: 将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不 同的表示。 2.建造者模式要解决的问题 建造者模式可以将部件和其组装过程分开&am…...

4.9 多协议标记交换MPLS
思维导图: 前言: **4.9 多协议标记交换MPLS笔记** 1. **定义与背景**: - MPLS (多协议标记交换) 是一种由 IETF 开发的新协议。 - “多协议”意味着 MPLS 的上层可以使用多种协议。 - 该协议综合了多家公司的技术,如 C…...

【经历】在职8个月->丰富且珍贵
在职8个月->丰富且珍贵 2021-3~2021-11:面试进入一家做400电话的公司,我进入公司时,加上我只有四个人(老板、人事、业务),开发只有我,所以:产品~设计~前端~后端~测试~上线~维护~培训,只有我自…...
使用GH(命令行)在本地提出Github上的issue、PR,合并PR
使用GH(命令行)在本地提出Github上的issue、PR,合并PR 前言 Github上的一些操作使用Git命令是无法完成的,因此正常流程就是在网页端进行。等一下,你让程序员用网页进行?果然,有命令行工具可以…...
@Scheduled定时器
Scheduled定时器 一、基本使用二、参数说明fixedDelayfixedRateinitialDelaycron 三、cron 表达式参数说明实用性的案例 四、Scheduled注意事项五、配置文件1、fixedDelay2、fixedRate3、cron 一、基本使用 Scheduled // 由Spring定义,用于将方法设置为调度任务。…...

Xshell+screen解决ssh连接 服务器掉线的问题
Linux screen命令解决SSH远程服务器训练代码断开连接后运行中断_linux screen ssh-CSDN博客 Linux命令之screen命令_linux screen_恒悦sunsite的博客-CSDN博客 使用教程: 这里粗略介绍一下 (1)xshell xftp(xshell点这个&#…...
coding_v3
面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台 数组/字符串 1.LC88【合并两个有序数组】 def solve(nums1, m, nums2, n):p1, p2 m-1, n-1tail m n -1while p1 > 0 or p2 > 0:if p1 -1:nums1[tail] nums2[p2]p2…...

Elasticsearch:什么是余弦相似度?
余弦相似度是数据科学、文本分析和机器学习领域的基本概念。 如果你想知道什么是余弦相似度或者它如何在现实世界的应用程序中使用,那么你来对地方了。 本指南旨在让你深入了解相似性是什么、其数学基础、优点及其在不同领域的各种应用。读完本指南后,你…...
【每日一题Day352】LC1726同积元组 | 哈希表+排列组合
同积元组【LC1726】 给你一个由 不同 正整数组成的数组 nums ,请你返回满足 a * b c * d 的元组 (a, b, c, d) 的数量。其中 a、b、c 和 d 都是 nums 中的元素,且 a ! b ! c ! d 。 思路 求出所有二元组的积及其出现次数,假设某个积出现的次…...
react中在js文件里定义的变量,如何在less文件里去使用该变量
在 React 中,如果在 JS 文件中定义了变量,可以使用 CSS Modules 的方式将变量导出到 LESS 文件中。 以下是示例代码: // index.js import React from react; import styles from ./styles.module.less;const color red;function App() {re…...

TIA博途中通过SCATTER指令实现将字节BYTE拆分成单个位的具体方法示例
TIA博途中通过SCATTER指令实现将字节BYTE拆分成单个位的具体方法示例 例如: 我们想判断某个字节中各个位的状态是1还是0 ,如何实现呢? 这里介绍通过SCATTER指令拆分字节的方法,仅供大家参考。 首先,我们先了解以下SCATTER指令的基本功能和使用方法: 如下图所示,在基本指…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...