关于Direct3D的深度测试

什么是深度测试?可以设想在一个比较复杂的游戏场景中,通常需要绘制多个物体,这些物体之间必然会存在遮挡关系,离观察点较远的物体会因为近处物体的遮挡而不可见或只有部分可见,Direct3D中提供深度测试功能来实现这种效果。
深度测试与Alpha混合有什么关系?其实在现实世界中遮挡也分不同的情况,如果遮挡另一个物体的物体是半透明的,那么此时在场景中观察到的被遮挡物体并非是不可见的,也并非是正常可见的,而是“模糊可见”(请允许让我这样形容,你一定可以理解我要表达的意思)的。然而,深度测试只是提供了“可见”和“不可见”两种测试结果,对于这种半透明遮挡便无能为力,此时便可以用Alpha混合技术产生这种半透明遮挡的效果,有关半透明技术的使用会在下一篇文章中详细记录。
下面用Photoshop软件模拟了一下上述表达的意思,图中黑色mask的透明度依次是100%、80%、0%,很明显中间80%的透明度中的红色三角形不能简单的用“可见”与“不可见”来表达。

深度测试技术的实现

要理解深度测试,首先需要理解深度缓冲区。深度缓冲区是Direct3D用来,存储绘制到屏幕上的每个像素点的深度信息的一块内存缓冲区。当Direct3D将一个场景渲染到目标表面上时,它使用深度缓冲区来决定各个图形的像素的前后遮挡关系,最终决定那个颜色值会被绘制出来。

可以结合上面深度测试的原理图来理解这一过程。Direct3D通过比较当前绘制的像素点和对应深度缓冲区的点的深度值来决定是否绘制当前像素。如果深度测试结果为true,则绘制当前像素,并用当前像素点的深度值来更新深度缓冲区,反之则不予绘制。通常情况下,深度缓冲区对应于屏幕大小的一块二维 区域。对于一个启用了深度缓冲区的场景进行光栅化处理时,渲染表面上的每个点都要进行深度测试。
拿上图中的绘制情景梳理这一过程:在深度测试开始时,深度缓冲区的深度值被设置为该场景可能出现的最大值。渲染表面的颜色值被设置为背景颜色值。首先渲染紫色的三角形,Direct3D首先会判断此三角形像素绘制的位置(x,y)是否出现在渲染表面上,如果出现,测试此像素点的深度值,看它是否小于存储在深度缓冲区中的深度值,因为开始的时候,深度缓冲区被设置为场景中可能的最大值,所以,此处紫色三角形像素的深度值要小于深度缓冲区中原有的深度值,则该深度值被更新到深度缓冲区当中,并将渲染表面上当前点的颜色值替换为紫色;绘制黄色五边形的时候,深度测试进行到两个图形重合的部分,由上面的分析过程可知,黄色的深度值比紫色的深度值更小,所以深度缓冲区会被更新成黄颜色所处的深度,而屏幕上两图形重合的部分也只会渲染成黄色。这样,从我们用户看来,后面紫色的三角形便被前面黄色的五边形“遮挡”了!
注意:在上面的叙述中涉及到两个操作(已经被设置为粗体表示):比较(compare)和更新(update)。这两个操作 在使用深度测试的程序代码中会有所体现。

在程序中使用深度测试

由深度测试技术概念的分析中可以概括出使用深度测试的基本步骤:

(1)创建深度缓冲区
(2)激活深度测试
(3)设置深度测试函数
(4)更新深度缓冲区
(5)清除深度缓冲区

下面简单的分析一下上述每个基本步骤的作用和使用方法。
(1)创建深度缓冲区
若要在Direct3D图形程序中使用深度测试,首先必须在创建Direct3D渲染设备时创建深度缓冲区。还记得在最开始创建Direct3D设备的时候,我们使用了一个用来描述Direct3D设备信息的结构D3DPRESENT_PARAMETERS,这个结构包含了不少成员,这里不再一一列出,如果还不是很清晰的话,可以到之前的文章中看看。
之前的定义是这样的:

而此处我们为了创建深度缓冲区需要添加两条语句:

这里第一条语句表明了由Direct3D创建并管理一个深度缓冲区;第二条语句说明深度缓冲区中每一个像素的深度值由16位二进制表示。使用这两个语句来初始化d3dpp结构,并创建的Direct3D设备便具有了深度缓冲区结构。
(2)激活深度测试
通过“创建深度缓冲区”的步骤,我们的Direct3D设备拥有了进行深度测试的数据结构,但是我们还需要将其激活,类似于设置其他渲染状态,用我们的老朋友SetRenderState()函数来激活深度测试:

这里第一个参数设置为D3DRS_ZENABLE,第二个蚕食设置为TRUE,激活深度测试。
注意:默认是激活状态,此语句可以省略,但是为了能让大家明白设置整个深度测试的完整过程,此处并未省略。
(3)设置深度测试函数
还记得之前我让你格外注意的两个操作吗?其中一个便是“比较”操作。这个操作的存在相当自然,设想一下,在绘制那个黄色五边形的时候,我们需要进行深度测试,而具体的操作就是把黄色像素点和之前已经存在过的紫色像素点的深度做一个比较,相应的会返回一个BOOL类型的比较结果,如果是TRUE我们就会进行下一步的“更新”操作……等等。那么什么时候应该返回TRUE呢?因为我们已知的比较操作就有很多:等于、小于、大于、大于等于、小于等于……。你可能会想,因为前面的物体会遮挡后面的物体,那么当欲渲染的像素深度小于深度缓冲区中同一位置已经存在的像素深度的时候,我们应该返回TRUE从而让Direct3D来更新它。没错!这说明你并没有被这些概念冲昏头脑,目的仍然很明确:就是为了营造游戏世界中的遮挡效果!但是,游戏世界中也会存在其他一些情景,如果角色可以透视呢?所以,仅仅定义小于操作便会捉襟见肘,为此Direct3D提供了设置深度测试过程中的比较函数的方法。
接下来仍然调用LPDIRECT3DDEVICE9::SetRenderState()函数设置深度测试的函数,第一个参数设置为D3DRS_ZFUNC,第二个参数设置为想要设置的深度测试函数,它属于D3DCMPFUNC枚举类型,定义如下:

D3DCMPFUNC枚举类的详细说明如下:

通常情况下,深度测试函数设置为D3DCMP_LESS,表示当前测试点深度值小于深度缓冲区中相应的值时,通过测试并绘制相关像素,这样没有被遮挡的物体才显示,而被遮挡的物体就不显示。此处我们使用下面的代码语句:

(4)更新深度缓冲区
上面强调的第二个操作便是“更新”操作,即:设置了深度测试函数后,还需要测试深度测试成功时对缓冲区如何操作,是保持原来的深度值,还是用当前像素深度值更新对应的数值。在程序中我们用如下代码来控制:

表示如果通过深度测试,则用当前像素的深度值更新深度缓冲区中对应的数值。这是最常用的设置,也是默认设置。
(5)清除深度缓冲区
清除深度缓冲区与之前的清除后台颜色缓冲区在同一个函数中实现(IDirect3DDevice9::Clear),表示清除当前的后台缓冲区,从而为下一场景的渲染做准备。调用方式如下:

这里,第三个参数增加了D3DCLEAR_ZBUFFER标志位,表示在每次清除后台缓冲区时,需要将深度缓冲区一并清除,并且将清空后的深度缓冲区中每个像素的深度值设置为第五个参数所表示的大小;第五个参数的取值范围是[0,1],这里设置为1,表示场景中可能的最大深度。
到这里,关于Direct3D中深度测试的基本概念及使用方法都介绍完了,下面我们用一个实例来加深这一过程的理解。好记性不如烂代码,难道不是吗?

一个实例

实例思路:在场景中创建一个灰色矩形作为遮挡板,一个红色的茶壶作为被遮挡物体。其中,茶壶的像素深度要大于遮挡板像素的深度(即正常情况下遮挡板会挡住茶壶)。
在关闭深度测试的情况下,遮挡板并没有遮住茶壶(因为茶壶是后渲染的)。通过按下键盘上的‘W’和‘S’键可以打开或关闭深度测试,从而可以感受到深度测试的作用效果。
运行效果:
打开与关闭深度测试:

