Directx11学习笔记【十九】 摄像机的实现
本文由zhangbaochong原创,转载请注明出处:http://www.cnblogs.com/zhangbaochong/p/5785100.html
之前为了方便观察场景,我们采用的方法是鼠标控制旋转视角和镜头拉伸,但是观察点依然限制在一个球面内,目标点也始终为坐标原点。为了能够自由的从各个角度、各个位置观察场景,实现一个第一人称摄像机是必不可少的。
1.摄像机视角矩阵推导
摄像机在空间有着特定的位置及朝向,它所观察到的物体取决于物体与摄像机的相对位置。为了表示摄像机位置,我们可以使用一个3维向量;对于摄像机的的朝向,可以使用三个相互垂直的坐标轴的惟一地确定,即右、上、前三个方向。以摄像机为参考点,即以之为原点来观察物体。因此,视角变换,本质上即把场景中所有的物体、连同摄像机,使用相同的变换,使得变换后摄像机位于场景的原点,且其三个坐标轴(右、上、前)与世界坐标系的X、Y、Z坐标轴重合。这时,变换后的所有物体的坐标值,即变为与摄像机的相对位置了。
我们把摄像机位置用[ px, py, pz ]来表示,其三个表示朝向的坐标轴分别表示为[ Rx, Ry, Rz ]、[ Ux, Uy, Uz ]、[ Lx, Ly, Lz ]。而视角变换的目的即把它的这些参数转换为:位置[ 0, 0, 0 ],以及三个坐标轴:[ 1, 0,0 ]、[ 0, 1, 0 ]、 [ 0, 0, 1 ]。由于有平移和旋转同时存在,因此该变换可以分为两步进行:1. 平移到原点;2. 旋转。
平移操作很简单,即把[Px,Py, Pz,1]移回到原点,矩阵为:

旋转操作需要点小技巧。我们的目的是把三个轴分别转换成[1,0,0]、[0,1,0]、[0,0,1],令旋转矩阵为M,可以表示为如下所示:

由上面式子可以看出,左边的矩阵与M相乘后变为右边的单位矩阵。这正是关键所在!我们知道,一个矩阵与它的逆矩阵相乘结果为单位矩阵,因此,要求矩阵M,我们可以求它的逆矩阵。求逆矩阵需要大量的计算,有没有更好的方法?
答案是有的!有一点需要知道,正交矩阵的逆矩阵等于它的转置矩阵。而正交矩阵的一个特点:正交矩阵的每行(每列)相互垂直。回来看下我们左边的矩阵,每行分别对应了摄像机的三个相互垂直的轴,显然它正是正交矩阵!好了,这下M的计算,就变成求它的转置矩阵了。而转置的计算再简单不过了,直接给出:

现在,两步需要的矩阵都有了,那最终的视角矩阵即两次转换的乘积,当然,由于平移变换的存在,现在要在4x4矩阵了,如下所示:

结果中P*R、P*U、P*L分别是位置向量与R、U、L三个向量的点积,通过查看左边的矩阵相乘很容易看出来。
OK, 视角矩阵的推导完毕,现在我们知道:
任一时刻,通过摄像机位置P(Px,Py,Pz),以及三个坐标轴(R,U,L),可以得到当前的视角变换矩阵:

