首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 网络游戏 >

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

2013-03-13 
【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现 本系列文章由zhmxy555

【Visual C++】游戏开发笔记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现


 
本系列文章由zhmxy555(毛星云)编写,转载请注明出处。  
文章链接: http://blog.csdn.net/zhmxy555/article/details/8657656
作者:毛星云(浅墨)    邮箱: happylifemxy@163.com 


本篇文章中,我们以核心思想为突破口,从原理介绍到一个C++类的写法,一步一步带领大家实现了一个第一人称三维摄像机的C++类。然后我们在这个摄像机类的帮助下,放出了一个几乎贯穿了我们之前学到的所有DirectX相关知识的“三维场景漫游”示例程序,算是对我们之前学的固定功能流水线这套渲染体系的总结。这个“三维场景漫游”示例程序的代码量有一千行,包括了Direct3D初始化,DirectInput输入处理,顶点缓存,光照与材质,文字输出,颜色,纹理贴图,四大变换,网格模型,X文件载入等等知识(当然还有默认被开启的深度缓存)。下面我们先放出一张运行截图:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

 

好吧,正文开始,来卡看如何一步一步实现一个第一人称三维摄像机。


一、一些概述

回想我们之前文章的配套示例程序中,我们都是通过封装的DirectInput类来处理键盘和鼠标的输入,对应地改变我们人物模型的世界矩阵来达到移动物体,改变观察点的效果。其实我们的观察方向乃至观察点都是没有变的,变的只是我们3D人物的位置。举个例子吧,我们之前的示例程序就像是在小区里面安放着的摄像头,位置和观察点都是固定的,变的只是来来往往的人群。贴几张我们之前文章配套的示例程序截图来让大家体会一下:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

之前的示例程序,和三维观察相关的四大矩阵变换我们都封装在了一个叫Matrix_Set()的函数中,而Matrix_Set()通常只用在初始化资源的时候调用一次而已。其中,四大变换中的取景变换,投影变换和视口变换都是一直老老实实地在Matrix_Set()中尽职尽责地呆着,当然,除了世界矩阵变换这个调皮的野孩子之外。世界矩阵由于实时处理的需要,自从第一次介绍完四大变换的时候让他在Matrix_Set()中呆了一次,在此之后的示例程序中我们都是把世界矩阵的设置放在Direct3D_Update()以及Direct3D_Render()之中的。因为我们必须实时地处理输入,让人物模型的世界矩阵能根据我们的输入进行相应的调整,这样才能做出人物模型随着键盘和鼠标的输入,随心所欲地受我们控制的效果出来。


但其实我们之前实现的那种所谓的“视角改变”还是来得不彻底不爽快,说白了就是用D3DXMatrixLookAtLH在资源初始化时固定住视角,在程序运行过程中接收到消息并改变三维人物模型的世界矩阵而已。我们这篇文章的主要讲解点就是手把手地教大家创建出一个可以在三维空间中自由移动的摄像机类,这样利用这个摄像机类写出来的示例程序就像是我们身临其境地在三维空间中真正地自由翱翔一样。浅墨怎么在写上面这段文字的时候,脑海中闪现的竟然是凤凰传奇的那句“在你的心上,自由地飞翔”,黑了- -


我们准备给这个摄像机类取名为CameraClass,这个类的功能非常适合用于三维飞行模拟类游戏(比如王牌空战)以及第一人称视角游戏(比如反恐精英(CS),穿越火线(CF))中。

 

 



二、开始设计摄像机类

首先,给大家介绍一下这个摄像机类的核心思想,那就是用四个分量:右分量(rightvector)、上分量(up vector)、观察分量(lookvector)和位置分量(position vector),来确定一个摄像机相对于世界坐标系的位置和朝向。并根据这四个分量计算出一个取景变换矩阵,完全取代之前的示例程序用D3DXMatrixLookAtLH创建的取景变换矩阵。

在世界坐标系中,这几个分量都是通过向量表示的,并且实际上他们为摄像机定义了一个局部坐标系。


其中,摄像机的左分量、上分量和观察分量定义了摄像机在世界坐标系中的朝向,因此他们也被称为方向向量。在通常的情况下,方向向量都是单位向量(模为1),并且两两之间相互垂直,也就是我们常说的标准正交。

