【Visual C++】游戏开发笔记四十五 浅墨DirectX教程十三 深度测试和Z缓存专场
本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
文章链接: http://blog.csdn.net/zhmxy555/article/details/8586540
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
在游戏三维场景中,想创造出唯美而具画面感和真实感的画面,常常需要绘制大量的物体。而这些物体之间通常都存在着遮挡的关系,离观察点较远的物体,会因为近处物体的遮挡而不可见,或者只有部分可见。在Direct3D中,微软为我们提供了,深度缓存(或称Z缓存),配套着深度测试技术,来专门实现这种效果。下图是本篇文章配套程序的一个截图,可以发现,利用了深度测试,才能让战士与这个矩形墙面水乳交融,表达出这个战士似乎是从墙面中穿越而出的真实视觉效果。
所以,想要绘制出真实的游戏三维场景,深度测试便是必须之物。
本篇文章里,我们就来探究一下深度测试的方方面面。
一、形象化理解深度测试首先奉上浅墨一直对深度测试的形象化理解。
把深度测试看做在一口井的井口处向井中观望。把所有物体都赋予一个深度值,放到井中来显示。深度越深的物体,离井口就越远。深度越浅的物体,离井口就越近。井表面的深度值为0。离井口近而深度浅的物体,可能会把离井口远的物体遮挡住。最终显示在屏幕上的开启深度测试后的画面,就如在井口处向井中观望里面物体显示出的遮挡与层次的效果一样。
(每次想到这里,浅墨就会联想起美剧《行尸走肉》第二季里面的农场里的那口井,可惜井中掉下去一个行尸,把井水污染了。。。。。)
当然,离井口的深度就是每个物体在世界坐标系中的矩阵的Z坐标值了。
然后,我们看一下深度测试的具体概念。
二、深度测试相关概念讲解
而想要理解深度测试,首先需要理解深度缓冲区。
深度缓冲区,也常常称为Z缓存,是Direct3D中用来存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。是一个只含有特定像素深度信息而不含图像数据的表面,深度缓存为最终绘制的图像中的每一个像素都保留了一个深度值,如果我们绘制屏幕的分辨率为800×600像素的话,那么深度缓存的大小也为800×600。
当Direct3D讲一个场景渲染来目标表面上时,它使用深度缓冲区来决定光栅化后各个多边形的像素前后的遮挡关系,最终决定哪个颜色值会被绘制出来。
所以可以这样理解,Direct3D通过比较当前绘制的像素点的深度和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试的结果为TRUE,就会绘制当前像素,并用当前像素点的深度值来更新一下深度缓冲区,反之,则不予绘制。而在通常情况下,深度缓冲区对应于屏幕大小的一块二维区域。
比如,我们在三维场景中同时绘制了一把长剑和一个盾牌两个立体模型。如果长剑的深度值为30,盾牌的深度值为60( 如取景变换中摄像机的位置矩阵在Z轴上为负值D3DXVECTOR3 vEye(0.0f, 1.0, -100.0f),长剑模型的世界矩阵为(0.0f,0.0f,30.0f),盾牌的世界矩阵为(0.0f,0.0f,60.0f)),那么就是表示长剑的盾牌的前面。Direct3D会首先绘制长剑和盾牌中的其一,而当绘制剩下的那个3D模型的时候,Direct3D会将当前3D模型位于同一位置的像素与已经绘制的像素(如果两者在该位置都有像素的话)进行测试,若当前像素比原来的像素更近(即深度值更小),那么将用当前像素来更新掉原来的像素(前提是),否则不予更新。
当Direct3D将一个场景渲染到目标表面的时候,它使用深度缓冲区决定光栅化后每个多边形的像素前后的遮挡关系,最终决定哪个颜色值被绘制出来,对于一个启用了深度缓冲区的场景进行光栅化操作时,表面上的每个值都要进行深度测试。
三、深度测试使用四步曲
在Direct3D中,使用深度测试有一个四步曲,但是这仅仅是一个按部就班的四步曲,后三步的顺序可以随意,只要在渲染前就可以了。而且由于Direct3D默认开启深度测试,甚至后面三步根本就不需要,就可以在程序中畅通无阻地使用深度测试。
但是,我们本着学知识的态度,依然是要介绍并掌握一下,这样在实现某些特殊渲染效果的时候才有“招”可用。就像现在有了游戏引擎,想要快速做一款游戏的话,完全可以去直接用现成的游戏引擎。但是那毕竟是别人的东西,是别人写出来的东西。如果本着学知识的态度的话,我们应该了解引擎的底层实现,看看游戏引擎是如何一那些做表层学问的人们来得深厚,才能成为真正的技术大牛,实现起游戏画面优化步一步把内容都封装起来的,知其然并知其所以然。这样,日积月累,才会显得功力比来游刃有余,甚至有能力写出属于自己的游戏引擎。
好像一下扯远了,囧,还是拉回来吧。深度测试使用四步曲如下:
1.创建深度缓冲区
2.开启深度测试
3.设置深度测试函数
4.更新深度缓冲区
下面我们分别来详细讲解。
1.创建深度缓冲区
关于深度缓冲区的创建,因为是在Direct3D初始化时顺手创建的,所以我们在之前讲解Direct3D初始化时,在《【Visual C++】游戏开发笔记三十三 浅墨DirectX提高班之二 化腐朽为神奇:DirectX初始化四步曲》一文"Direct3D初始化四步曲之三:填内容"中就有提到。
回忆之前的Direct3D初始化四步曲知识,四步曲之三,其实从头到尾其实就是在填充一个D3DPRESENT_PARAMETERS结构体,下面我们先贴出这个结构体的原型:
枚举类型值
精析
D3DCMP_NEVER
深度测试函数总是返回FALSE
D3DCMP_LESS
测试点深度值小于深度缓冲区中相应值时,返回TRUE,为默认值
D3DCMP_QUAL
测试点深度值等于深度缓冲区中相应值时,返回TRUE
D3DCMP_LESSEQUAL
测试点深度值大于等于深度缓冲区中相应值时,返回TRUE
D3DCMP_GREATER
测试点深度值大于深度缓冲区中相应值时,返回TRUE
D3DCMP_NOTEQUAL
测试点深度值不等于深度缓冲区中相应值时,返回TRUE
D3DCMP_GREATEREQUAL
测试点深度值大于等于深度缓冲区中相应值时,返回TRUE
D3DCMP_ALWAYS
深度测试函数总是返回TRUE
D3DCMP_FORCE_DWORD
这个枚举值一般不用,保证将D3DCMPFUNC枚举类型编译为32位
一般我们都将深度测试函数设为D3DCMP_LESS,表示当测试点深度值小于深度缓冲区中相应值时,通过测试并绘制相关像素,这样没有被遮挡的物体才显示,被遮挡住的物体是不显示的。
这步里面也就是一句SetRenderState代码:
开启深度测试后,这个战士看起来还是非常帅气的,但是接下来我们来看看一些开启/关闭深度测试的对比图。
开启了深度测试的状态:
关闭深度测试的状态:
看到辫子的异样了吗,还有一直横亘在战士身前的讨厌的单色矩形。
再看一组吧。
开启了深度测试的状态:
关闭了深度测试的状态:
大家看到这奇葩的头发了吗?这货这么帅气,一世英名就毁在没开深度测试上面了。。。。。
大家可要注意了,我们在写3D游戏程序时难免会载入很多3D模型,如果不开深度测试的话,且不说不同3D模型之间不会有正确的遮挡显示效果,就连3D模型本身都会缺失真实的深度感,看起来怪模怪样。
另外,不开深度测试,后绘制的物体永远显示在先绘制物体的前面,这是需要注意一个常识。这正好解释了为什么后绘制的单色墙为什么在不开深度测试的情况下永远横艮于战士之前。
也就是说,如果不开深度测试,如果两个物体在某个像素点有交集,那么后绘制的物体会一直挡住先绘制的物体。就像我们在一张白纸上画画,先画的人物就在最底层,后画的人物会覆盖住先画的人物的笔迹一样。
最后提一点,Direct3D渲染五步曲的第一步里面用到的那个Clear方法里面也有和深度测试相关的内容,大家可以留心一下。
好了。这篇文章也就告一段落了。下篇文章如果不出意外,应该会开始讲解可编程渲染流水线了。Direct3D还真是博大精深,一时半会儿还真是讲不完。
另外。从本篇文章的配套程序开始,都会配上循环播放的背景音乐,通过PlaySoundAPI函数来实现,在Main函数中加一句这个即可:
文章最后,依旧是放出本篇文章配套源代码的下载:
本节笔记配套源代码请点击这里下载:
(下载频道抽了,上传的资源不及时显示,下载链接稍后放出)
以上就是本节笔记的全部内容,更多精彩内容,且听下回分解。
浅墨在这里,希望喜欢游戏开发系列文章的朋友们能留下你们的评论,每次浅墨登陆博客看到大家的留言的时候都会非常开心,感觉自己正在传递一种信仰,一种精神。
文章最后,依然是【每文一语】栏目,今天的句子是:
假如一个人寿命为60岁,那么他总共有21900天。一生时间的用途分别为:睡20年(7300天);吃饭6年(2190天);穿衣和梳洗5年(1825天);生病3年(1095天);打电话1年(365天);照镜子70天;抹鼻涕10天。最后只剩下3205天,即8年又285天,做你真正想做的事情。珍惜大好时光吧~
下周一,让我们离游戏开发的梦想更近一步。
下周一,游戏开发笔记,我们,不见不散。