A First-Person Camera

现在开始扩展Camera基类,实现一个第一人称的camera,这种类型的camera可以通过鼠标和键盘进行控制。通过W和S键可以沿着direction向量分别向前和向后移动camera。按键A和D沿着right向量“扫射”(水平移动camera)。使用鼠标控制camera的俯仰和偏航移动:垂直移动鼠标执行camera的俯仰操作,水平移动鼠标执行camera的偏航操作。其中,俯仰是指绕着camera的right向量进行旋转,而偏航则绕着坐标轴的y轴旋转。这是第一人称camera的惯例方式,但是这种camera并不能用于所有的情况,比如,某个游戏中需要camera能在空间中沿着z轴滚动(纵向旋转)。
列表13.3列出了FirstPersonCamera类的头文件。

列表13.3 The FirstPersonCamera.h Header File

#pragma once#include "Camera.h"namespace Library
{class Keyboard;class Mouse;class FirstPersonCamera : public Camera{RTTI_DECLARATIONS(FirstPersonCamera, Camera)public:FirstPersonCamera(Game& game);FirstPersonCamera(Game& game, float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance);virtual ~FirstPersonCamera();const Keyboard& GetKeyboard() const;void SetKeyboard(Keyboard& keyboard);const Mouse& GetMouse() const;void SetMouse(Mouse& mouse);float& MouseSensitivity();float& RotationRate();float& MovementRate();        virtual void Initialize() override;virtual void Update(const GameTime& gameTime) override;static const float DefaultMouseSensitivity;static const float DefaultRotationRate;static const float DefaultMovementRate;        protected:float mMouseSensitivity;float mRotationRate;float mMovementRate;        Keyboard* mKeyboard;Mouse* mMouse;private:FirstPersonCamera(const FirstPersonCamera& rhs);FirstPersonCamera& operator=(const FirstPersonCamera& rhs);};
}

FirstPersonCamera类中包含了表示鼠标和键盘的成员变量,并提供了访问和修改这些变量的公有成员函数。但是在FirstPersonCamera的Initialize()函数中,试图通过查询service container获取鼠标和键盘。用于修改鼠标和键盘变量的成员函数只是简单地重写这些设置,甚至通过指定NULL值禁用鼠标和键盘控制。
还包含了用于表示camera移动和旋转的速率,以及对鼠标控制的响应敏感度。变量“rate”表示在1秒内camera移动或旋转的单位长度。鼠标敏感度支持增强或抑制对鼠标输入的响应。
列表13.4列出FirstPersonCamera实现中最值得关注的部分代码。本书的配套网站上提供了完成的实现代码。

列表13.4 The FirstPersonCamera Class Implementation (Abbreviated)

#include "FirstPersonCamera.h"
#include "Game.h"
#include "GameTime.h"
#include "Keyboard.h"
#include "Mouse.h"
#include "VectorHelper.h"namespace Library
{RTTI_DEFINITIONS(FirstPersonCamera)const float FirstPersonCamera::DefaultRotationRate = XMConvertToRadians(1.0f);const float FirstPersonCamera::DefaultMovementRate = 10.0f;const float FirstPersonCamera::DefaultMouseSensitivity = 100.0f;FirstPersonCamera::FirstPersonCamera(Game& game): Camera(game), mKeyboard(nullptr), mMouse(nullptr), mMouseSensitivity(DefaultMouseSensitivity), mRotationRate(DefaultRotationRate), mMovementRate(DefaultMovementRate){}FirstPersonCamera::FirstPersonCamera(Game& game, float fieldOfView, float aspectRatio, float nearPlaneDistance, float farPlaneDistance): Camera(game, fieldOfView, aspectRatio, nearPlaneDistance, farPlaneDistance), mKeyboard(nullptr), mMouse(nullptr),mMouseSensitivity(DefaultMouseSensitivity), mRotationRate(DefaultRotationRate), mMovementRate(DefaultMovementRate){}FirstPersonCamera::~FirstPersonCamera(){mKeyboard = nullptr;mMouse = nullptr;}void FirstPersonCamera::Initialize(){mKeyboard = (Keyboard*)mGame->Services().GetService(Keyboard::TypeIdClass());mMouse = (Mouse*)mGame->Services().GetService(Mouse::TypeIdClass());Camera::Initialize();}void FirstPersonCamera::Update(const GameTime& gameTime){XMFLOAT2 movementAmount = Vector2Helper::Zero;if (mKeyboard != nullptr){if (mKeyboard->IsKeyDown(DIK_W)){movementAmount.y = 1.0f;}if (mKeyboard->IsKeyDown(DIK_S)){movementAmount.y = -1.0f;}if (mKeyboard->IsKeyDown(DIK_A)){movementAmount.x = -1.0f;}if (mKeyboard->IsKeyDown(DIK_D)){movementAmount.x = 1.0f;}}XMFLOAT2 rotationAmount = Vector2Helper::Zero;if ((mMouse != nullptr) && (mMouse->IsButtonHeldDown(MouseButtonsLeft))){LPDIMOUSESTATE mouseState = mMouse->CurrentState();            rotationAmount.x = -mouseState->lX * mMouseSensitivity;rotationAmount.y = -mouseState->lY * mMouseSensitivity;}float elapsedTime = (float)gameTime.ElapsedGameTime();XMVECTOR rotationVector = XMLoadFloat2(&rotationAmount) * mRotationRate * elapsedTime;XMVECTOR right = XMLoadFloat3(&mRight);XMMATRIX pitchMatrix = XMMatrixRotationAxis(right, XMVectorGetY(rotationVector));XMMATRIX yawMatrix = XMMatrixRotationY(XMVectorGetX(rotationVector));ApplyRotation(XMMatrixMultiply(pitchMatrix, yawMatrix));XMVECTOR position = XMLoadFloat3(&mPosition);XMVECTOR movement = XMLoadFloat2(&movementAmount) * mMovementRate * elapsedTime;XMVECTOR strafe = right * XMVectorGetX(movement);position += strafe;XMVECTOR forward = XMLoadFloat3(&mDirection) * XMVectorGetY(movement);position += forward;XMStoreFloat3(&mPosition, position);Camera::Update(gameTime);}
}