其实,这三个向量我们完全可以理解为三维坐标系中的X,Y,Z轴。

另外,我们需要了解标准正交矩阵的一个重要性质,那就是标准正交矩阵的逆矩阵与其转置矩阵相等。

 【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

用上面提到的右分量(right vector)、上分量(up vector)、观察分量(look vector)和位置分量(position vector)这四个向量来描述摄像机的话,其中的位置分量其实我们可以把他看做一个描述位置的点,那么有用的就还3个分量,每个分量我们可以进行沿着其平移和绕着其旋转两种操作,那么我们可以想到的方式就是2x 3=6种,就是以下这六种运动方式:

 

  ● 沿着右分量平移

  ● 沿着上分量平移

  ● 沿着观察分量平移

  ● 绕着右分量旋转

  ● 绕着上分量旋转

  ● 绕着观察分量旋转

 

另外,我们新创建的这个摄像机类在我们的示例程序中完全“上位”了,取代了之前我们在Matrix_Set()中实现的取景变换和投影变换,所以在我们新的程序中,Matrix_Set()函数将不复存在,单单用一个摄像机类就行了。

根据上面的讲解,我们可以勾勒出这个CameraClass类的轮廓如下:

 

表示位置向量,

  向量【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现表示右向量,

  向量【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现表示上向量,

  向量【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现表示观察向量。

 

我们知道,取景变换所解决的其实就是世界坐标系中的物体在以摄像机为中心的坐标系中如何来表示的问题。这就是说,需要将世界坐标系中的物体随着摄像机一起进行变换,这样摄像机的坐标系就与世界坐标系完全重合了。

如下图所示:

 【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

上面的(a)图到(b)图,是一个平移的过程,而(b)图到(c)图则是一个旋转的过程。另外需要注意的一点是,空间中的物体也应该随着摄像机一同进行变换,这样摄像机中看到景物才没有变化。

我们的目的,就是通过一系列的矩阵变换,得到最终的取景变换矩阵V。

我们要得到取景变换矩阵V,说白了就是能够满足如下的条件: 

pV=(0,0,0) 矩阵V将摄像机移动到世界坐标系的原点

rV=(1,0,0)矩阵V将摄像机的右向量与世界坐标系的x轴重合

uV=(0,1,0)矩阵V将摄像机的上向量与世界坐标系的y轴重合

lV=(0,0,1)矩阵V将摄像机的观察向量与世界坐标系的z轴重合

所以,想得到这个取景变换矩阵V,就是进行了先平移,后旋转,最后综合的三步曲操作,下面我们来对其各个击破:

 

 

1.平移

将摄像机的位置向量p平移到原点就是实现这个式子:pV=(0,0,0),即摄像机的位置分量乘以V之后等于(0,0,0)。那么很简单,我们假设取景变换矩阵V实现平移操作的的中间矩阵为T来简化一下。

而将摄像机的位置向量p平移到原点可以通过把他和它大小相等,方向相反的-p向量做加法轻松实现,即p-p=0,所以描述取景变换中的平移变换部分的T矩阵就是这样写:【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

 

2.旋转

想要让摄像机的各分量与世界坐标系的各轴重合的话,

即满足之前我们列出的这三个式子:

rV=(1,0,0)

uV=(0,1,0)

lV=(0,0,1)

实现起来,求出如下的一个3x3的矩阵A就可以了:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

这里的矩阵A有很多种解法,最科学的解法在这里:

我们一眼就可以看出来A其实就是B矩阵的逆矩阵。而矩阵B刚好是个正交矩阵,我们文章前面就跟大家提到过要用到正交矩阵的这个性质:正交矩阵的逆矩阵等于其转置矩阵。用到我们这里,就是B矩阵的逆矩阵等于B矩阵的转置矩阵,而B矩阵的逆矩阵就是A矩阵,那么就是说A矩阵等于B矩阵的转置矩阵。

那么我们求一下B矩阵的转置矩阵,就是求出A矩阵了。

也就是:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

 

 

3.综合前两步

我们需要把A矩阵扩展成4X4的,然后计算一下两矩阵的值TA=V就行了

即:

 【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

好了,取景变换矩阵V就被我们求出来了。

