DirectX 总结

  1. 云栖社区>
  2. 博客>
  3. 正文

DirectX 总结

吞吞吐吐的 2017-10-12 11:07:00 浏览1390
展开阅读全文

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 * M* M* MS (在DirectX文档里多采用这种写法)

MTP最近,那么它是最先进行的变换,MR次之,MS再次。

很多书籍或文档里面说是按照从左到右的顺序连乘,但是别忘了顶点在公式中的位置

如果顶点在右边,那么就变成了下面的形式

M* M* M* PT (在以OpenGL为基础的图形学书籍里多采用这种写法)

所以准确的说应该是,在连乘式中,先进行的变换离被变换的顶点近。

注意如果P在连乘式右边,那么应该取其转置形式,否则无法与矩阵相乘。

为什么变换矩阵是4 x 4的

因为三维的矩阵无法完成某些变换,比如平移变换对应的矩阵如下

 

这用三维矩阵是无法实现的,类似的还有透视投影变换,所以在D3D中使用4×4矩阵来实现变换,又一个问题来了,由于3D中的顶点都是三维的,三维的vector和四维的matrix是无法相乘的,于是在三维的顶点后面再加上一维,形成齐次坐标,如下

 [x, y, z, w]

最后一维通常用w表示,而w通常取值1

这样就可以用一个1×4vector与一个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*v+ uy*vy + uz*v=|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。

 

==

本文转自zdd博客园博客,原文链接:http://www.cnblogs.com/graphics/archive/2009/11/25/1583682.html,如需转载请自行联系原作者

网友评论

登录后评论
0/500
评论
吞吞吐吐的
+ 关注