在列表13.4中,首先设置了rotation rate,movement rate,以及mouse sensitivity的默认值。其中使用了DiretXMath库的XMConvertToRadians()函数,用于把degress转换成radians。而XMConvertToDegrees()函数则radians转换成degrees。
在Initialize()函数中演示了从service container中获取mouse和keyboard services的方法。需要注意的是这两个services很可能不存在,在这种情况下返回值为NULL,意味着在该component中不需要键盘的鼠标的功能。否则你应该assert在service container中一定存在这些对象。
Update()函数中包含了FirstPersonCamera类实现代码中的主要部分。首先检查W,S,A和D按键是否按下;如果按下了,就保存值1或-1,用于表示移动的方向。键盘是是一种数字形式的,按键要么按下要么没有按下,因此无法表示一个变量,比如游戏手柄上的模拟触发器或者摇杆。因此,成员变量mMovementRate用于定义camera运动的幅度(尽管理论上可以根据按键被按下的时间缩放幅度值)。
接下来,从鼠标移动(如果鼠标左键被按住不放)过程中获取旋转量。这些数量由成员变量mRotationRate与每一帧的elapsed time相乘得到。在执行乘法运算之前,需要把rotationAmount变量加载到一个XMVECTOR对象中,以便利用SIMD运算的优势。使用elapsed time变量是为了与计算帧率相互独立。
有了旋转数量值之后,就可以构建用于camera的偏航和俯仰旋转矩阵。俯仰矩阵通过调用XMMatrixRotationAxis()函数进行创建,该函数可以构建一个绕任意轴旋转的矩阵。对于俯仰矩阵,是绕着camera的right向量旋转。XMMatrixRotationAxis()函数中使用了XMVectorGetY()函数的返回值作为第二个参数。调用XMVectorGetY()函数可以从一个无法访问内部成员的XMVECTOR对象中获取Y分量值。偏航矩阵则通过调用XMMatrixRotationY()函数进行创建,该函数创建了一个绕y轴旋转的矩阵。在DirectXMath库中还有类似的矩阵用于创建围绕x轴和z轴旋转的矩阵。偏航和俯仰矩阵在传送给Camera::ApplyRotation()函数之前,需要先通过乘法运算进行串联。
接下来,使用成员变量mMovementRate和elapsed time对movementAmount进行乘法运算,并把结果存储到一个XMVECTOR类型的movement向量中。然后把camera的right向量与movement向量的X分量进行乘法运算,得到strafe向量。再把该向量值加到camera的position中。对于forward movement,执行同样的操作过程,把最终的position再写回到成员变量Camera::mPosition中。

总结

本章首先创建了一个基类component用于支持一个虚拟camere的通用部分。然后扩展基类创建了一个支持使用鼠标和键盘控制的第一人称camera。在这个过程中,练习了上一章所讨论的component和services模块,并温习了一些3D camera原理和DirectXMath的使用方法。
到目前为止,所有的工作都已经准备就绪。在下一章,就可以正式开始在屏幕上渲染3D场景了。

Exercises

1. Create an orbit camera similar to the one found in NVIDIA FX Composer. Note: Visualizing
the output of your camera will be difficult without any 3D rendering (which the next chapter
covers). You can use the SpriteBatch/SpriteFont system to output your camera’s data or
employ the Grid component on the companion website. This component renders a
customizable reference grid at the world origin.

1.创建一个与NVIDIA FX Composer中类似的orbit camera。注意:在没有任何3D渲染(下一章才会讲到)的情况要显示camera的输出是很困难的。可以使用SpriteBatch/SpriteFont系统输出camera的相关数据,或者使用本书配套网站上提供的Grid component。这个component可以在渲染一个自定义的参考网格。

本章配套学习代码

