【Visual C++】游戏开发笔记四十六 浅墨DirectX教程十四 模板测试与镜面特效专场
本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
文章链接: http://blog.csdn.net/zhmxy555/article/details/8632184
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
上篇文章中我们讲解了深度缓存的方方面面,有不少朋友都评论或者发邮件跟浅墨说接下来讲一讲和深度缓存情同手足的模板缓存相关的技术,于是,这篇文章就诞生了。这篇文章可是费了浅墨不少脑细胞啊,写了周末整整一天,一万多字,从早上11点写到晚上12点- -。
这篇文章的主角模板技术整体来说比深度测试技术难理解,文中有不懂的地方大家可以多看几遍,最好是结合文章后面我们提供的配套示例程序的代码一起理解。好了,我们开始正题吧。
一、对模板技术中概念的理解
想要学习模板技术,有两个首先的概念需要理解,即模板缓存与模板测试。
1、模板缓存
首先我们了解什么是模板缓存。
模板缓存(stencil buffer)是一个用于专门用于制作特效的离屏(off-screen)缓存。模板缓存的分辨率与之前讲过的后台缓存和深度缓存的分辨率完全相同,模板缓存的像素也后台缓存、深度缓存中的像素一一对应。正所谓人如其名,模板缓存,模板也,它能让我们动态地、有针对性地决定是否将某个像素写到后台缓存中。
比如,我们稍后会讲到的实现镜面特效,我们只需在镜子所在的那个特定的平面区域(注意是一片区域,不是整个平面)中绘制出最终幻想里的游戏角色“雷霆”的镜像,而不在镜子之外做多余的绘制。这个时候,模板缓存就可以派上用场了。
其实,模板缓存可以理解为Direct3D中的一个专门来做特效的工具缓存而已。
2、模板测试
在运用模板技术来进行特效的绘制时,需要精确到每个像素。我们会根据每个像素的模板缓存的值,进行一些检查,最后得出这个像素是否需要绘制的结论,从而实现一些特殊的效果。而这个检查的过程,就是模板测试。
在Direct3D中,我们常常利用模板测试来实现一些特殊的效果。比如图形的合成、镜面特效、消融、淡入淡出、轮廓的显示、侧影和实时阴影等等特效。
二、模板测试精细讲解
解释完基本概念,下面我们就来看看模板测试到底如何使用。
首先说一点,缓冲区和缓存是一个概念,都是根据buffer这个单词译过来的,只是根据语境的选择,有时候我们写作“缓冲区”,有时候我们写作“缓存”而已。
1.创建模板缓冲区
首先需要注意,Direct3D哎创建深度缓冲区的同时创建了模板缓冲区,而且将深度缓冲区的一部分作为模板缓冲区使用,就好像上帝(Direct3D)在造人时先创造了亚当(深度缓冲区),再从亚当的身上取一块肋骨,于是这就有了夏娃(模板缓冲区)。笑:D
既然他们是同时创建的。那么他们如何创建相关的讲解也就是八九不离十。那么根据我们上篇文章《【Visual C++】游戏开发笔记四十五 浅墨DirectX教程十三 深度测试和Z缓存专场》里讲到的,深度缓冲区和模板缓冲区都是在Direct3D初始化时顺手创建的,我们在之前讲解Direct3D初始化时,在《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位
对于目标表面上的每一个像素,Direct3D首先将应用程序定义的模板参考值和模板掩码进行逐位与运算,然后将当前测试的像素在模板缓冲区中的数值与模板掩码进行逐位与运算,最后根据模板比较函数对得到的结果进行比较,如果模板测试成功,也就是测试结果为true,那么该像素就被写入后台缓存;如果模板测试失败的话,也就是测试结果为false,那么该像素就不会被写入后台缓存,也不会被写入深度缓存。
另外,上面我们讲到的渲染状态 D3DRS_STENCILFAIL、D3DRS_STENCILZFAIL、D3DRS_STENCILPASS定义了模板测试、深度测试失败或者通过时进行的模板操作,他们也是在一个枚举类型中取值,这个枚举类型是D3DSTENCILOP,这个枚举类型的定义如下:
枚举类型值(模板操作)
精析
D3DSTENCILOP_KEEP
是默认的选项,表示不更新模板缓冲区中的值
D3DSTENCILOP_ZERO
将模板缓冲区中的值设为0
D3DSTENCILOP_REPLACE
用模板参考值替换模板缓冲区中对应的值
D3DSTENCILOP_INCRSAT
增加模板缓冲区中的对应数值,如果大于最大值,则等于最大值
D3DSTENCILOP_DECRSAT
减小模板缓冲区中的对应数值,如果小于最小值,则等于最小值
D3DSTENCILOP_INVERT
倒置模板测试区中的对应值的数据位
D3DSTENCILOP_INCR
增加模板缓冲区中对应数值,如果大于最大值,则等于0
D3DSTENCILOP_DECR
减小模板缓冲区中对应数值,如果小于0,则等于最大值
D3DSTENCILOP_FORCE_DWORD
这个枚举值一般不用,用于保证将D3DCMPFUNC枚举类型编译为32位
呼,这些参数终于介绍完了,再多介绍几个的话,恐怕大家就要到欧洲来去医院探望浅墨了- -。
4.对模板测试的一些理解
模板测试使用模板参考值、模板掩码、模板比较函数和当前像素在模板缓冲区中的模板值作为参数,判断某个像素是否将被写入到后台缓冲区中。模板测试的表达式是这样的:
其中的ref表示模板参考值,mask表示模板掩码,value表示模板缓冲中的值,OP表示模板比较函数,而符号“&”则表示模板值或模板参考值与模板掩码进行按位的与计算。
在Direct3D进行模板测试前,我们需要对模板测试的模板参考值、模板掩码和模板比较函数进行下设置。需要注意的是,模板参考值的默认值为0。当然,我们也可以自己亲手设置,用的依然是那个号称万能的SetRenderState。第一个参数参数渲染状态我们设为D3DRS_STENCILREF,而第二个参数就填一个数值(最好是填16进制的),表示需要的模板参考值。
举个小实例,下面这段代码我们就把模板参考值设为了1:
好了,我们继续来讲。
想要在Direct3D程序中实现镜面特效,首先需要计算出物体先归于特定平面中的镜像,而这个过程可以通过镜面成像的数学原理来进行计算,然后通过模板技术将物体的镜像正确地绘制到所指定的平面(镜面)中。
先来看一下镜面成像的原理图:
上图中,假设空间中有任意一点q,那么它相对于平面所成的像就为q'。
而已知q点的坐标,求出q'的坐标,就实现了我们镜面成像的目的。
其实,我们只要通过数学知识,求出q点到q'点的镜像变换矩阵就可以了,这样知道q点,根据镜像变换矩阵,就可以求出q'来。
这个镜像变换矩阵的求法,微软早就为我们准备好了,那就是D3DX库中的D3DXMatrixReflect函数。我们在MSDN中查到D3DXMatrixReflect的声明如下:
好吧,我们开始讲解。
Ⅰ. 清空模板缓存
第一步,在清空模板缓存,并将模板缓存的值都设为0,用Clear方法完成。这一步我们在Direct3D_Render()函数中渲染五步曲的第一步清屏里面已经做了,代码就是这样:
这个示例程序中的背景音乐用的是《仙剑奇侠传3问情篇》中的一首战斗音乐,有些小闹腾的。
文章最后,依旧是放出本篇文章配套源代码的下载:
本节笔记配套源代码请点击这里下载:
【浅墨DirectX提高班】配套源代码之十四下载
以上就是本节笔记的全部内容,更多精彩内容,且听下回分解。
浅墨在这里,希望喜欢游戏开发系列文章的朋友们能留下你们的评论,每次浅墨登陆博客看到大家的留言的时候都会非常开心,感觉自己正在传递一种信仰,一种精神。
文章最后,依然是【每文一语】栏目,今天的句子是:
想一千次,不如去做一次。华丽的跌倒,胜过无谓的徘徊。
下周一,让我们离游戏开发的梦想更近一步。
下周一,游戏开发笔记,我们,不见不散。