为DirectX游戏计算帧率
最近我写了自己的一个DirectX程序,使用的是自己的框架,以后我就会在我的DirectX程序框架上进行开发了。
首先我们需要探讨的是一个效率问题。在ACM程序中,是很讲究时空比的,也就是说一个程序要达到规定的时间和规定的内存容量这才算成功。那么游戏又何尝不是如此呢?相比一般的数值计算程序,游戏更讲究时效性,也就是说游戏开发人员可能为了达到渲染速度,可能会丢失一些渲染精度。这对程序员来说是一种很困难的抉择:是否选择现有的算法,还是放弃采取低精度的算法,亦或是开发出更有效的算法?为了衡量游戏的渲染效率,我们提出了FPS这个概念。FPS就是frame per second,简单来说就是一秒画多少帧。程序员花了那么多的时间,就是为了精益求精,但是这在游戏开发中是值得的。试想,如果一个游戏的帧率是15或者更低,那么谁会玩这么卡的游戏呢?基于这样的考量,游戏开发人员必须要想尽一切办法提高FPS。
以下是我电脑中部分游戏的截屏。(请注意右下角的FPS示意)
那么FPS是怎样测定的呢?有两种方法:一种是使用软件进行测量。我使用的是fraps,这样比较从客观的角度来衡量渲染的效率。另外也可以在已有的游戏代码中嵌入一些计算FPS的代码,这样做的好处是便于携带,如果想在别的机器上测定游戏的运行的效率,只需要将游戏复制到目的机器上就行了。事实上,使用软件测定的FPS会偏低,因为别的软件由于监视游戏的运行,会占用一定的CPU,而使用自己游戏的代码来查看虽然也占用CPU,但是渲染的部分是耗在GPU上的,给CPU的负担微乎其微。所以我建议大家在编辑DirectX游戏的时候,还是开发出一套计算帧率的代码,因为好处明摆着呢,况且实现也非常简单。
首先大家先想想,我们应该达到什么样的目的?我想大多数游戏给了我们答案。我们不想让自己游戏的FPS跳动太快,否则我们也看不清FPS到底是多少,但是我们也希望FPS能够随着时间的推移或者是用户的操作而产生变化。所以你应该猜得出每一秒FPS产生一次变化应当是理想的选择。那么FPS是怎样刚好在一秒内变化呢?这里我们应当想起了使用windows调用系统时间的API函数了。此外,我们也能够想到,计算出的FPS应当能够显示,因此我们可以使用D3DX字体,如果要求高一些,我们也可以使用纹理图案来显示。在这里,我使用了前者,因为它的操作最简单。下面我们就开始着手准备了。
- LPD3DXFONTg_FPSFont=NULL;
-
RECTg_FPSFontPos={WINDOW_WIDTH-100,WINDOW_HEIGHT-15,WINDOW_WIDTH,WINDOW_HEIGHT};
-
INTg_FrameCount=0;
-
INTg_lastTime=0;
-
INTg_currentTime=0;
-
CHARg_FPSstr[25]={0};
在上面的代码中,每一句我都添加了注释,在这里我就不复述了。随后我们在初始化游戏的时候添加创建字体的代码:
-
if(FAILED(D3DXCreateFont(g_JDevice,15,0,1,1,0,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,DEFAULT_QUALITY,
-
DEFAULT_PITCH|FF_DONTCARE,"Impact",&g_FPSFont)))returnFALSE;
创建字体的代码有些复杂,为了避免陷入具体的代码细节中,在这里我就略去了,这里有个链接,大家可以点击查看更多关于创建字体的内容。http://msdn.microsoft.com/en-us/library/bb172773(VS.85).aspx
然后就是我们的计数器代码了,虽然说计数器是我们这篇文章最有技术含量的内容,但它也非常简单。首先调用时间API函数,然后进行和前一秒记录的时间进行比对,如果超过了一秒,则更新当前的FPS,并且让前一秒的数值成为当前的数值,并且将帧计数器清零,否则帧计数器进行自加运算,并不更新FPS。下面就是我写的代码:
- g_currentTime=GetTickCount();
-
if(g_currentTime-g_lastTime>1000)
- {
-
sprintf_s(g_FPSstr,25,"当前FPS:%d",g_FrameCount);
- g_lastTime=g_currentTime;
- g_FrameCount=0;
- }
-
elseg_FrameCount++;
最后就是在渲染之中添加一个显示字体的代码了,代码如下面所示:
- g_FPSFont->DrawText(NULL,g_FPSstr,-1,&g_FPSFontPos,DT_CENTER,
-
D3DCOLOR_XRGB(255,255,255));
程序的显示效果如下图所示。为了符合一般游戏的规律,我们将FPS显示的内容尽量小些,并且放在右下角,以免影响玩家进行游戏。
下面我就这个程序做一点说明:
如果游戏是在窗口形式运行的话,如果我没有猜错的话,大家的FPS一定会小于等于60。因为桌面的刷新频率默认是60Hz。这点可以从“右键-属性-设置标签页-高级-监视器标签页”中可以看到。但是如果是全屏的游戏,则没有这个限制。一般来说,在D3DPRESENT_PARAMETERS::FullScreen_RefreshRateInHz这里进行修改,要让程序来识别可以使用的显示器模式,可以使用EnumAdapterModes()函数进行测试。
以下是我程序的全部代码:
-
-
-
#include<windows.h>
-
#include<d3d9.h>
-
#include<d3dx9.h>
-
#include<stdio.h>
-
-
#pragmacomment(lib,"d3d9.lib")
-
#pragmacomment(lib,"d3dx9.lib")
-
-
#defineJCLASSNAME"优化的程序"
-
#defineJCAPTION"程序演示"
-
#defineWINDOW_WIDTH320
-
#defineWINDOW_HEIGHT320
-
#defineSAVE_RELEASE(p)if(p)p->Release();p=NULL;
-
-
#pragmawarning(disable:4100)
-
-
-
LPDIRECT3D9g_JD3D=NULL;
-
LPDIRECT3DDEVICE9g_JDevice=NULL;
-
LPD3DXFONTg_FPSFont=NULL;
- RECTg_FPSFontPos={WINDOW_WIDTH-100,WINDOW_HEIGHT-15,
-
WINDOW_WIDTH,WINDOW_HEIGHT};
-
INTg_FrameCount=0;
-
INTg_lastTime=0;
-
INTg_currentTime=0;
-
CHARg_FPSstr[25]={0};
-
-
-
BOOLInitializeD3D(HWNDhWnd)
- {
- D3DDISPLAYMODEdisplayMode;
-
-
if((g_JD3D=Direct3DCreate9(D3D_SDK_VERSION))==NULL)
-
returnFALSE;
-
-
if(FAILED(g_JD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&displayMode)))
-
returnFALSE;
-
- D3DPRESENT_PARAMETERSjd3dpp;
-
ZeroMemory(&jd3dpp,sizeof(jd3dpp));
-
- jd3dpp.Windowed=TRUE;
- jd3dpp.BackBufferWidth=WINDOW_WIDTH;
- jd3dpp.BackBufferHeight=WINDOW_HEIGHT;
- jd3dpp.SwapEffect=D3DSWAPEFFECT_DISCARD;
- jd3dpp.BackBufferFormat=displayMode.Format;
- jd3dpp.EnableAutoDepthStencil=TRUE;
- jd3dpp.AutoDepthStencilFormat=D3DFMT_D16;
-
-
if(FAILED(g_JD3D->CreateDevice(D3DADAPTER_DEFAULT,
- D3DDEVTYPE_HAL,hWnd,D3DCREATE_SOFTWARE_VERTEXPROCESSING,
- &jd3dpp,&g_JDevice)))
-
returnFALSE;
-
- g_JDevice->SetRenderState(D3DRS_LIGHTING,FALSE);
- g_JDevice->SetRenderState(D3DRS_ZENABLE,D3DZB_TRUE);
-
-
if(FAILED(D3DXCreateFont(g_JDevice,15,0,1,1,0,DEFAULT_CHARSET,
- OUT_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_DONTCARE,
-
"Impact",&g_FPSFont)))returnFALSE;
-
returnTRUE;
- }
-
-
-
voidReleaseMemory(void)
- {
- SAVE_RELEASE(g_JD3D);
- SAVE_RELEASE(g_JDevice);
- SAVE_RELEASE(g_FPSFont);
- }
-
-
-
voidRenderScene(void)
- {
-
- g_currentTime=GetTickCount();
-
if(g_currentTime-g_lastTime>1000)
- {
-
sprintf_s(g_FPSstr,25,"当前FPS:%d",g_FrameCount);
- g_lastTime=g_currentTime;
- g_FrameCount=0;
- }
-
elseg_FrameCount++;
-
- g_JDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
-
D3DCOLOR_XRGB(0,0,0),1.0f,0);
-
-
- g_JDevice->BeginScene();
- g_FPSFont->DrawText(NULL,g_FPSstr,-1,&g_FPSFontPos,DT_CENTER,
-
D3DCOLOR_XRGB(255,255,255));
-
-
- g_JDevice->EndScene();
- g_JDevice->Present(NULL,NULL,NULL,NULL);
- }
-
-
-
HRESULTCALLBACKMyAppProc(HWNDhWnd,UINTmsg,WPARAMwParam,LPARAMlParam)
- {
-
switch(msg)
- {
-
caseWM_DESTROY:
- PostQuitMessage(0);
-
return0;
-
caseWM_KEYDOWN:
-
if(wParam==VK_ESCAPE)PostQuitMessage(0);
-
return0;
- }
-
return(HRESULT)DefWindowProc(hWnd,msg,wParam,lParam);
- };
-
-
-
INTWINAPIWinMain(HINSTANCEhInst,HINSTANCEhPrevInst,LPSTRcmd,INTshow)
- {
-
-
WNDCLASSEXjWndCls={sizeof(jWndCls),CS_CLASSDC,MyAppProc,0,0,hInst,
- NULL,LoadCursor(NULL,IDC_ARROW),0,NULL,JCLASSNAME,NULL};
- RegisterClassEx(&jWndCls);
-
-
HWNDhWnd=CreateWindow(JCLASSNAME,JCAPTION,WS_CAPTION|WS_SYSMENU,100,40,
- WINDOW_WIDTH,WINDOW_HEIGHT,GetDesktopWindow(),NULL,jWndCls.hInstance,NULL);
-
if(hWnd==NULL)
-
returnFALSE;
- ShowWindow(hWnd,SW_SHOWDEFAULT);
- UpdateWindow(hWnd);
-
-
if(InitializeD3D(hWnd)==FALSE)
-
returnFALSE;
-
- MSGmsg;
-
ZeroMemory(&msg,sizeof(msg));
-
while(msg.message!=WM_QUIT)
- {
-
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
- {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
else
- {
-
- RenderScene();
- }
- }
-
- ReleaseMemory();
-
- UnregisterClass(JCLASSNAME,jWndCls.hInstance);
-
return(INT)msg.wParam;
- }
我更多的DirectX程序,请看:
为自己创作一个好的DirectX程序风格
设计DirectX游戏开头动画效果
设计DirectX游戏开头画面淡入淡出的效果
我的游戏:走迷宫(DirectX版)
好了,今天就说到这里了,祝愿所有喜欢游戏开发的同学们能够实现自己开发游戏的梦想!我又得回去学习我的数据结构了,顺便说一声了,我看到了关键路径来了,而我们的老师讲课的速度也太慢了吧。图都还没有讲呢。
分享到:
相关推荐
DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX 游戏开发终极指南DirectX ...
《DirectX游戏开发终极指南》涵盖了游戏开发过程和方方面面,书中以一个名为Straned的第一人称射击游戏开发过程为主题展开介绍,首先介绍了DirectX和Direct3D图形学,然后逐章介绍了游戏引擎和完整游戏的开发过程。...
DirectX游戏开发终极指南pdf版+源码,可以作为初学者的入门书籍。
一个DirectX游戏的源码,采用vc++开发的三维游戏,很有学习价值哦!o(∩_∩)o...
DirectX游戏开发实例
《directX游戏编程》光盘源代码 《directX游戏编程》光盘源代码
很好的directx游戏编程入门教程,特别适用于初学者。下下来看看吧!
DirectX 12 3D游戏开发实战
第1章 DirectX导论 第2章 游戏:Stranded 第3章 Direct3D光照和物体 第4章 纹理 第5章 Direct3D文本和图形用户界面 第6章 特效 第7章 基本脚本系统 第8章 游戏数学回顾 第9章 碰撞检测 第10章 输入检测和...
DirectX3D游戏源代码 DirectX3D游戏源代码 DirectX3D游戏源代码 DirectX3D游戏源代码 DirectX3D游戏源代码
DirectX游戏开发终极指南,初学DirectX游戏编程的宝典.包括书上大部分的源代码.如需要光盘全部内容.可以联系:exuan516@hotmail.com
DirectX游戏程序设计
DirectX游戏开发入门.pdf
DirectX游戏编程基础教程-王德才 教材配套源代码 所有的代码都有
directx游戏编程基础教程,DirectX最初是为了弥补Windows 3.1对图形、声音处理能力的不足,作为一个附加工具包,用于在Windows平台上开发游戏。现在DirectX已经成为Windows系统自身的重要组成部分,以支持各种现代...
DirectX游戏编程 (游戏开发技术系列丛书) - 王鹏杰
《DirectX 游戏开发终极指南》EXE电子书!!!!游戏开发推荐!!!!
DirectX 游戏开发 不错的资源啊 值得下载收藏
美国游戏编程大师Allen Sherrod编写的Ultimate Game Programming with DirectX(第一版)的所有源代码。这本书围绕一个FPS游戏进行讲述,最后得到一个比较简单的3D的FPS游戏。英文版的电子书网上找不到,不过代码...
用DirectxDraw实现基本绘图,用DirectxInput 实现游戏基本操作 4.首次学习并使用DirectxSound组件实现了混音效果。 5.尝试封装了Directx的这三个重要组件。 5.本人为非专业编程人员,代码难免有的地方比较乱和不...