2.摄像机类的实现
摄像机参数主要有:位置、三个坐标轴、视角矩阵、投影矩阵、生成投影矩阵的四个参数(视角大小,宽高比,近裁剪平面,远裁剪平面)。
实现的功能比较简单,主要实现移动(前后左右移动),视角旋转(左右摇头,上下点头)。
由于代码比较简单,故不再详细说明了:
#pragma once #include <Windows.h>
#include <DirectXMath.h>
#include <cmath>
class Camera
{
public:
Camera(); //设置摄像机位置
void SetPosition(float x, float y, float z) { m_position = DirectX::XMFLOAT3(x, y, z); }
void SetPosition(DirectX::FXMVECTOR pos) { DirectX::XMStoreFloat3(&m_position, pos); } //获得摄像机位置方向等相关参数
DirectX::XMFLOAT3 GetPosition() const { return m_position; }
DirectX::XMFLOAT3 GetRight() { return m_right; }
DirectX::XMFLOAT3 GetLook() { return m_look; }
DirectX::XMFLOAT3 GetUp() { return m_up; } DirectX::XMVECTOR GetPosotionXM() const { return DirectX::XMLoadFloat3(&m_position); }
DirectX::XMVECTOR GetRightXM() const { return DirectX::XMLoadFloat3(&m_right); }
DirectX::XMVECTOR GetLookXM() const { return DirectX::XMLoadFloat3(&m_look); }
DirectX::XMVECTOR GetUpXM() const { return DirectX::XMLoadFloat3(&m_up); } //获得投影相关参数
float GetNearZ() const { return m_nearZ; }
float GetFarZ() const { return m_farZ; }
float GetFovY() const { return m_fovY; }
float GetFovX() const { return atan(m_aspect * tan(m_fovY * 0.5f)) * .f; }
float GetAspect() const { return m_aspect; } //获得视图投影矩阵
DirectX::XMMATRIX GetView() const { return DirectX::XMLoadFloat4x4(&m_view); }
DirectX::XMMATRIX GetProj() const { return DirectX::XMLoadFloat4x4(&m_proj); }
DirectX::XMMATRIX GetViewProj() const { return XMLoadFloat4x4(&m_view) * XMLoadFloat4x4(&m_proj); } //设置投影矩阵
void SetLens(float fovY, float aspect, float nearz, float farz); //设置视角矩阵
void LookAtXM(DirectX::FXMVECTOR pos, DirectX::FXMVECTOR lookAt, DirectX::FXMVECTOR worldUp);
void LookAt(const DirectX::XMFLOAT3& pos, const DirectX::XMFLOAT3& lookAt,
const DirectX::XMFLOAT3& worldUp); //基本操作
void Walk(float dist); //前后行走
void Strafe(float dist); //左右平移
void Pitch(float angle); //上下点头
void RotateY(float angle); //左右摇头 //更新矩阵
void UpdateViewMatrix();
private:
DirectX::XMFLOAT3 m_right; //位置和三个坐标轴
DirectX::XMFLOAT3 m_up;
DirectX::XMFLOAT3 m_look;
DirectX::XMFLOAT3 m_position; float m_aspect; //投影相关参数
float m_fovY;
float m_nearZ;
float m_farZ; DirectX::XMFLOAT4X4 m_view; //视角矩阵
DirectX::XMFLOAT4X4 m_proj; //投影矩阵
};
#include "Camera.h"
using namespace DirectX; Camera::Camera():
m_position(.f,.f,.f),
m_look(.f,.f,.f),
m_up(.f,.f,.f),
m_right(.f,.f,.f)
{
SetLens(0.25*XM_PI, .f / , .f, .f);
} //设置投影矩阵
void Camera::SetLens(float fovY, float aspect, float nearz, float farz)
{
m_fovY = fovY;
m_aspect = aspect;
m_nearZ = nearz;
m_farZ = farz;
XMMATRIX P = XMMatrixPerspectiveFovLH(fovY, aspect, nearz, farz);
XMStoreFloat4x4(&m_proj, P);
} //设置视角矩阵
void Camera::LookAtXM(DirectX::FXMVECTOR pos, DirectX::FXMVECTOR lookAt, DirectX::FXMVECTOR worldUp)
{
XMVECTOR look = XMVector3Normalize(lookAt - pos);
XMVECTOR right = XMVector3Normalize(XMVector3Cross(worldUp, look));
XMVECTOR up = XMVector3Cross(look, right); XMStoreFloat3(&m_position, pos);
XMStoreFloat3(&m_look, look);
XMStoreFloat3(&m_right, right);
XMStoreFloat3(&m_up, up);
} void Camera::LookAt(const DirectX::XMFLOAT3& pos, const DirectX::XMFLOAT3& lookAt,
const DirectX::XMFLOAT3& worldUp)
{
XMVECTOR p = XMLoadFloat3(&pos);
XMVECTOR l = XMLoadFloat3(&lookAt);
XMVECTOR u = XMLoadFloat3(&worldUp); LookAtXM(p, l, u);
} //前后行走
void Camera::Walk(float dist)
{
XMVECTOR pos = XMLoadFloat3(&m_position);
XMVECTOR look = XMLoadFloat3(&m_look);
pos += look * XMVectorReplicate(dist); //XMVectorReplicate(x)返回XMVector(x,x,x,x)
XMStoreFloat3(&m_position, pos);
} //左右平移
void Camera::Strafe(float dist)
{
XMVECTOR pos = XMLoadFloat3(&m_position);
XMVECTOR right = XMLoadFloat3(&m_right);
pos += right * XMVectorReplicate(dist); XMStoreFloat3(&m_position, pos);
} //上下点头
void Camera::Pitch(float angle)
{
XMMATRIX rotation = XMMatrixRotationAxis(XMLoadFloat3(&m_right), angle);
//向量矩阵相乘
XMStoreFloat3(&m_up, XMVector3TransformNormal(XMLoadFloat3(&m_up), rotation));
XMStoreFloat3(&m_look, XMVector3TransformNormal(XMLoadFloat3(&m_look), rotation));
} //左右摇头
void Camera::RotateY(float angle)
{
XMMATRIX rotation = XMMatrixRotationY(angle); XMStoreFloat3(&m_right, XMVector3TransformNormal(XMLoadFloat3(&m_right), rotation));
XMStoreFloat3(&m_up, XMVector3TransformNormal(XMLoadFloat3(&m_up), rotation));
XMStoreFloat3(&m_look, XMVector3TransformNormal(XMLoadFloat3(&m_look), rotation));
} //更新视角矩阵
void Camera::UpdateViewMatrix()
{
XMVECTOR r = XMLoadFloat3(&m_right);
XMVECTOR u = XMLoadFloat3(&m_up);
XMVECTOR l = XMLoadFloat3(&m_look);
XMVECTOR p = XMLoadFloat3(&m_position); r = XMVector3Normalize(XMVector3Cross(u, l));
u = XMVector3Normalize(XMVector3Cross(l, r));
l = XMVector3Normalize(l); float x = -XMVectorGetX(XMVector3Dot(p, r));
float y = -XMVectorGetX(XMVector3Dot(p, u));
float z = -XMVectorGetX(XMVector3Dot(p, l)); XMStoreFloat3(&m_right, r);
XMStoreFloat3(&m_up, u);
XMStoreFloat3(&m_look, l);
XMStoreFloat3(&m_position, p); m_view(, ) = m_right.x; m_view(, ) = m_up.x; m_view(, ) = m_look.x; m_view(, ) = ;
m_view(, ) = m_right.y; m_view(, ) = m_up.y; m_view(, ) = m_look.y; m_view(, ) = ;
m_view(, ) = m_right.z; m_view(, ) = m_up.z; m_view(, ) = m_look.z; m_view(, ) = ;
m_view(, ) = x; m_view(, ) = y; m_view(, ) = z; m_view(, ) = ;
}
3.摄像机的使用
例子是上个demo,只不过是把控制视角的方式换成了刚实现的摄像机而已。
代码下载:http://files.cnblogs.com/files/zhangbaochong/CameraDemo.zip
Directx11学习笔记【十九】 摄像机的实现的更多相关文章
- SharpGL学习笔记(十九) 摄像机漫游
所谓的摄像机漫游,就是可以在场景中来回走动. 现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游. 在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行 ...
- python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法
python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...
- Directx11学习笔记【九】 3D渲染管线
原文:Directx11学习笔记[九] 3D渲染管线 原文地址:http://blog.csdn.net/bonchoix/article/details/8298116 3D图形学研究的基本内容,即 ...
- (C/C++学习笔记) 十九. 模板
十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...
- Directx11学习笔记【九】 【转】 3D渲染管线
原文地址:http://blog.csdn.net/bonchoix/article/details/8298116 3D图形学研究的基本内容,即给定场景的描述,包括各个物体的材质.纹理.坐标等,照相 ...
- Java基础学习笔记十九 IO
File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...
- Java基础学习笔记十九 File
IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...
- JavaScript权威设计--跨域,XMLHttpRequest(简要学习笔记十九)
1.跨域指的是什么? URL 说明 是否允许通信 http://www.a.com/a.jshttp://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a. ...
- python 学习笔记十九 django深入学习四 cookie,session
缓存 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求一个页面,Web服务器将进行所有涵盖数据库查询到模版渲染到业务逻辑的请求,用来创建浏览者需要的页面.当程序访问量大时,耗时必然会更加明显, ...
- yii2源码学习笔记(十九)
view剩余代码 /** * @return string|boolean the view file currently being rendered. False if no view file ...
随机推荐
- Java 开源博客——B3log Solo 0.6.1 正式版发布了!
Java 开源博客 —— B3LOG Solo 0.6.1 正式版发布了!欢迎大家下载. 该版本主要是改善细节体验,并加入了一款 Metro 风格的皮肤. 特性 基于标签的文章分类 Ping Goog ...
- MySQL触发器 trigger之for each row
for each row 每行受影响,触发器都运行.叫行级触发器. oracle 触发器中分行级触发器和语句级触发器,可不写for each row,不管影响多少行都仅仅运行一次. mysql不支持语 ...
- cocos2d-x游戏开发 跑酷(四) 关联与物理世界
原创.转载注明出处http://blog.csdn.net/dawn_moon/article/details/21451077 前面一节尽管实现了一个跑动的人物,可是他只不过一个精灵在运行一个跑动的 ...
- Fluentd: Open Source Log Management
Fluentd: Open Source Log Management "Fluentd" is an open-source tool to collect events and ...
- android 在你的UI中显示Bitmap - 开发文档翻译
由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接 Displaying Bitmaps in Your UI 在你的UI中显示Bitmap ...
- C语言笔记之结构体
结构的本质是C语言的一种数据抽象,通俗的说,是基本数据类型的重组. 为什么要重组呢?由于基本数据类型不够用了.为什么不够用了呢?由于须要的信息类型太多了. 这是一个非常大的话题.信息本来是没有什么类型 ...
- Android开发之模板模式初探
模板模式我认为在Android的开发中是最长用到的,基本是随处可见的,认识该模式,有助于我们对Android的源代码及框架有一个更深层次的认识.那什么是模板模式呢,模板模式就是定义一个基本框架,将当中 ...
- oracle 之 内存—鞭辟近里(四)
oracle 之 内存—鞭辟近里(四) 今天是2013-07-11日,首先我非常感谢我的哥们也是我的网友杨工,非常感谢他能在大数据库内帮我执行一下我所需要的信息.就是他说的网络真是一个互助友爱的平台. ...
- form表单中的 action=./?> 是什么意思
./代表当前目录,?代表查询字符串为空 action="" //一般可以为空的,这里的双引号都要有的,表示提单提交给自己(也就是当前页处理)action="a.php&q ...
- 使用 JQueryMobile 点击超链接提示“error loading page” 错误
使用jquery mobile创建dialog时出现加载错误,“Error Loading Page”. 原因是:jquery mobile页面默认采用ajax方式进行交互,而ajax方式下是不支持f ...