3D游戏编程入门(十)D3D程序Debug技巧和D3D基础
呼,现在开始D3D专门相关的东西开始介绍了,果然一天之内一直复习有点顶不住的感觉,需要补充一些感性方面的东西哇。恩,可又不愿
意花过多的时间,着实令人烦恼,稍后再想想做点什么来休闲吧。
###D3D程序BUG测试技巧
这东西本来应该是程序最后才总结的,但是想了想,对于初次涉及D3D编程的朋友还是先说一下的比较好,对自学有很大的好处。
了解编程的新手朋友会明白,我们初心者进行一个项目开发时最头疼的莫过于一连串的错误了,即使经过反复的调试,也可能有很多的BUG我们依旧难以找到,下面是一些总结的经验,大家在以后进行测试时会有一定的帮助的。
- 在CPP文件开头,include
之前,我们定义(define)STRICT符号的话,将导致更严格的类型检查,帮助我们进行错误的定位。 - 在DX SDK中,DXDiag实用程序可以进行错误报告,但这并不值得推荐给新手,使用它时,我们必须很清楚我们系统上有些什么东西。
- 在DX控制面板中允许调设debug output level等级,我建议使用4级,另打开Debugging中的四个选项Maximum Validation,Enable Shader Debugging,Break on Memory Leaks,Break on D3D Error,另外一定要记得在D3D程序调试时设置为Use Debug Version,恩,当然,进行别的游戏运行时,一定记得调整回来,不然可无法正常运行的哦- -(似乎我开始就多次犯这个错误)
- D3DX库是一个静态库,为了方便调试,我们可以导入一个动态库,d3dx9d.lib它是专门用做调试的库。
- 从网上下载个内存管理器可以减少内存分配和释放的问题,当然VC++提供了一个称为CRT的内存管理器。根据个人爱好了
- 下载VC++.Net的插件来调试顶点Shader和象素Shader
- 使用OutputDebugString();Debugbreak();等调试辅助函数
- 尽量避免在调试时使用全屏模式,在创建窗口时应传参isWindowed来控制全屏与否。
- 对于释放时,经常SafeRealse,SafeFree来替代Realse,free,其实仅仅需要自己去加个判断为空的定义。
- 一般DX的方法都会返回一个HRESULT,我们可以用SUCCEEDED(),FAILED()的函数对它返回值进行判断,防止出错。
- 还有VC环境中设置断点,进行监视等技巧,相信大家比较熟悉了
###D3D基础
又是Windows编程基础,又是向量矩阵介绍,又是DX介绍,现在又是D3D介绍,会不会比较郁闷,怎么还没开始打代码啊0 0呵呵,别着急,学习是慢慢积累的,编程就象写文章,这些知识是思想,函数调用等代码是文字。我们要写好的文章,光有文字没有思想是不行滴,而且高程们更多的倒是整体流程框架思想的把握,我们这样的低级程序员到是象打字员一样的忙来忙去,呼,向高手看齐。不多说了,现在把D3D里面的一些术语介绍下。
- PageFlipping(页翻动)。
我们要明白,屏幕上显示的图象并非直接绘制在屏幕上的,而是先将这些图象绘制在一个不可见的页面上,这个页面我们称为“后台缓冲”,当绘制完毕后,系统快速的将它放到屏幕的可见的“前台缓冲”上,并且将我们刚才看到的页面再放到“后台缓冲”进行重复绘制。所以我们的屏幕显示要分为60桢,75桢,85桢的频率的问题,那就是每秒种后台缓冲放置到前台缓冲的页面频率。当这个频率过低,我们眼睛的视觉残留能够捕捉时,我们就会发现屏幕的翻动了,想想,电视里录制的电脑屏幕是否是一闪一闪的,那就是因为摄象机的录制频率比电脑屏幕刷新速率快的问题。
- Pixel和Resolution象素和分辨率。
屏幕上的显示的图象是由一块块的小点组成的,这个最小单元我们叫做象素,它也是颜色缓冲区中最小的图形单元。分辨率则是我们的显示设备可以显示的最大象素个数。例如,我们现在电脑屏幕大部分可以支持1024X768的象素,则代表他上面有1024X768=78万多的象素。也就是说我们此时的屏幕是由78万多个小点组成的。
- 颜色缓冲区FrameBuffer。
这是位于系统内存或者显存中的一块内存。它存放者我们需要显示的一块二维显示区。
- 设备Device。
说白点,就是我们的显卡
- 本地空间。
是指我们在定义组成物体的三角形列表时使用的坐标系。在其中创建物体模型时,我们不需要关心物体位置和其他世界空间内的物体关系,可以很大程度上简化我们的模型处理工作。
- 世界空间。
我们每个模型都是在自己的本地空间创建,创建好了之后,我们需要将这些模型物体组合到一起,形成一个整个的场景,这时我们需要一个统一的世界坐标系。在其中,我们开始考虑与其他物体之间的位置关系,到底谁遮挡谁?
- 视图空间。
我们在D3D的场景中需要设置一个虚拟的摄象机,以确认我们可以看到的场景,这个由虚拟摄象机决定的空间是视图空间。在其中,我们需要考虑到底我们可以看到什么,哪些我们又看不到?
- 视口。
这个很好理解,当我们游戏全屏运行时,我们的视口就是全屏幕,当我们设置为窗口模式时,窗口上显示游戏的矩形区域就是视口。
- 顶点Vertices。
就是模型物体的一个顶点。我们将模型物体都看为由一个个三角形组成时,这些三角形的三个顶点,就是我们这里说的顶点。
- T&L流水线。
在介绍OpenGL时我曾做过介绍。我们渲染图形时通常是可以分为两大步的。第一部分是转换和照明(T&L)在这阶段我们将顶点进行矩阵转换,再根据灯光材质等计算每个顶点的照明效果。第二部分是光栅化处理,这里我们开始控制纹理,深度缓冲,来判断哪些用户可见的顶点,并将绘制结果显示在屏幕上。
在这个流水线中,我们将未处理的顶点从其中一端送入,经过处理操作,从另一端就会出来我们所需要的3D图形。这里在详细的介绍下吧。
首先进行顶点坐标的转换。我们可以通过SetTransfrom的方法来实现对应的变换。我们首先需要创建一个D3D设备的指针。
我们可以写以下代码
IDirect3DDevice g_pD3DDevice = NULL ; /* 创建D3D设备指针 */
g_pD3DDevice->SetTransform( D3DTS_WORLD, &matWorld ); /* 其中D3DTS_WORLD代表矩阵转换的类型,matWorld是一个变换矩阵 */
在上述代码中,我们实现了将一个独立的物体放到世界坐标系的功能,在世界转换中我们主要完成对模型的旋转,缩放,平移等
等我们将物体转换到世界空间后,我们需要确定视图空间,我们需要定义好摄象机的一些属性,其中包括摄象机的位置,摄象机的朝向,摄象机的正方向,这些我们都是使用向量来表示。
D3DXMATRIX* D3DXMatrixLookAtLH
(
D3DXMATRIX *pOut, //输出用于视图变换的矩阵
const D3DXVECTOR3* pEye, //摄像机的位置
const D3DXVECTOR3* pAt, //摄像机朝向的位置
const D3DXVECTOR3* pUp //摄像机的正方向
);
在这里我们需要注意的是两点:
这里的函数是D3DX开头的,这代表这是一种比较高层的接口,它是捆绑了一定的低层接口的方法的,在一定程度上,它方便了我们的设计,但是也硬性的捆绑了一些设定,对我们编程时的调整有很大的坏处,我们尽量可能的使用D3D开头的函数。
大家看到了,此函数第一个参数是输出的矩阵,我们接下来进行使用的矩阵也就是这个矩阵,我们在此可以先声明一个空的矩阵
D3DXMATRIX* pOut = NULL;
在函数运行完毕之后,我们进行监视,会发现其值发生了变化,我们就可以使用这个变换过的结构变量进行下面 的处理了,这是DX中很常见的一种设计模式。
然后我们来设置视图变换。
g_pD3DDevice->SetTransfrom( D3DTS_VIEW, &matView ); //其中D3DTS_VIEW依旧是转换类型,后面matView是一个视图变换矩阵。
之后我们将视图变换完的顶点提取出来,根据其位置,法线位置,本身颜色,和材料材质来计算每个顶点上的光亮度,并将其颜色信息存贮在顶点信息中。
在光照计算后,我们将进行投影变换,它将帮助我们去生成一个投影图,该图中我们将离摄象机远的物体成像进行缩小,使图具有纵深感,依旧是
g_pD3DDevice->SetTransfrom( D3DTS_PROJECTION, &matProj ); //D3DTS_PROJECTION转换类型
matProj是一个投影变换矩阵. 之后我们对一些不可见的点进行剪裁,在我们设置物体三角行图元顶点时,我们就需要很注意这一点,默认的时候D3D会将顶点顺时针排列的三角型作为前向多边形,逆时针的做为后向多边形。我们可以跟着来确认该图元是否可见。
代码为
g_pD3DDevice->SetRenderState( D3DRS_CULLMODE, Value ); //参数为剪裁方式
此时我们的大致流程就差不多了,最后在定义屏幕显示区域(视口)的大小,通过它来将顶点从投影坐标系转换为最终显示的屏幕坐标。
在D3D中定义了视口结构为
typedef struct _D3DVIEWPORT9
{
DWORD X; //视口区域的左上角x坐标
DWORD Y; //视口区域的左上角y坐标
DWORD Width; //视口区域的宽度
DWORD Height; //视口区域的高度
float MinZ; //视口内景物的最小深度值,0-1.0之间,通常为0
float MaxZ; //视口内景物的最大深度值,0-1.0之间,通常为1
} D3DVIEWPORT9;
我们通常使用SetViewport()函数来设置D3D视口。
HRESULT SetViewport
(
const D3DVIEWPORT9 *pViewport //视口
);
呼,其他的明天再说了,累了>_<写这些东西那是相当费神的。。还有两天,继续努力0 0