directD学习笔记.docx
《directD学习笔记.docx》由会员分享,可在线阅读,更多相关《directD学习笔记.docx(36页珍藏版)》请在冰豆网上搜索。
directD学习笔记
DirectXSDK2006学习笔记1——框架
2008-01-3113:
20
友情提醒:
所谓的框架是指SDK目录下\Samples\C++\Common路径下的DXUT系列函数包装。
学习框架的前提是必须有足够的WindowsAPI,GUI编程经验,必须熟悉Windows的消息机制,回调机制,最好有万行左右的C/C++编程经验。
MFC在这里没有任何用处。
另外我觉得最好在看程序之前对于D3D的所有概念有点了解,什么是vertex,texture,matrix,lighting,mesh等等,以及相关的数学概念。
这些都可以在网上找到中文翻译,帮助你快速入门。
DXSDK2006和2003版的比起来更新了不少东西,比如DirectX10,还有Managed
DirectX等等。
不过我关心的还是D3D9。
除了个别接口的更改之外,DXSDK2006还提供了一套图形控件的类库,它的界面还是很漂亮的:
)如图:
学习一个框架还是从它的入口学习比较方便,否则容易迷失在无穷无尽的API和层层包装之中。
DXSDK2006的框架和2003版的DX9.0c框架有很大的不同。
首先是2003版的框架中提供了一个CD3DApplication类,这个类对于初始化,清除,以及游戏窗口的创建,游戏主循环进行了包装。
这是一个不错的类,不知道为什么在2006版中去掉了。
不过不要紧,2006版的框架中提供的一些C包装函数已经足够了。
在看这些函数之前,我们还是先来看看SDK目录下\Samples\C++\Direct3D\Tutorials中有些什么吧。
Tut01_CreateDevice是创建框架,这个程序不用框架,研究一下有助于了解D3D的大致工作流程。
下面是winmain函数中的一部分。
//InitializeDirect3D
if(SUCCEEDED(InitD3D(hWnd)))
{
//Showthewindow
ShowWindow(hWnd,SW_SHOWDEFAULT);
UpdateWindow(hWnd);
//Enterthemessageloop
MSGmsg;
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
在消息循环之前有个初始化设备的函数InitD3D(hWnd),其代码如下:
HRESULTInitD3D(HWNDhWnd)
{
if(NULL==(g_pD3D=Direct3DCreate9(D3D_SDK_VERSION)))
returnE_FAIL;
D3DPRESENT_PARAMETERSd3dpp;
ZeroMemory(&d3dpp,sizeof(d3dpp));
d3dpp.Windowed=TRUE;
d3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat=D3DFMT_UNKNOWN;
if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,
&g_pd3dDevice)))
{
returnE_FAIL;
}
returnS_OK;
}
主要是调用Direct3DCreate9和g_pD3D->CreateDevice这两个函数。
查看DXSDK文档中关于D3DPRESENT_PARAMETERS的定义,大致了解一下。
接下来要关心的就是消息循环了,在回调函数MsgProc中处理了两个消息,一个是WM_DESTROY,里面调用了Cleanup函数,另一个是WM_PAINT函数,里面调用了Render函数。
Cleanup函数很简单,就是调用D3D对象及其设备对象的Release函数释放资源,而Render函数就是D3D中最重要的函数了。
VOIDRender()
{
if(NULL==g_pd3dDevice)
return;
//Clearthebackbuffertoabluecolor
g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,255),1.0f,0);
//Beginthescene
if(SUCCEEDED(g_pd3dDevice->BeginScene()))
{
//Renderingofsceneobjectscanhappenhere
//Endthescene
g_pd3dDevice->EndScene();
}
//Presentthebackbuffercontentstothedisplay
g_pd3dDevice->Present(NULL,NULL,NULL,NULL);
}
主要调用的函数有BeginScene,EndScene和Present函数。
对D3D应用程序有了大概了解之后就可以看空框架程序了。
这个程序可以在Samples\C++\Direct3D\EmptyProject中找到。
从WinMain中的调用可以看到,框架首先设定一堆回调函数,很多事情的是在用户自己写的回调函数中实现。
从DXUTInit开始,程序开始调用框架内的API来完成初始化——创建窗口——创建设备——主消息循环——退出等一系列操作。
调查Common目录下DXUT.cpp文件就可以发现DXUTInit函数干了以下几件事情
① 设定开始调用这个函数的标志符
② InitCommonControls
③ 保存当前的sticky/toggle/filter键
④ 通过事先导入winmm.dll的方法timeBeginPeriod来确保调用Sleep的准确性
⑤ 设定一些标志附,读取命令行参数
⑥ 检查版本
⑦ 获得D3D对象指针。
值得一提的是框架中大部分全局变量是通过类DXUTState的静态变量state的get/set方法得到的。
这些get/set方法是用宏定义的,里面调用了加锁和解锁,因此保证了全局变量设定的线程安全。
这些全局性的变量包括D3D对象指针,D3D设备对象指针,BackBufferSurfaceDesc,DeviceCaps,窗口HINSTANCE,窗口句柄HWND,焦点句柄HWNDFocus,全屏设备句柄,窗口设备句柄,窗口客户端矩形,模式切换时窗口客户端矩形,模式切换时全屏客户端矩形,Time,ElapsedTime,FPS数,窗口标题,设备数据DeviceStats,以及是否暂停渲染,时间是否暂停,窗口是否激活等标志,一些窗口事件等等。
这些都可以通过DXUTGETXXX/DXUTSETXXX/DXUTISXXX系列包装函数获得。
⑧ 通过DXUT_Dynamic_Direct3DCreate9创建D3D对象。
很多D3D底层API都是通过动态的方式加载的,这样有利于效率的提高。
⑨ 重设全局时钟
⑩ 设定DXUTInited为true。
很多DXUT系列的函数都喜欢在入口设定一个开始调这个函数的标志,在出口设定一个这个函数已经被调过的标志,这样可以在以后再次调用这个函数的时候了解当前什么工作已经做了,什么工作没做需要补做。
我想这个主要是用来防止函数重入问题的吧。
其他函数中的这一对函数就不再提了
呼~第一个函数大致看完了,接下来是DXUTCreateWindow函数。
什么?
要问DXUTSetCursorSettings为什么被无视?
因为这个函数不重要。
DXUTCreateWindow的工作大致是这样的
① 判断关于设备的CallBack有没有设定好
② 判断DXUTInit()有没有被调用成功(注意不是有没有调用)。
③ 获得焦点句柄,因为窗口还没有创建,所以这个句柄应该是NULL
④ 设定HInstance
⑤ 设定窗口类
⑥ 注册窗口类
⑦ 设定窗口位置和大小。
好长一段代码,汗
⑧ 创建窗口。
终于。
。
。
⑨ 设定窗口焦点句柄,全屏设备句柄,窗口设备句柄
接下来的函数是DXUTCreateDevice。
这个函数就是用来选择最优设备并创建的。
① 设定参数中的回调函数和上下文,以备后用
② 检查窗口是否被成功创建,否则再调用一次DXUTCreateWindow
③ 枚举所有可能的显示模式。
枚举过程非常复杂,用到了CD3DEnumeration中的一些包装函数,这些设备信息包括分辨率,颜色位深等等。
这里会用到DXUTCreateDevice传进来的参数IsDeviceAcceptable
④ 如果命令行设定过显示模式,那么将刚才得到的信息覆盖。
⑤ 采用某种权重的算法找出最优显示模式(DXUTFindValidDeviceSettings)
⑥ 切换设备。
这里用到了DXUTCreateDevice传进来的参数ModifyDeviceSettings。
切换设备时要考虑很多问题:
比如需要暂时忽略WM_SIZE消息;只有在第一次创建设备的时候才用命令行参数;按照需要调用DXUTCreate3DEnvironment和DXUTReset3DEnvironment;分全屏和窗口设备重设;重设完了根据需要处理WM_SIZE消息;显示窗口,允许WM_SIZE消息等等
最后是DXUTMainLoop。
① 检查是否有重入问题
② 设定进入主循环标志
③ 检查设备是否已经被成功创建,没创建的话用默认参数创建一次
④ 检查前面三个函数是否成功调用。
汗,又是检查
⑤ 处理窗口消息,注意只有在没有消息处理的时候才调用DXUTRender3DEnvironment()
⑥ 在消息循环退出之后清除加速表。
应该是类似SHIFT+X这种键盘加速表的清除吧
⑦ 更改主循环标志
还是有必要看一下主消息循环中的DXUTRender3DEnvironment
① 检查设备是否丢失
② 在窗口模式下检查桌面分辨率位深设定,以便重设设备
③ 尝试重设设备DXUTReset3DEnvironment
④ 判断上次渲染到现在时间(elapsedtime)决定是否要进行渲染
⑤ 调用用户的FrameMove函数
⑥ 调用用户的FrameRender函数
⑦ 调用Present函数
⑧ 更新当前Frame
⑨ 根据命令行检查是否需要关闭应用程序
主函数看完之后,剩下的就是一些回调函数了。
要正确使用这些回调函数,除了知道它们的作用之外,还需要知道这些函数是何时被调用的。
下面是调用顺序
∙程序启动:
InitApp→MsgProc→IsDeviceAcceptable→ModifyDeviceSettings→OnCreateDevice→OnResetDevice→渲染主循环
∙渲染主循环:
OnFrameMove→OnFrameRender
∙改变设备:
ModifyDeviceSettings→OnLostDevice→根据需要调用OnDestroyDevice→OnResetDevice→渲染主循环
∙程序退出:
OnLostDevice→OnDestroyDevice
下面是各函数的作用:
InitApp
初始化一些图形控件和GUI的消息处理函数
OnCreateDevice
创建设备时的回调函数,用于创建D3DPOOL_MANAGED资源
OnResetDevice
重设设备时的回调函数,用于创建D3DPOOL_DEFAULT资源
OnFrameMove
动画实现处,常用于矩阵转换等操作
OnFrameRender
渲染实现处,常用于渲染场景
OnLostDevice
设备丢失时的回调函数,释放由OnResetDevice创建的资源
OnDestroyDevice
设备析构时的回调函数,释放由OnCreateDevice创建的资源
IsDeviceAcceptable
创建设备时用来对所有可用设备进行过滤的函数
ModifyDeviceSettings
更改设备时的回调函数,用于实现更改设备时所需做的其他操作
MsgProc
安排各空件处理消息的顺序
OnGUIEvent
程序控件绑定的消息处理回调函数
以上函数均可以更换名字,这里只是用框架默认的函数名字。
DirectXSDK2006学习笔记2——顶点缓冲
2008-01-3113:
22
D3D9以一种比较易于理解的方式让程序员来组织游戏画面,这种方式就是顶点缓冲。
程序员可以自己定义一组记录多边形定点颜色,纹理位置等的数组,让D3D9去自动生成多边形内部每个像素的信息。
为了和以后的vertexshader相区别,我们现在谈论的都是固定功能的顶点处理(Fixed-Function)。
上面一段话似乎有点晦涩,让我们首先来看看这些术语的定义吧:
∙顶点缓冲VertexBuffer:
由用户定义的,包含顶点信息的数组。
首先我们来学习一下VertexBuffer的技术。
我喜欢先看代码再讲理论,否则看了理论也找不到北。
下面就是一个自定义的顶点缓冲的结构
structCUSTOMVERTEX
{
FLOATx,y,z,rhw;//Thetransformedpositionforthevertex
DWORDcolor; //Thevertexcolor
};
其中,x,y,z是顶点在3维空间的最终位置,rhw是3维矩阵的倒数(不明白的话找本图形学的书研究一下),color自然就是顶点的颜色了。
由于这个结构是自定义的,所以我们需要告诉D3D应该如何识别这个结构,这就需要我们定义一个常量了:
//OurcustomFVF,whichdescribesourcustomvertexstructure
#defineD3DFVF_CUSTOMVERTEX(D3DFVF_XYZRHW|D3DFVF_DIFFUSE)
这个D3DFVF_CUSTOMVERTEX就是用来告诉D3D上面那个CUSTOMVERTEX结构是如何组织的:
首先是D3DFVF_XYZRHW,即顶点在3维坐标系的最终位置,D3DFVF_DIFFUSE是色彩模式,告诉D3D紧接着D3DFVF_XYZRHW信息的是顶点的色彩信息,并且这种色彩信息是漫反射模式的。
类似的标志可以学习DirectXSDK文档(搜索D3DFVF),这里就不再赘述。
上面所提到的顶点缓冲定义方式就是为大家熟知FlexibleVertexFormat,简称FVF,是不是有点眼熟呢。
下面是Direct3D游戏编程入门一书中对复杂的FVF举的一个例子,供参考
typedefstructSObjVertex
{
FLOATx,y,z; //position
FLOATnx,ny,nz; //normal
DWORDdiffuse; //diffusecolor
DWORDspecular; //specularcolor
FLOATtu,tv; //firstpairoftexturecoordinates
FLOATtu2,tv2,tw2; //secondpairoftexturecoordinates
FLOATtu3,tv3; //thirdpairoftexturecoordinates
FLOATtu4,tv4; //fourthpairoftexturecoordinates
}SObjVertex;
constDWORDgSObjVertexFVF=(D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_SPECULAR|D3DFVF_NORMAL|D3DFVF_TEX4|D3DFVF_TEXCOORDSIZE2(0)|D3DFVF_TEXCOORDSIZE3
(1)|D3DFVF_TEXCOORDSIZE2
(2)|D3DFVF_TEXCOORDSIZE2(3));
看完了FVF顶点格式的定义,就可以看它是如何使用的。
使用方法非常简单,在OnCreateDevice中添加创建和初始化代码,在OnFrameRender中添加渲染代码就可以了,具体如下:
LPDIRECT3DVERTEXBUFFER9g_pVB=NULL;//Buffertoholdvertices
HRESULTCALLBACKOnCreateDevice(IDirect3DDevice9*pd3dDevice,constD3DSURFACE_DESC*pBackBufferSurfaceDesc,void*pUserContext)
{
…
//Initializethreeverticesforrenderingatriangle
CUSTOMVERTEXvertices[]=
{
//x,y,z,rhw,color
{150.0f,50.0f,0.5f,1.0f,0xffff0000,},
{250.0f,250.0f,0.5f,1.0f,0xff00ff00,},
{50.0f,250.0f,0.5f,1.0f,0xff00ffff,},
};
if(FAILED(pd3dDevice->CreateVertexBuffer(
3*sizeof(CUSTOMVERTEX),
0,D3DFVF_CUSTOMVERTEX,
D3DPOOL_MANAGED,&g_pVB,NULL)))
{
returnE_FAIL;
}
//Nowwefillthevertexbuffer.Todothis,weneedtoLock()
//theVBtogainaccesstothevertices.Thismechanismis
//requiredbecuasevertexbuffersmaybeindevicememory.
VOID*pVertices;
if(FAILED(g_pVB->Lock(0,sizeof(vertices),
(void**)&pVertices,0)))
returnE_FAIL;
memcpy(pVertices,vertices,sizeof(vertices));
g_pVB->Unlock();
}
为了统一和强调精度,D3D采用了float作为其主要的数值类型。
上面的程序调用了CreateVertexBuffer和LPDIRECT3DVERTEXBUFFER9->Lock和 Unlock函数。
另外需要在OnFrameRender中的BeginScene和EndScene中调用如下代码
pd3dDevice->SetStreamSource(0,g_pVB,0,sizeof(CUSTOMVERTEX));
pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,1);
其中SetStreamSource函数第一个参数StreamNumber用来设定使用哪条数据流,这个数据流的最大值根据显卡硬件的不同而不同,现在好的显卡可以支持8条或者16条数据流。
SetFVF函数用来把我们自定义的顶点缓冲布局常量传递给D3D。
DrawPrimitive函数用来告诉D3D以那种图元绘制这个图形。
其中图元这个概念很重要,它是D3D世界中最基本的单位,分为点列表,线列表,线带,三角形列表,三角形带,三角扇形六中,在D3D中的具体定义如下:
typedefenum_D3DPRIMITIVETYPE
{
D3DPT_POINTLIST=1,
D3DPT_LINELIST=2,
D3DPT_LINESTRIP=3,
D3DPT_TRIANGLELIST=4,
D3DPT_TRIANGLESTRIP=5,
D3DPT_TRIANGLEFAN=6,
D3DPT_FORCE_DWORD=0x7fffffff,
}D3DPRIMITIVETYPE;
其中点列表,线列表,三角形列表很好理解,它们就是独立的点/线/三角形的集合,每个顶点缓冲中的点分别表示一个点/独立线条中的一个端点/独立三角形中的一个顶点。
而线带,三角形带每个顶点缓冲中的点表示连续的线条的端点/连续三角形中的顶点。
三角扇形顶点缓冲中的点表示三角扇形中的每个顶点,如图:
三角形列表 三角形