http://download.csdn.net/detail/chenjinxian_3d/9584778

首先在FirstPersonCamera的基础上,增加了一个OrbitCamera,并添加了一个Grid组件用于演示各种Camera的操作结果。

A First-Person Camera相关推荐

  1. Camera ISP技术

    Camera ISP技术 ISP图像信号处理 • 1,ISP图像信号处理介绍 • 2,ISP的目的是什么? • 3, ISP的处理流程以及算法 o 3.1镜头的几何变形 o 3.2 镜头渐晕 o 3. ...

  2. Camera系列规格参数

    Camera系列规格参数 FH8858V200: 新一代8M高性能网络摄像机 SoC FH8858V200是新一代面向8M专业型网络摄像机应用的高性能H.265/H.264/JPEG SoC芯片.芯片 ...

  3. IP SOC与Camera ISP

    IP SOC与Camera ISP FH8858V200: 新一代8M高性能网络摄像机 SoC FH8858V200是新一代面向8M专业型网络摄像机应用的高性能H.265/H.264/JPEG SoC ...

  4. 手机与Camera CCM技术发展趋势

    手机与Camera CCM技术发展趋势 CCM是CMOS Camera Module 互补金属氧化物半导体摄像模组的英文缩写,用于各种新一代便携式摄像设备的核心器件,与传统摄像系统相比具有小型化,低功 ...

  5. camera数字降噪(DNR)

    camera数字降噪(DNR) 闭路电视摄像机 无论多么出色和弱光,在黑暗中拍摄视频监控录像时都会不可避免地产生一些噪音.噪声是任何电子通信中不可避免的部分,无论是视频还是音频.本质上是静态的–视频信 ...

  6. Camera噪声问题

    Camera噪声问题 Camera RGB 域的噪声 以上部分属于sensor processing,接下来的部分属于color.luminance processing. gamma gamma是在 ...

  7. camera中LENS和SENSOR的CRA是如何搭配的?

    camera中LENS和SENSOR的CRA是如何搭配的? camera中,lens和sensor的搭配是非常关键的问题.但这两者是如何搭配的呢? 一般在Sensor data sheet中会附有全视 ...

  8. Camera Lens Coating

    Camera Lens Coating Coating Progress 转换镜头,根据要求进行OEM和设计. 光学元件:望远镜.显微镜.相机和数码相机镜头.放大镜头和远摄镜头.定心镜头.投影镜头.投 ...

  9. 摄像头标定GML Camera Calibration

    摄像头标定GML Camera Calibration GML Camera Calibration官方版是一款十分优秀出色的相机标定软件,GML Camera Calibration官方版界面友好, ...

  10. Camera Calibration 相机标定

    Camera Calibration 相机标定 一.相机标定方法 在opencv中提供了一组函数用于实现相机的标定,标定返回的值包括:相机内参矩阵(fx fy xc yc).相机外参矩阵(R t)以及 ...

最新文章

  1. eventsource 服务器发送事件
  2. Java多线程闲聊(三):RxJava
  3. seaborn 教程_使用Seaborn进行数据可视化教程
  4. 深入理解Java虚拟机(第三版)-13.Java内存模型与线程
  5. java jsp公共异常页面_Java如何创建JSP错误页面以处理异常?
  6. Github开源之旅启程:GitHub 上部署网页
  7. HDU5855 Less Time, More profit(最大权闭合图)
  8. 刘元普双生贵子(但行好事,莫问前程)
  9. 【渝粤题库】国家开放大学2021春2721乡镇行政管理题目
  10. html中不显示竖线边框代码,DIV用CSS定义边框为实线,但为什么预览的时候不显示。...
  11. Jie Business Project
  12. 微信订阅号申请与使用
  13. endcap和welltap_ICC布局规划
  14. 安卓日志:拍照、文件读取的问题
  15. 分布式ID之snowflake
  16. 水生植物的Java莫斯
  17. 09-微服务版的单点登陆系统设计及实现(2105~2106)
  18. 安卓项目各文件夹的含义和用处
  19. LVS三种工作方式八种算法
  20. PointNet论文解读和代码解析

热门文章

  1. PHP打印九九乘法表(让输出内容能够完全对齐)
  2. python 计算标准差和平均值
  3. 轨道运营管理专业自荐书_单招面试自我介绍,我报的是:城市轨道交通运营管理,谁可以给我一篇自我介绍的范文啊?谢谢!...
  4. swaggerconfig.java下载_Spring Boot:整合Swagger文档
  5. Unity3d关于Particle System is trying to spawn on a mesh with zero surface area的警告
  6. 全国最全乡镇边界面矢量、中国最全乡镇、街道级行政区划边界矢量数据-shp面数据-wgs84坐标数据分享
  7. 这 14 个短代码,蕴含着丰富的 Python 编程思维
  8. GPS定位平台软件,GPS/UWB/WIFI融合定位,提供开发接口
  9. 关于layui不支持IE兼容模式导出问题
  10. python网络爬虫笔记05:request进阶