二者在世界坐标系中的相对位置:

注意事项: 下面只是给出了程序中关键地方的代码,完整程序可以在文章末下载。
关键代码:
观察矩阵与投影矩阵的设置:

//创建并设置观察矩阵D3DXVECTOR3 vEyePt(0.0f, 10.0f, -15.0f);        //摄像机位置向量D3DXVECTOR3 vLookatPt(0.0f, 0.0f, 0.0f);    //摄像机朝向向量D3DXVECTOR3 vUpVec(0.0f, 1.0f, 0.0f);       //指定摄像机上方的朝向向量D3DXMATRIXA16 matView;D3DXMatrixLookAtLH(&matView, &vEyePt, &vLookatPt, &vUpVec);g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView);//创建并设置投影矩阵D3DXMATRIXA16 matProj;D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0, 1.0f, 1.0f, 1000.0f);g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);

渲染状态的设置:

//启用深度测试g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);      //激活深度测试,默认是激活的//设置深度测试g_pd3dDevice->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESS); //设置深度测试函数g_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, TRUE); //通过测试则更新深度缓冲区,默认是更新的g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0xffffffff);    //设置环境光

场景中图形的创建函数:

/*
* 创建场景图形,即在三维世界中要渲染的模型。包括一个茶壶模型
* 和一个由顶点缓冲区创建的矩形(遮挡板)模型
*/
HRESULT InitGriphics() {//遮挡板顶点数据CUSTOMVERTEX g_Vertices[] = {{ -1.0f, -1.0f, -5.0f, 0xff808080 },{ -1.0f, 1.0f, -5.0f, 0xf808080 },{ 1.0f, -1.0f, -5.0f, 0xf808080 },{ 1.0f, 1.0f, -5.0f, 0xf808080 }};//创建遮挡板顶点缓冲区if (FAILED(g_pd3dDevice->CreateVertexBuffer(4 * sizeof(CUSTOMVERTEX), 0,D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pShutter, NULL))) {return E_FAIL;}//填充遮挡板的定点缓冲区VOID* pVertices;if (FAILED(g_pShutter->Lock(0, sizeof(g_Vertices), (void**)&pVertices, 0))) {return E_FAIL;}memcpy(pVertices, g_Vertices, sizeof(g_Vertices));g_pShutter->Unlock();D3DXCreateTeapot(g_pd3dDevice, &Teapot, 0);return S_OK;
}

场景渲染代码:

//开始渲染if (SUCCEEDED(g_pd3dDevice->BeginScene())) {//渲染遮挡板g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matShutter);g_pd3dDevice->SetStreamSource(0, g_pShutter, 0, sizeof(CUSTOMVERTEX));g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);渲染茶壶g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, TRUE);g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_matTea);g_pd3dDevice->SetMaterial(&TeapotMtrl);g_pd3dDevice->SetTexture(0, 0);Teapot->DrawSubset(0);g_pd3dDevice->EndScene();}g_pd3dDevice->Present(NULL, NULL, NULL, NULL);  //提交到前台缓冲区

值得注意的是,茶壶所在的位置是世界坐标中的原点处;而且渲染顺序是先渲染的遮挡板后渲染的茶壶;默认情况下深度测试是激活的。

下载源代码

链接:https://pan.baidu.com/s/1k8GurbSdYGo9aTlyWRc0Xg 密码:8cdp

过关Direct3D中的深度测试就介绍到这里,下一篇来探讨Alpha混合技术的使用。