上面过程看不太懂不要紧,我们看一下最后V矩阵的结果就行了,下面我们实现计算取景变换矩阵的CalculateViewMatrix函数中,其实也就是用了一下最后我们求出的V矩阵的结果而已:

 尺寸是512x512


然后是PS3版《真三国无双6》中的王元姬在3DS Max中的渲染效果图:【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现


也先放一张程序的运行截图:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现


 

本篇文章的配套源代码包含了6个文件,主要用于公共辅助宏定义的D3DUtil.h,用于封装了DirectInput输入控制API的DirectInputClass.h和DirectInputClass.cpp,以及封装了虚拟摄像机类的CameraClass.h和CameraClass.cpp,当还还有核心代码main.cpp。

DirectInputClass.h和DirectInputClass.cpp较之前的文章中依然没有任何修改,依然不再贴出,CameraClass.cpp和CameraClass.h在上面讲解的过程中以及贴出来了,这里也不贴出,我们依然只贴核心代码main.cpp,大家要看得爽的话,源代码在文章末尾有下载链接,下回去用VisualStuido看就行了。下面就是main.cpp的全部代码:


我们开始在键盘上W,A,S,D,I,J,K,L,↑,↓,←,→12个键乱按,加上鼠标乱晃:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

越飞越远:

 【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

摄像机在蓝天之下,草原之上盘旋:

 【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

来张正面照:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

暮然回首,那人却在灯火阑珊处:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

隔近了看草地就是这样的:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

这一袭蓝衣真美:

【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现

更多图片就不贴了,大家在文章末尾下源代码自己拿回去玩去吧~

 

另外我们在Direct3D_Update函数中涉及到了将鼠标限制在某一区域之内的写法,这个其实很好实现的,我们在讲解Windows鼠标消息处理的那篇文章中用到过了。

细心的朋友们应该可以发现,这个程序运行起来还是有一点小问题的,当我们对摄像机进行旋转操作时,视图有时候会发生倾斜,如果对于飞行射击游戏,这样的倾斜是完全没问题的,但是如果是做CF,CS,鬼泣这类的第一人称的游戏的话,那就不应该发生这样的视觉倾斜了。

摄像机之所以发生倾斜是因为我们构造的摄像机的几个分量是两两之间互相垂直的。要想解决这样的问题,就需要保证每次进行摄像机的左右旋转时,是绕着垂直于世界坐标系的轴的向量,而这根轴的向量就是世界坐标系的Y轴。这是我们以后待完成的工作了。

 


文章最后,依旧是放出本篇文章配套源代码的下载:

 

本节笔记配套源代码请点击这里下载:


【浅墨DirectX提高班】配套源代码之十五下载 


 


以上就是本节笔记的全部内容,更多精彩内容,且听下回分解。

浅墨在这里,希望喜欢游戏开发系列文章的朋友们能留下你们的评论,每次浅墨登陆博客看到大家的留言的时候都会非常开心,感觉自己正在传递一种信仰,一种精神。 


文章最后,依然是【每文一语】栏目,今天的句子是: 


我们所有的梦想都可以成真,只要我们有勇气去追求它们。

All our dreams can come true, if we have the courageto pursue them.

 ————沃尔特·迪斯尼


【Visual C++】游戏开发札记四十七 浅墨DirectX教程十五 翱翔于三维世界:摄像机的实现


下周一,让我们离游戏开发的梦想更近一步。

下周一,游戏开发笔记,我们,不见不散。




        

2楼pinbo2010cp1小时前
浅墨老兄,今天我一早就来看你的新的文章了,写的真好,我很喜欢游戏开发,基础还不错,我大三了,毕业后想去家PC 3D游戏公司工作,但是我的学校不是211、985之类的有名的学校,他们那些游戏公司会要我吗?(但我自己有游戏开发这方面的能力)
Re: weijihai52052分钟前
回复pinbo2010cpn最好先去一家公司实习啊,积累经验。
Re: weijihai52051分钟前
回复pinbo2010cpn去面试嘛,怕什么
1楼GRAY_XIAOXIAO1小时前
还以为会做个沙发的呢,没想到还有人更期待呢啊!一个字,继续顶..............这章过后,就更3D啦!嘿嘿

热点排行