本文由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学习笔记【十九】 摄像机的实现的更多相关文章

  1. SharpGL学习笔记(十九) 摄像机漫游

    所谓的摄像机漫游,就是可以在场景中来回走动. 现实中,我们通过眼睛观察东西,身体移动带动眼睛移动观察身边的事物,这也是在漫游. 在OpenGL中我们使用函数LookAt()来操作摄像机在三维场景中进行 ...

  2. python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法

    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法 同一台机器同时安装 python2.7 和 python3.4不会冲突.安装在不同目录,然 ...

  3. Directx11学习笔记【九】 3D渲染管线

    原文:Directx11学习笔记[九] 3D渲染管线 原文地址:http://blog.csdn.net/bonchoix/article/details/8298116 3D图形学研究的基本内容,即 ...

  4. (C/C++学习笔记) 十九. 模板

    十九. 模板 ● 模板的基本概念 模板(template) 函数模板:可以用来创建一个通用功能的函数,以支持多种不同形参,进一步简化重载函数的函数体设计. 语法: template <<模 ...

  5. Directx11学习笔记【九】 【转】 3D渲染管线

    原文地址:http://blog.csdn.net/bonchoix/article/details/8298116 3D图形学研究的基本内容,即给定场景的描述,包括各个物体的材质.纹理.坐标等,照相 ...

  6. Java基础学习笔记十九 IO

    File IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再 ...

  7. Java基础学习笔记十九 File

    IO概述 回想之前写过的程序,数据都是在内存中,一旦程序运行结束,这些数据都没有了,等下次再想使用这些数据,可是已经没有了.那怎么办呢?能不能把运算完的数据都保存下来,下次程序启动的时候,再把这些数据 ...

  8. JavaScript权威设计--跨域,XMLHttpRequest(简要学习笔记十九)

    1.跨域指的是什么? URL 说明 是否允许通信 http://www.a.com/a.jshttp://www.a.com/b.js 同一域名下 允许 http://www.a.com/lab/a. ...

  9. python 学习笔记十九 django深入学习四 cookie,session

    缓存 一个动态网站的基本权衡点就是,它是动态的. 每次用户请求一个页面,Web服务器将进行所有涵盖数据库查询到模版渲染到业务逻辑的请求,用来创建浏览者需要的页面.当程序访问量大时,耗时必然会更加明显, ...

  10. yii2源码学习笔记(十九)

    view剩余代码 /** * @return string|boolean the view file currently being rendered. False if no view file ...

随机推荐

  1. Java 开源博客——B3log Solo 0.6.1 正式版发布了!

    Java 开源博客 —— B3LOG Solo 0.6.1 正式版发布了!欢迎大家下载. 该版本主要是改善细节体验,并加入了一款 Metro 风格的皮肤. 特性 基于标签的文章分类 Ping Goog ...

  2. MySQL触发器 trigger之for each row

    for each row 每行受影响,触发器都运行.叫行级触发器. oracle 触发器中分行级触发器和语句级触发器,可不写for each row,不管影响多少行都仅仅运行一次. mysql不支持语 ...

  3. cocos2d-x游戏开发 跑酷(四) 关联与物理世界

    原创.转载注明出处http://blog.csdn.net/dawn_moon/article/details/21451077 前面一节尽管实现了一个跑动的人物,可是他只不过一个精灵在运行一个跑动的 ...

  4. Fluentd: Open Source Log Management

    Fluentd: Open Source Log Management "Fluentd" is an open-source tool to collect events and ...

  5. android 在你的UI中显示Bitmap - 开发文档翻译

    由于本人英文能力实在有限,不足之初敬请谅解 本博客只要没有注明“转”,那么均为原创,转贴请注明本博客链接链接 Displaying Bitmaps in Your UI 在你的UI中显示Bitmap ...

  6. C语言笔记之结构体

    结构的本质是C语言的一种数据抽象,通俗的说,是基本数据类型的重组. 为什么要重组呢?由于基本数据类型不够用了.为什么不够用了呢?由于须要的信息类型太多了. 这是一个非常大的话题.信息本来是没有什么类型 ...

  7. Android开发之模板模式初探

    模板模式我认为在Android的开发中是最长用到的,基本是随处可见的,认识该模式,有助于我们对Android的源代码及框架有一个更深层次的认识.那什么是模板模式呢,模板模式就是定义一个基本框架,将当中 ...

  8. oracle 之 内存—鞭辟近里(四)

    oracle 之 内存—鞭辟近里(四) 今天是2013-07-11日,首先我非常感谢我的哥们也是我的网友杨工,非常感谢他能在大数据库内帮我执行一下我所需要的信息.就是他说的网络真是一个互助友爱的平台. ...

  9. form表单中的 action=./?> 是什么意思

    ./代表当前目录,?代表查询字符串为空 action="" //一般可以为空的,这里的双引号都要有的,表示提单提交给自己(也就是当前页处理)action="a.php&q ...

  10. 使用 JQueryMobile 点击超链接提示“error loading page” 错误

    使用jquery mobile创建dialog时出现加载错误,“Error Loading Page”. 原因是:jquery mobile页面默认采用ajax方式进行交互,而ajax方式下是不支持f ...