8.1 Direct3D的深度测试相关推荐

  1. RapidIO协议概述(一)

    [SRIO]1.RapidIO协议概述 目录 一.RapidIO背景介绍 二.RapidIO协议概述 2.1 包与控制符号 2.2 包格式 2.3 事务格式与类型 2.4 消息传递 2.5 全局共享存 ...

  2. 深度测试与alpha混合(1)

    深度测试与alpha混合(1) 在绘制复杂的三维场景时,不可避免地会出现物体间的相互遮挡,在这种情况下,为了正确地绘制场景需要使用深度测试.半透明物体的绘制不同于不透明物体,Direct3D通过alp ...

  3. 剔除与深度测试(Culling Depth Testing)相关内容

    一.剔除与深度测试(Culling & Depth Testing)相关内容 1.1 剔除(Culling)的概念 对于实时交互的3D环境而言,现实的速度和效率是非常重要的.虽然现在的硬件能力 ...

  4. 第六章 使用Direct3D绘制

    第五章主要关注渲染管道的概念和数学方面.本章重点介绍配置渲染管道所需的Direct3D API接口和方法,定义顶点和像素着色器,并将几何图形提交给绘制管道进行绘制.学完本章,您将能够绘制各种几何形状的 ...

  5. 图形世界分裂的两派——理清Direct3D和OpenGL的脉络

    计算机三维图形是指将用数据描述的三维空间通过计算转换成二维图像并显示或打印出来的技术,API(Application Programming Interface)即"应用程序接口" ...

  6. 图形世界分裂的两派 理清Direct3D和OpenGL的脉络

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 计算机三 ...

  7. DirectX11 Direct3D基本概念

    Direct3D基本概念 1. Direct3D概述 Direct3D是一种底层绘图API(application programming interface,应用程序接口),它可以让我们可以通过3D ...

  8. dx12 龙书第四章学习笔记 -- Direct3D的初始化

    1.预备知识: ①Direct3D 12概述: 通过Direct3D这种底层图形应用程序编程接口(Application Programming Interface, API),即可在应用程序中对图形 ...

  9. Direct3D 10系统(一)

    Direct3D 10系统(一) 作者:David Blythe 本文版权归原作者所有,仅供个人学习使用,请勿转载,勿用于任何商业用途. 由于本人水平有限,难免出错,不清楚的地方请大家以原著为准.欢迎 ...

最新文章

  1. 最新必读图神经网络论文
  2. 大雁塔为什么七层_西安旅游的打卡景点,大雁塔是干嘛的?怎么来的?
  3. 学习笔记Hadoop(九)—— Hadoop基础操作(1)—— Hadoop安全模式、Hadoop集群基本信息
  4. 写的很好!细数 Java 线程池的原理
  5. ipv4校验(java)
  6. Nginx系统环境准备
  7. rails jquery_Spring与Rails的jQuery UJS
  8. vs2010中引入boost库
  9. 浅谈消息队列的原理及优势
  10. java数组的声明学号姓名线性结构_20172302 《Java软件结构与数据结构》实验一:线性结构实验报告...
  11. 格力电器详解举报奥克斯 巨头互撕为哪般?
  12. Oct22 实例测试
  13. J2ME-Loader 使用体验:在安卓上运行 Java 应用
  14. html制作qq对话消息框,qq对话框设置 QQ聊天对话框的背景皮肤怎么设置?
  15. python函数和代码复用思维导图_函数式编程库:Ramda函数思维导图,帮你快速选择要用的函数...
  16. nodejs中使用nodemon加载文件报错
  17. 年报文本分析:jieba词频统计
  18. 真假内推?直拿offer?别被无良中介给骗了
  19. 【顿悟】会者定离,一期一祈,勿怀忧也,世相如是.
  20. 局域网内帆软BI使用arcgis发布的wms服务

热门文章

  1. 并不简单的翻页时钟(二):JavaScript篇
  2. AdaBoost算法部分理论推导
  3. 获取淘宝店铺所有商品数据
  4. 原生手写富文本编辑器组件
  5. 微信小程序支付流程!
  6. word-解决复制相同的word内容,显示行间距差别大的原因
  7. 源于实践的ERP123——ERP123方法论(转)
  8. 【单链表】单链表的删除
  9. 《Spring实战》读书笔记-第3章 高级装配,最新Java大厂高频面试题
  10. HOG特征提取matlab代码