转自:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html
DDS
DirectXDraw Surface file format, .dds。这是微软从DirectX7开始引进的一种文件格式,它用来存储压缩的或未压缩的纹理,该格式支持mimaps cube maps和volume maps, D3DX和许多其他的DX工具都支持这种格式,比如DirectX Texture Editor(dxtex.exe)和Texture Conversion Tool(Texconv.exe),从D3D110开始,DDS文件也支持纹理数组
DXGI
DirectX Graphics Infrastructure
转换为.x格式
MeshConvert.exe,这个tool位于Microsoft DirectX SDK \Utilities\bin\x86目录下面,可以用来转换.x文件,支持.x, .obj, .sdkmesh格式之间的相互转换,.sdkmesh是微软新的格式在DX10/DX11之后使用,用来取代.x格式,MeshConvert是一个命令行工具,使用方法如下
Usage: meshconvert <options> <input filename>
<input filename> Input mesh filename. The input file can be of .x, .obj, or .sdkmesh format /o <file> Optional output mesh filename /y Overwrite existing destination file /n Generate normals /t Generate tangents /tb Generate tangents and binormals /tcount <count> Texcoord count for the output mesh /a Output animation information /op Vertex cache optimize the mesh before exportin (non-functional) /sdkmesh [default] convert to .sdkmesh binary file /x convert to .x binary file /xt convert to .x text file /v Verbose
.SDKMesh is a simple custom mesh format used by new SDK samples
使用DirectX Control Panel
使用DirectX Control Panel可以方便的设置DirectX相关的东西,比如用Debug库还是Release库,是否检测内存泄漏,是否允许Shader调试等等,这个工具的位置在...\Microsoft DirectX SDK\Utilities\bin\x86,界面如下图。
使用PIX分析D3D应用程序
PIX是调试和分析D3D应用程序的,该工具位于...\Microsoft DirectX SDK\Utilities\bin\x86下,关于它的用法,DX帮助文档中有详细的说明,位于tools-DirectX Performance Tools-PIX下,工具截图
注意资源释放的顺序
有时候在释放资源的时候会出现运行时错误,这可能是由于资源之间的依赖造成的,比如下面的代码,运行时会出现一个违规访问的错误,为什么呢,因为m_SoundBuffer是依赖m_pDirectSound的,而m_pDirectSound先释放了,所以m_SoundBuffer变得不可访问了
if(m_pDirectSound != NULL) { m_pDirectSound->Release() ; m_pDirectSound = NULL ; }
if(m_SoundBuffer != NULL) { m_SoundBuffer->Release() ; m_SoundBuffer = NULL ; }
将二者释放的顺序交换一下就好了,如下。
if(m_SoundBuffer != NULL) { m_SoundBuffer->Release() ; m_SoundBuffer = NULL ; }
if(m_pDirectSound != NULL) { m_pDirectSound->Release() ; m_pDirectSound = NULL ; }
注意变换的顺序
有很多图形学书籍都讲到过,先旋转再平移和先平移再旋转效果是不一样的,同理,先缩放再平移和先平移再缩放也是不一样的。
不要在Render函数中设置变换矩阵
为了渲染,一般的对象都有个Render函数,在渲染之前要做一些变换,比如移动旋转之类的,但是切记不可在Render函数中做这些变换,因为Render函数是被实时调用的,如果在这里做变换,则一个变换会被应用很多次。
D3D中顶点的顺序问题
Remember in Direct3D the vertices must be defined in clockwise order. see the picture below, the order is v0, v1, v2, v3
正确使用时间函数timeGetTime()
这个函数返回的是当前时间-单位毫秒,可是类型是DWORD,而我们一般做计算都需要float类型,转换一下,一般应该先计算差值,再做类型转换
DWORD lastTime = timeGetTime();
...
DWORD currTime = timeGetTime(); float timeDelta = (currTime - lastTime) * 0.001f;
...//use timeDelta lastTime = currTime;
而不要这样做:
static float lastTime = (float)timeGetTime();
... float currTime = (float)timeGetTime(); float timeDelta = (currTime - lastTime)*0.001f;
这样是要损失精度的,因为DWORD是32为整数,而float只有24位精度,此法不可取。
让代码只执行一次
void RunOnce() { static bool flag = false ;
if(!flag) { cout << "you can not see this line twice!" << endl ; flag = true ; } }
头文件包含警戒
通常我们用下面的代码来防止头文件被重复包含,这是标准的写法
#ifndef __HEADER_H__ #define __HEADER_H__
// code of the file #endif //HEADER_H__
还有一种方法是,这种方法是MS特有的,不适合其他平台,所以推荐使用第一种方法
#pragma once
// code of the file
解决从VC6转换到VS2005/VS2008时编译和链接错误
有很多几年前编写的游戏书籍,其中所附代码大多是基于VC6的,而我们现在用的大多是VS2005/VS2008/VS2010,所以需要一次转换,而在转换中经常会遇到编译或者链接错误,解决办法如下:
编译错误:error C2440: '=' : cannot convert from 'char [14]' to 'LPCWSTR'
这是由于作者默认使用的是函数的A版本,而我们的工程使用了W版本。
解决办法一:手动将所有函数改为A版本,例如MessageBox -> MessageBoxA, 不推荐这种防范。或者将所有字符串改为宽字符串,即在字符串前加大写字母L,这个方法比较笨,但是确实好的方法。推荐使用。
解决办法二:在工程文件上点右键-Properties-Configuration Properties-C/C++-Preprocessor-Preprocessor Definitions-点击右边的省略号按钮-在弹出的对话框中将Inherit from parent or project defaults选项去掉即可。这个方法一劳永逸,可以一次性去除所有此类编译错误。但是却不是好的编程习惯,我们应该尽量使用函数的W版本。
链接错误:unresolved external symbol xxx
这种错误一般都是由于缺少lib文件导致的,只要引用正确的lib文件就可以了
解决办法:在工程文件上点右键-Properties-Configuration Properties-Linker-Input-Additional Dependencies-点击右边的小省略号按钮-将所需的lib文件加入到弹出的对话框中即可。
调试选项的设置
通过注册表也可以设置D3D的调试选项,这和使用DirectX控制面板是一样的,而且两者是相通的,改变一个,另一个也会跟着改变,如下,注册表位置:
HKLM\Software\Microsoft\Direct3D
如何求取视线
有很多时候需要用到视线,比如Billboard技术,要求被观察物体始终面对观察者,这时候就需要该物体与视线垂直,求取视线很简单,用观察点减去眼睛的位置即可
1 D3DXVECTOR3 viewRay = lookAt - eyePt ;
Zoom in/Zoom out效果是如何实现的?
很多图形软件都支持该效果,在浏览一个model的时候,鼠标滚轮向前滚时(mouse wheel rotate forward, away from the user),model会逐渐变大,谓之zoom in,鼠标滚轮向后滚时(mouse wheel rotate backward, toward the user),model会逐渐变小,谓之zoom out
那么这个效果是如何实现的呢?在我初学DirectX的时候,一直以为用缩放变换来实现。直到研究了DXUT的Camera类以后,才发现,其实有一个更简单的办法。通过改变Camera与Model之间的距离来实现,这与现实生活中的感觉是一样的,当我们离一个物体近的时候,感觉它很大,而当我们渐渐远离它的时候,它会变得越来越小,此其所以然也!编程的乐趣就在于灵活!
transform小结
Transform(变换)的本质就是从一个坐标系到另一个坐标系的过程 world transform model space -> world space
view transform world space -> view/camera space
projection transform view/camera space -> projection space
transform engine(变换引擎) 以顶点为输入,对其进行world, view and projection transform, 然后进行剪
裁,将结果传送给rasterizer进行光栅化
//world transform通过下面的代码完成 // 将model移动至原点 D3DXMATRIXA16 matWorld ; D3DXMatrixTranslation( &matWorld, 0.0f, 0.0f, 0.0f) ; g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
//view transform通过下面的代码完成 D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-10.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 );
projection transform通过下面的代码完成 D3DXMATRIXA16 matProj; //视角:pi/4,即45度 //纵横比:1:1 //近剪裁平面:1.0f //远剪裁平面:1000.0f D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f ); g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
如何区分Point和Vector
在D3D中Point和vector都是用三维坐标表示的,比如,给定 v = [x, y, z], 如何区分这是一个点还是一个向量呢 答案是:在三维坐标下无法区分,于是在最后再加一维,形成齐次坐标 v = [x, y, z, w], 实际应用中w通常取值1 规定如下: w = 1时,v表示点 w = 0时,v表示向量 为什么呢? 先看一下平移变换的矩阵
因为向量是没有位置属性的,向量只有长度和方向两个属性,即平移一个向量是没有意义的 所以将向量的第四维w设置为0,阻止平移。 但是点是有位置属性的,平移一个点也是有意义的,所以将点的w置为1,使之可以平移
如何从一个坐标系变换到另一个坐标系
通常这种需求都是逆向的,因为正向的D3D已经做了 比如对于一个view space中的model,如何把它变换到world space呢?(在D3D顶点处理流水线中world space是view space的前一个过程) 首先我们应该知道world space -> view space 是怎样一个过程,了解了这个过程后,应用它的逆过程即可 显然,world space -> view space是通过视图变换实现的,这个变换矩阵就是view matrix,好了,现在我们需要做的是: 1. 找出这个view矩阵 2. 求出它的逆矩阵 3. 将该逆矩阵应用到要变换的模型 下面以一个点为例,假设我们要将点p(x, y, z) 从view space变换到world space
步骤: 首先求出view matrix D3DXMATRIX view; Device->GetTransform(D3DTS_VIEW, &view);
再求出它的逆矩阵 D3DXMATRIX viewInverse; D3DXMatrixInverse(&viewInverse, 0, &view);
然后将这个矩阵乘到这个点上即可 D3DXVec3TransformCoord(p, p, viewInverse);
注:D3DXVec3TransformCoord是用来变换点的(即w=1的vector) 而 D3DXVec3TransformNormal是用来变换向量的(即w=0的vector)
矩阵连乘的顺序
如果需要对同一个图形连续进行多个变换,那么可以把多个变换矩阵连乘形成一个矩阵。
但矩阵乘法不满足交换律,所以连乘的顺序很重要。
大多数文档都是以左右顺序来区分的,这是不准确的,而且不便于理解和记忆。
这里讲一个规则,比较方便
规则:在连乘式中,离被变换顶点近的矩阵对应的变换先进行。
比如,对顶点P(x, y, z, 1)进行如下变换
1. 平移到(1, 1, 1)点,变换矩阵为MT
2. 绕z轴旋转30度,变换矩阵为 MR
3. 放大2倍,变换矩阵为 MS
那么正确的连成顺序是
P * MT * MR * MS (在DirectX文档里多采用这种写法)
MT离P最近,那么它是最先进行的变换,MR次之,MS再次。
很多书籍或文档里面说是按照从左到右的顺序连乘,但是别忘了顶点在公式中的位置
如果顶点在右边,那么就变成了下面的形式
MS * MR * MT * PT (在以OpenGL为基础的图形学书籍里多采用这种写法)
所以准确的说应该是,在连乘式中,先进行的变换离被变换的顶点近。
注意如果P在连乘式右边,那么应该取其转置形式,否则无法与矩阵相乘。
为什么变换矩阵是4 x 4的
因为三维的矩阵无法完成某些变换,比如平移变换对应的矩阵如下
这用三维矩阵是无法实现的,类似的还有透视投影变换,所以在D3D中使用4×4矩阵来实现变换,又一个问题来了,由于3D中的顶点都是三维的,三维的vector和四维的matrix是无法相乘的,于是在三维的顶点后面再加上一维,形成齐次坐标,如下
[x, y, z, w]
最后一维通常用w表示,而w通常取值1
这样就可以用一个1×4的vector与一个4×4matrix相乘了。
[x, y, z, w] *
d3dx9.h
这个头文件包含了几乎D3D用到的所有头文件,如果编译、链接或者运行有问题,可以尝试包含这个文件试试。
如何查看VertexShader和PixelShader的版本号
如果安装了DirectX SDK,可以使用其中的工具DirectX Caps Viewer 来查看,这个工具在DirectX的开始菜单中,也在SDK安装目录的Utilities\Bin\x86(x64)下面
打开这个工具后,依次展开结点DirectX Graphics Adapters-video card name-D3D Device Types-HAL-Caps
注意:一定要选择HAL分支,这才是你显卡真正支持的特性,而Reference目录下存储的是所有D3D特性,这是用软件模拟的,速度奇慢,只有在安装了DirectX SDK的机器上才可用,这就是HAL Device和Reference Device的区别,大家可以查看SDK帮助文件中Device Type一节来获取详细的信息
如果没有安装DirectX SDK, 可以使用Everest来查看
如何发布游戏(XNA)
看这里吧,比较详细
点积和叉积
设u和v是两个三维向量,α是它们之间的夹角
点积
u·v = ux*vx + uy*vy + uz*vz =|u|*|v|*cos(α)
乍一看,几何意义不十分明显,注意:当u和v都是单位向量时,点积就是两个向量夹角的余弦值
D3D中经常用这种方法来求夹角或者旋转角度,或者顶点法向量与入射光的夹角。
叉积
u×v = [(uyvz - uzvy), (uzvx - uxvz), (uxvy - uyvx)] =|u|*|v|*sin(α)
叉积的结果同时垂直于两个向量
叉积的结果值是以u和v为邻边的平行四边形的面积,叉积可以用来求多边形的面积
global variables are implicitly constant, enable compatibility mode to allow modification
编译Shader程序时遇到以下错误
全局变量默认是常量,貌似在Shader程序中不能修改?新版的HLSL编译器不支持
解决办法:
将D3DXCompileShaderFromFile的第六个参数改为D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY即可
花屏
如果程序执行后,窗口出现花屏现象,那么多半是由于depth buffer没有clear导致的,比如下面这幅图就是一个花屏的例子。
解决办法,在调用Clear函数时,将depth buffer也clear一下即可。原来可能是这样调用的
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0xff00ff00, 1.0, 0 ); //仅仅clear render target
修改后如下
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER , 0xff00ff00, 1.0, 0 );
需要注意的是,这个Clear函数可能会失败,比如你在CreateDevice的时候并没有创建depth buffer,而在Clear函数中却要Clear depth buffer,所以请检查你的CreateDevice函数,确保设备确实有一个关联的depth buffer。
转自:http://blog.csdn.net/fuli1215/article/details/5961632
DirectX 9.0
1:平面(法向量(normal vector)和平面中一点表示。
2,D3DX库用如下结构体来表示一个平面类,typedef struct D3DXPLANE{}
3点和平面的空间关系
Direct3D 基础
一:
1, 初始化Direct3D
2, Direct3d 可以被视作应用程序与图形设备(3d硬件)的交互中介
3, 一个中间环节 HAL(hardware abstraction layer)硬件抽象层,HAL是一个指示设备完成某些操作的设备的相关代码集。规范 specification
4, 软件定点运算???
二:
REF设备:Direct3D提供了参考光栅设备(reference rasterizer device),它能以软件的方式完全支持direct3d API。
注意:REF设备仅用于开发阶段,它与direct SDK捆绑(ship)在一起,无法发布给最终的用户,此外REF设备的速度十分的缓慢,在测试以外的其他场合都很不实用。
D3DDEVTYPE :HAL 设备用值D3DDEVTYPE_HAL来指定,
1.2 COM(组建对象模型)
COM(Component Object Model)是一项能使Direct 独立于编程语言并具备向下兼容的技术,可将其视为一个C++类来使用
创建COM接口时不可使用C++关键字new ,用完后应该用 Release方法而不是delete,
注意:COM接口都有一个前缀I,
1.3 预备知识
1.3.1 表面
表面主要是Direct3d 用于存储2D图像数据的一个像素矩阵。
线性数组(linear array)
跨度(pitch,stride的同义语)用字节表示,
代码中用 接口 IDirect3DSurface9来描述表面。
获取(retrieve)
1.3.2 多重采样
用像素矩阵表示图像时往往出现块状(blocky-looking)效应,多重采样(multisampling)时一项用于平滑块状图像的技术。
全屏反走样(full-screen antialiasing)
多重采样会降低应用程序的运行速度。如果要使用多重采样技术 ,请务必使用IDirect3D9::CheckDeviceMultiSmpleType 方法来检查你的设备是否支持多重采样。
1.3.3 像素格式
1.3.4 内存池(memory Pool)
内存池可以用D3DPOOL 枚举类型来表示,、
显存(video memory)
1.3.5 交换链和页面置换
表面集合(collection),交换链(Swap Chain),该集合用接口Idirect3DswapChain9 来表示,
置换技术(page Flipping),这两项技术主要用于生成更加平滑的动画。
前台缓存槽(front buffer slot),帧频(frame rate),后台缓存(back buffer)
1.3.6 深度缓存
深度缓存(depth buffer)是一个只含有特定像素的深度信息而不含图像数据的表面。
深度项(entry)。
Z-缓存 (z-buffering)。//前面的物体遮挡后面的物体。
深度缓存的格式决定了深度测试的精度。
1.3.7 定点运算
定点运算(vertex processing) 分为两种,即:软件定点运算(software vertex process)或硬件定点运算(hardware vertex proceeding)。无论怎样 ,软件定点运算总是被支持,所以总是可以实现的。但我们应该始终优先考虑硬件定点运算的方式,因为速度更快,而且可以不占用cpu资源,也就意味着cpu可以被解放出来进行其他的运算。
注意:图像卡支持硬件定点运算的另外一种等价的说法是该图形卡支持变换和光照的硬件计算。
1.3.8 设备性能
bool supporthardware ;
if(caps.DevCaps &D3DDEVCAPS_HWTRANSFORMNDLIGHT)
{
Supporthardware=true;
}
Else
{
Supporthardware=FALSE;
}
注:DevCaps 表示“设备性能 (Device Capabilities)”
1.4 Direct3D 的初始化
初始化的过程可以分为如下的步骤:
1:获取接口IDirect3d 的指针。获取物理硬件信息并创建接口IDirect3Ddevice9该接口为一个C++对象。
2:检查设备性能(D3DCAPS9),判断主显卡(primary display adapter)是否支持硬件定点运算。
3:初始化D3DPRESENT_PARAMETERS结构中的一个实例。
4:利用已经初始化的D3DPRESENT_PARAMETERS结构创建IDirect3Ddevice对象。
1.4.1 获取接口IDirect3D9的指针
IDirect3D9 * _d3d9;
_d3d9=Direct3DCreate9(D3D_SDK_VERSION);
_d3d9对象的用途:设备枚举(device enumeration)以及创建IDirect3DDevice9类型的对象。
1.4.2 校验硬件顶点运算
1.4.3填充D3DPRESENT_PARAMETER结构
1.4.4创建IDirect3DDevice接口
HRESULT IDirect3D9::createDevice();
1.5 例程,Direct3D的初始化
InitD3D 函数对应用程序主窗口进行初始化,如果成功,则返回一个已经创建好的IDirect3DDevice接口指针。(这个函数我们可以指定窗口的尺寸,运行模式)。
EnterMsgLoop 该函数封装了应用程序的消息循环。
1.5.2 例程框架
。。。。bool Setup()例程所需进行的全部设置和初始化都在改函数中进行。如分配资源,检查设备性能,设置应用程序的状态。
。。。。。void Cleanup()该函数用于释放在setUP函数中分配的任何资源。
。。。。。bool Display(float timedelta).实现全部的绘制代码以及相邻帧之间应该执行的操作。
1.5.3 例程 D3D初始化
第二章 绘制流水线
绘制流水线(rendering pipeline)
场景(scene)是物体和模型的集合。
任何物体都可以用三角形网格(triangle mesh)逼近表示。三角形网格是构建物体模型的基本单位。多边形(polygons),图元(primitives),网格几何元(mesh geometry),
定点格式:
定点除了包含空间信息外,还包含其他的附加属性,如颜色属性,法线(normal)属性,
灵活定点格式(flexible vertex format fvf),
2.1.2
三角形单元
三角形个定点的指定顺序非常重要,我们称为绕序(winding order),
2.1.3 索引
索引(indices),其原理,我们创建一个定点列表和一个索引列表,定点列表包含了全部独立定点,索引列表包含了指向定点列表的索引,这些三角形规定了为构建三角形单元,各定点应按何种方式来组织,
定点列表可以这样来建:
Vertex vertexlist【4】={v0,v1,v2,v3}
接下来,借助索引列表来定义顶点列表中顶点的组织方式以构成两个三角形单元。
WORD indexList【6】={0,1,2,0,2,3}
2.2 虚拟摄像机
可见的空间体积(volume of space)。视域体(frustum)
丢弃看不见的视域体的这类数据运算过程我们称为裁剪(clipping)。
投影窗口(projection window)是一个2D区域,
2.3 绘制流水线
一个简单的绘制流水线
局部坐标系-》世界坐标系-》观察坐标系-》背面消隐-》光照-》裁剪-》投影-》视口坐标系-》光栅化。
局部坐标系到世界坐标系的变换:
Device-》SetTransform(D3DTS_WORLD,&worldMatrix);
2.3.1 局部坐标系
局部坐标系(local space)又叫建模坐标系(modeling space),是用于定义构成物体三角形单元列表的坐标系,
2.3.2世界坐标系
世界坐标系又叫全局坐标系,一个位于局部坐标系的物体通过一个叫世界变换(world transform)的运算(过程)变换的世界坐标系(world space),该变换通常包括平移(translation),旋转(rotation)以及比例(scaling)运算,
世界变换用一个矩阵来表示,并由Direct3D通过IDirect3DDevice9::SetTransform方法来加以应用,该方法的第一个参数表示要变换的类型,如 要进行世界变换则要设为:D3DTS_WOLRD,第二个参数表示采用的世界变换矩阵。
2.3.3 观察坐标系
在世界空间中,集合体和摄像机都是相对世界坐标系定义的,
取景变换(view space transformation),我们称变换后的集合体位于观察坐标系(view space)中,
取景变换矩阵(即观察矩阵)可由如下D3DX函数计算得到:
D3DXMATRIX *D3DXMaTrixLookAtlLH();
例如摄像机位于(5,3,-10),其观察点位世界坐标系的原点(0,0,0),我们就可以这样创建取景变换矩阵:
D3DXVECTOR3 position(5.0f,3.0f,-10.0f);
D3DXVECTOR3 targetPoint(0.0f,0.0f,0.0f);
D3DXVECTOR3 worldUp(0.0f,1.0f,0.0f);
D3DXMATRIX V;
D3DXMatrixLookAtLH(&v,&targetPoint,&worldUp);
Device->SetTransform(D3DTS_VIEW,&v);
2.3.4背面消隐
每个多边形都有两个侧面(sides),我们把其中的一个侧面标记为正面(front side),另一个侧面标记为背面(back side),通常,多边形的背面是看不见的,因为场景中的多数物体都是封闭的(enclosed volume),背面消隐(backface culling)。
Direct3D认为顶点排列顺序为顺时针(观察坐标系中)的三角形单元是正面朝向的,顶点排列顺序为逆时针(观察坐标系)的三角形单元是背面朝向的。
如果因为某种原因,默认的消隐方式不能满足用户的要求,我们可以通过修改绘制状态(render state)D3DRS_CULLMODE来达到目的。
Device->SetRenderState(D3DRS_CULLMODE,Value);
其中,value的值可以取:
D3DCULL_NONE 完全禁止背面消隐
D3DCULL_CW 只对顺时针绕序的三角形单元进行消隐
D3DCULL_CCW 默认值,只对逆时针绕序的三角形单元进行消隐
2.3.5 光照
光源是在世界坐标系中定义的,但必须经过取景变换到观察坐标系中才可以使用。
固定的流水线(fixed function pipeline)。
2.3.6裁剪
一个三角形单元与视域体的相对位置关系有以下三种:
。。完全在内部
。。完全在外部 将被踢出
。。部分在内(外),
2.3.7 投影
观察坐标系中,我们的任务是获取3D场景的2D表示,从N维变换到N-1维的过程称为投影(projection),
我们只关心透视投影(perspective projection),它可以产生“透视缩短(foreshortening)”的视觉效果,即近大远小,
投影变换定义了视域体,并负责视域体中的几何体投影到投影窗口,投影矩阵比较复杂,我们将使用D3DX函数,其功能是依据视域体的描述信息创建一个投影矩阵。
D3DXMATRIX *D3DMatriPerspectiveFovLH(D3DXMATRI &pout,
FLOAT fovy,FLOAT Aspect,FLOAT zn,FLOAT zf);
横纵比(aspect ratio)参数,投影窗口中的几何体最终将变换到屏幕显示区,从方形的投影窗口到矩阵的显示屏的变换会导致拉伸畸形(stretching distortion),所谓纵横比就是显示屏纵横两维尺寸的比率,常用于矫正由方形到矩形的映射而引发的畸形。
纵横比(aspect ratio)=屏幕宽度(screen width)/屏幕高度(screen height)。
Zn 近裁剪面到坐标原点的距离
Zf 远裁剪面到原点的距离
2.3.8 视口变换
视口变换(viewport transform)的任务就是将顶点坐标从投影窗口转换到屏幕的一个矩形区域中,该矩形区域成为视口(viewpoint)。
在Direct3D中,视口用结构D3DVIEWPORT9来表示。
Typedef strut D3DVIEWPORT9{
DWORD X,
DWORD Y,
DWORD width,
DWORD height,//前四个变量定义了矩形相对于父窗口的位置和大小
DWORD MINZ,//指定了深度缓存中的最小深度值。
DWORD MAXZ//深度缓存中最大深度值
}D3DVIEWPOER9;
注意,MINZ MAXZ的范围设定在【0,1】,除非你想最求某中特效,否则其值都应该限制在这个范围。
实例:
D3DVIEWPORT9 vp={0,0,640,480,0,1};
Device->SetViewport(&vp);
2.3.9 光栅化
光栅化(rasterization)的任务就是为了绘制每个三角形单元,如何计算构成三角形单元的每个像素的颜色值。光栅化的计算量非常大,我们一般要借助专门的图形卡的加速功能,光栅化的最终结构就是显示在屏幕上的一幅2D图像。
小结
在观察坐标系中,摄像机位于坐标原点,光轴方向与z轴正向一致,物体变换到观察坐标系后,就可以通过投影映射到投影窗口中,视口变换将投影窗口中的几何体在映射到视口中,最终在绘制流水线的光栅阶段计算出最终显示的2D图像的每个像素的颜色值。
第三章 Direct3D中的绘制
3.1 顶点缓存与索引缓存
顶点缓存和索引缓存(vertex/index buffer)是两类相似的接口。
为什么不用数组呢?因为顶点缓存和索引缓存可以被放置在显存(video memory)中,进行绘制时,使用显存中的数据将获得比使用系统内存中的数据开得多的绘制速度。
在代码中 ,顶点缓存用接口IDirect3DVertexBuffer9 表示,索引缓存用接口IDirect3DIndexBuffer9表示。
3.1.1
创建顶点缓存和索引缓存
HRESULT IDirect3DDevice9::CreateVertexBuffer(
UINT Length,//为缓存分配的字节数 如果有8个定点,则为 8*sizeof(Vertex)
DWORD Usage,////缓存的一些附加属性
DWORD FVF,//存储在定点缓存中顶点的灵活顶点格式
D3DPOOL POOL,//容纳缓存的内存池
IDirect3DVretexBuffer9 **ppvertexBuffer,//用于接收所创建的顶点缓存的指针
HANDLE *Psharehandle);//不使用,该值设为0
HRESULT IDirect3DDevice9 ::CreateIndexBuffer(
UINT Length,
DWORD Usage,
D3DFORMAT Format,//指定索引的大小,设为D3DFMT_INDEX16表示16位索引,
IDirect3DIndexBuffer9 **ppIndexBuffer,//用于接收所创建的索引缓存指针
HANDLE psharedhandle);
注意:
创建缓存时,如果未使用D3DUSAGE_DYNAMIC,则所创建的缓存为静态缓存(static Buffer),我们用静态缓存来存储静态数据(那些不需要经常修改或访问的数据),例如,地图和城市建筑的数据,如果使用的动态缓存,动态缓存一般放置在AGP存储区中,其内容可以迅速的更新,动态缓存的一个最大的优点是更新的速度非常的快,它的一个很常见的例子就是粒子系统,
3.1.2
访问缓存内容
可以借助方法Lock来获取指向缓存内容的指针,但访问完之后,必须对缓存进行解锁(unlock),
HRESULT IDirect3DVertexBuffer9::Lock(
UINT offsetToLock,从哪里开始锁定
UINT SizeToLock,//要锁定的长度(字节数)
BYTE **ppbdata,//指向被锁定的存储区位置的指针
DWORD flags//锁定的方式,
};
3.1.3 获取顶点缓存和索引缓存的信息
D3DVERTEXBUFFER_DESC vbDescription;
vertexBuffer->GetDesc(&vbDescription);
3.2 绘制状态
Direct3D封装了多种绘制状态(rendering state),这些绘制状态将影响几何体的绘制方式。
HRESULT IDirect3DDevice9::SetRenderState(
D3DRENDERSTATETYPE State,
DWORD Value);
3.3 绘制的准备工作
在绘制之前,我们还有三个步骤需要完成:
(1):指定数据流输入源。将顶点缓存和数据流进行连接。方法
HRESULT IDirect3DDevice9::SetStreamSource(
UINT,//标识与顶点缓存建立连接的数据流,我们总设为0
IDirect3DVertexBuffer9,//指向与给定数据流建立连接的顶点缓存的指针
UINT,//自数据流的起点的一个偏移量。单位为字节,
UIN);//将要连接到数据流的顶点缓存中每个元素的大小,单位为字节
例如:
_device->SetStreamSource(0,vb,0,sizeof(Vertex));
(2),这只顶点格式
_device->SetFVF(D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1);
(3),设置索引缓存
_device->SetIndices(_ib);
3.4 使用顶点缓存和索引缓存进行绘制
即使用DrawPrimitive 或DrawIndexedPrimitive方法将待绘制几何体的信息通过绘制流水线传输,这两个方法从顶点数据流中获取顶点信息,并从当前设定的索引缓存中提取索引信息。
3.5 D3DX几何体
D3DX库提供了如下6个网格创建函数:
。D3DXCreateBox
D3DXCreateSphere
D3DXCreateCylinder
D3DXCreateTeapot
D3DXCreatePolygon
D3DXCreateTorus
上述六个方法都非常的相似,都使用D3DX网格数据结构ID3DXMesh和ID3DXBuffer接口,
3.6 例程,三角形,立方体,茶壶,D3DXCreate*
第四章 颜色
这一章我们学习如何为几何体添加颜色
4.1 颜色的表示
1.在Direct3D中,颜色用RGB三元组来表示,三种颜色的加性混合(additive mixing)决定了最终的颜色。
2.RGB数据可用两种不同的格式来保存,第一种是D3DCOLOR,它实际上与DWORD类型完全相同,共有32位,Direct3D提供了D3DCOLOR_ARGB宏帮助我们完成这项工作,如:
D3DCOLOR brightRed=D3DCOLOR_ARGB(255,255,0,0);
D3DCOLOR_XRGB可以代替D3DCOLOR_ARGB;
在Direct3D中,存储颜色数据的另外一种结构是D3DCOLORVALUE,我们用单精度浮点数来度量每个颜色的亮度值,范围为0-1,
我们也可以用D3DXCOLOR替代D3DCOLORVALUE,且两种都有4个浮点类型的成员,这样我们就可以将其表达成一个4D向量,故其运算与向量完全一样,在D3DXCOLOR中颜色的乘法表示成各分量的值分别相乘。
4.2 顶点颜色
图元的颜色有构成该图元的顶点颜色决定。
Struct colorVertex{
Float x,y,z;
D3DCOLOR color;
Static const DWORD FVF;
}
Const DWORD ColorVertex::FVF=D3DFVF_XYZ|D3DFVF_DIFFUSE;
4.3着色(shading)
目前我们使用两种着色模式(shading mode),平面着色(flat shading)和Gouraud着色(gourand shading)。
如果使用平面着色模式,则每个图元的每个像素被一致的赋予该图元的第一个顶点所指定的颜色。平面着色容易使物体呈现块状,这是因为各颜色没有平滑的过渡,平滑着色(smooth shading),其中各图元的颜色由个顶点的颜色线性插值得到,
着色模式由Direct3D状态机(state maching)控制,
Device->SetRenderState(D3DRS_SHARDMODE,D3DSHADE_FLAT或者D3DSHADE_GOURAUD);
4.4 具有颜色的三角形
第五章 光照
光照(lighting)有助于描述实体形状和立体感。使用光照我们无需自行指定顶点的颜色值,Direct3D会将顶点送入光照计算引擎(light engine),依据光源类型,材质(material)以及物体表面相对于光源的朝向,计算出每个顶点的颜色值,基于某中光照模型计算出的个顶点的颜色,绘制结果更加的逼真。
5.1 光照的组成
在Direct3D的光照模型中,光源发出的光由以下三个分量或三种类型的光组成。
。环境光(Ambient light),反射
。漫射光(Diffuse light),这种类型光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射。漫射光方程(diffuse lighting equation).
.镜面光(specular light),这种类型的光沿着特定方向传播,光到达某一表面时,将严格的按照另一个方向反射。因为镜面光的计算量非常大,所以Direct3D为其提供了开关选项,一般都是关了的,如果要开启,必须:
Device->SetRenderState(D3DRS_SPECULARENALE,true);
每种类型的光都可以用结构D3DCOLORVALUE 或D3DXCOLOR来表示。这些类型描述了光线的颜色,
5.2 材质
材质允许我们定义物体表面对个颜色光的反射比例。材质结构用D3DMATERIAL9来表示。
Typedef struct D3DMATERIAL9{
D3DCOLORVALUE ;//材质对漫射光的反射率
D3DCOLORVALUE;//材质对环境光的反射率
D3DCOLORVALUE;镜面光的反射率
D3DCOLORVALUE;增强物体的亮度
Float ;镜面高光点的锐度(sharpness),该值越大,高光点的锐角越大
}D3DMATERIAL9,*LPD3DMATERIAL9;
具体使用例子见书
5.3 顶点法线 是一个描述多边形朝向的向量。
顶点法线(Vertex normal)正是基于上述思路而产生的,
光照计算是对每个顶点进行的,所以Direct3D需要知道表面再每个顶点处的局部朝向(法线方向),注意顶点法线和表面法线不一定相同,例如近似的求和园。
Struct Vertex{
Float x,y,z;
Float _nx,_ny,_nz;
Static const DWORD FVF;
};
Const DWORD Vertex::FVF=D3DFVF_XYZ|D3DFVF_NORMAL;
注意我们去掉了表示颜色的成员变量,这是因为我们将用光照计算顶点的颜色。
一种更好的计算顶点法向量的方法是计算法向量均值(normal averaging),
注意:在变换的过程中,顶点的法向量可能已经不再规范化,所以,要规范化
Device->SetRenderState(D3DRS_NORMALIZENORMALS,TRUE);
5.4 光源
Direct3D支持三种光源
。点光源(point light),在世界坐标系中有固定的位置
。方向光(directional lights),该光源没有位置信息,
。聚光灯(spot lights),这种类型的光源与手电筒类似。有位置信息,光线呈锥形(conical shape)。有两个角度,
在程序代码中,光源用结构D3DLIGHT9来表示。
5.5 例程 光照
为一个场景添加光照的步骤如下:
(1):启用光照
(2):为每个物体创建一种材质,并在绘制相应物体前启用(设置)该材质,
(3):创建一种或多种光源,设置并启用
(4):启用所用其余的光照状态,如镜面高光(specular highlightings)。
第六章 纹理映射(texture mapping)
借此此技术 ,我们可以将图像数据映射到三角形单元中,增加立体感,真实感。
在Direct3D中,纹理用接口IDirect3DTexture9表示,纹理类似于一个表面的像素矩阵,
6.1 纹理坐标
纹理元:
为了处理不同尺度的纹理,Direct3D将纹理坐标做了规范化处理,使之限定在区间【0,1】中。
为了实现该映射,必须修改顶点坐标。
增加:float _u,_v;
6.2 创建并启用纹理
HRESULT D3DXCreateTextureFromFile();
创建纹理
IDirect3DTextrue9 *_stone;
D3DXCreateTextureFromFile(_device,”stone.bmp”,&_stone);
设置当前纹理
Device->SetTexture(0,_stone);
禁用某一层纹理
Device->SetTexture(0,0);
renderObjectWithoutTexture();
6.3纹理过滤器
纹理过滤技术主要处理纹理三角形与屏幕三角形的大小不一样的问题。
Direct3D提供了3种类型的纹理过滤器(fileter)。纹理过滤方式可以用方法;
Device3DDevice->SetSamplerState来设置。
最近点采样(nearest point samping)。
线性纹理过滤(linear filtering)
各向异性纹理过滤(anisotropic filtering),但使用此纹理时,必须对D3DSAMP_MAXAISOTROPIC 水平值进行设定,改值越大,图像效果就越好。请调用IDirect3DDevice9::GetDeviceCaps函数检验返回D3DCAPS9结构参数。
Device->SetSamplerstate(0,D3DSAMP_MAXANISOTROPIC,4);
6.4 多级渐进纹理
多级渐进纹理链(Chain of mipmap),方法是,有某一纹理创建一系列分辨率逐渐减小的纹理图像,并且对每种分辨率下的纹理所采用的过滤方式进行定制,以保证保留那些较重要的细节。
6.4.1 多级渐进纹理过滤器
使用Device->SetSamplerState(0,D3DSAMP_MIPFILTER,filter);
Filter可以取不同的值,如:D3DTEXT_NONE,D3DTEXT_POINT,D3DTEXT_LINEAR.
6.4.2 使用多级渐进纹理
6.5 寻址模式
前面说过纹理坐标必须限制在【0,1】中,但这样是有问题的,Direct3D中定义了4中用来处理纹理坐标值超出【0,1】区间的纹理映射模式,它们分别是:
重复(wrap)寻址模式(address mode),边界颜色(border color )寻址模式,箝位(clamp)寻址模式,以及镜像(mirror)寻址模式。
6.6 例程 纹理四边形
现在来总结一下为场景添加纹理映射的步骤:
(1):构造组成物体的顶点,并为其指定纹理坐标
(2):用函数D3DXCreateTextureFromFile为IDirect3DTexture9接口加载一种纹理
(3):设置缩小过滤器,放大过滤器和多级渐进纹理过滤器
(4):绘制物体前,用函数IDirect3DDevice9::SetTexture来设定与该物体关联的纹理。
第七章 融合技术
融合(blending)技术,可以得到透明效果
7.1 融合方程
融合:将当前计算得到的像素(源像素)颜色值与先前计算得到的像素(目标)颜色值进行合成的做法称为融合。
注意:融合的效果并不局限于普通的玻璃类型的透明效果,
在融合运算时,必须遵循以下的原则:
首先绘制的那些物体不需要进行融合的物体,然后将需要进行融合的物体按照相对于摄像机的深度值进行排序,如果物体已处于观察坐标系中,该运算的效率会相当高,因为此时只需对z分量进行排序,最后,按照自后往前的顺序逐个绘制将要融合运算的物体。
源融合因子,目标融合因子,Direct3D中,默认状态下是禁止融合运算的,可以开启:
Device->SetRenderState(D3DRS_ALPHABLENDENABLE,true);
注意:融合的计算量并不小,当你绘制完需要进行融合的几何体之后,应该禁止Alpha融合,在对三角形单元组进行融合的时候,最好进行批处理,之后应立即绘制出来,这样可以避免在每帧图像中都启用和禁止融合运算。
7.2 融合因子
可以通过绘制状态D3DRS_SRCBLEND,D3DRS_DESTBLEND的值来对源融合因子和目标融合因子分别进行设定。
例如:
Device->SetRenderSate(D3DRS_SRCBLEND,Source);
Device->SetRenderState(D3DRS_DESTBLEND,Destination);
7.3 透明度
顶点颜色中的Alpha分量和材质。
Alpha分量主要用来指定像素的透明度(transparency)。
源融合因子和目标融合因子分别设置为D3DBLEND_SRCALPHA,D3DBLEND_INVSRCALPA.
7.3.1Alpha 通道
我们往往是从纹理的Alpha通道(channel)中获取Alpha信息。Alpha通道是保留给存储了Alpha分量的纹理元的一个额外的位集合(bits set)。
7.3.2 指定Alpha来源
如果当前的纹理拥有一个Alpha通道,Alpha值就取自该Alpha通道,如果没有Alpha通道,Alpha值就取自顶点的颜色。
7.4 用DirectX Textrue Tool创建Alpha通道
DDS文件之专门的一种为DirectX应用程序的纹理设计的图像格式。可使用D3DXCreateTextrueFromFile函数将DDS文件加载到纹理中,
7.5 例程 透明效果
融合处理的步骤总结如下:
(1):设置融合因子D3DRS_SRCBLEND ,D3DRS_DESTBLEND.
(2):如果使用Alpha分量,还需指定其来源
(3):启用Alpha融合绘制状态
第八章 模板
模板缓存(stencil Buffer)。模板缓存是一个用于获得某种特效(special effect)的离屏(off-screen)缓存,模板缓存的分辨率(resolution)与后台缓存(back Buffer)和深度缓存(depth Buffer)的分辨率完全相同,所以模板缓存与后台缓存和深度缓存中的像素是一一对应的,它允许我们动态的,有针对性的决定是否将某个像素写入后台缓存中。
8.1 模板缓存的使用
1.询问硬件是否支持,再启用 Device->SetRenderState(D3DRS_STENCILENABLE,TRUE/FALSE);
Device->Clear(0,0,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL,0Xff000000,1.0f,0);
D3DCLEAR_STENCIL表明我们要对模板缓存,目标缓存,深度缓存进行清空操作,第六个值为指定要将模板缓存清为何值。
8.1.1 模板缓存格式的查询
8.1.2 模板测试
我们可以使用模板缓存对后台缓存中的某些特定的区域进行绘制,判定是否将某个像素写入后台缓存的决策过程称为模板测试(stencil test),其表达式为:
(ref &mask)ComparisonOperation(value & mask)。
注:模板参考值(ref),模板掩码(mask)按位运算得到左操作数
返回值为bool型。
8.1.3 模板测试的控制
1.模板参考值
模板参考值(stencil reference value)ref的默认值为0,但可这样将其改为1
Device->SetRenderState(D3DRS_STENCILREF,0X1);
注意:使用的为16进制数
2.模板掩码(mask)
模板掩码用于屏蔽或隐藏ref和value变量中的某些值,例如:
Device->SetRenderState(D3DRS_STENCILMASK,0X0000ffff);
模板值。
我们不能显式的单独设置模板的值,但是可以对模板缓存进行清空操作。
下面我们介绍与模板相关的绘制状态:
比较运算:
我们可以通过绘制状态D3DRS_STENCILFUNC来设置比较运算(comparison operation),该比较运算函数可取自如下枚举类型:
Typedef enum D3DCAMPFUNC{
}
8.1.4 模板缓存的更新:
我们可以基于以下3种可能的情形定义模板缓存中的值如何进行更新:
一:第i行,第j列的像素模板测试失败,我们可以借助绘制状态D3DRS_STENCILFAIL将模板缓存中处于同样位置的项更新为如下:
Device->SetRenderState(D3DRS_STENCILFAIL,StencilOperation);
二:第i行,第j列的像素深度测试失败,这样设置:
Device->SetRenderState(D3DRS_STENCILZFAIL,StencilOperation);
三:第i行,第j列的像素深度测试和模板测试均成功,我们这样设置:
Device->SetRenderState(D3DRS_STENCILPASS,StencilOperation);
StencilOperation的值可取以下的常量:
8.1.5 模板写掩码
写掩码(write mask),该值可以屏蔽我们将写入模板缓存的任何值的某些位:
可用绘制状态D3DRS_STENCILWRITEMASK来设定写掩码的值,默认为0xffffffff。
下例我们将对高16位进行屏蔽:
Device->SetRenderState(D3DRS_STENCILWRITEMASK,0X0000ffff);
8.2 例程 镜面效果
8.2.1 成像中的数学问题:
D3DX库给我们提供了创建相对于任意平面的镜像变换矩阵:
D3DXMATRIX *D3DXMatrixReflect(
D3DXMATRIX *pout;
CONST D3DXPLANE *pPlane;
);