1、DirectX:是底层图形库的API,支持3D硬件加速,可以通过软件接口来控制图形硬件。
2、COM:(组件对象模型)让DirectX能够独立编程并且向后兼容。通常,COM指的是接口,在使用C++进行DirectX编程时,COM的细节是隐藏不可见的。我们获取COM组件接口的指针时是通过调用别的COM组件接口的方法来实现的,我们并不能用C++中的new关键字来创建接口对象,在释放接口的时候也要使用它提供的Release方法,而不是使用delete。COM对象拥有它自己的内存管理机制。
3、Texture纹理数据格式
2D纹理贴图是矩阵中的数据元素。每个元素存储着像素的颜色。不仅仅只保存颜色,在法线贴图中,存储的是3D vector向量。存储的数据也是有格式要求的。
4、交换链
为了避免动画闪烁的情况,在绘制的时候采用双重缓冲绘制的方式。硬件拥有两块缓冲区,前台缓冲区和后台缓冲区,当前显示的图像存储在前台缓冲区,下一帧将要显示的图像存储在后台缓冲区。到了显示下一帧的时刻,两个缓冲区交换,前台缓冲区变成后台缓冲区,后台缓冲区变成前台缓冲区。
5、Depth Buffering 深度缓冲区
在深度缓冲区中存储的不是图像数据,而是像素的深度信息,范围是0-1。0表示离观察者最近,1表示最远。只有最靠近观察者的像素才会被渲染,形成了遮挡关系。深度缓冲区属于纹理,因此也有数据格式。
6、纹理资源视图
Texture可以绑定到渲染管线里不同的阶段上,比如渲染或者着色器资源上。为了效率,SDK指出,将资源视图绑定到管线上可以减少运行时的类型检查。
7、Multisampling多重采样
在显示器上像素点是有大小的,不能很好的展示斜线。会有像阶梯一样的锯齿,通过提高分辨率来缩小像素点可以让锯齿大大减少。当增加分辨率不能很好的去掉锯齿时,我们可以使用抗锯齿技术。通过超级采样,在后台缓冲区的图像是屏幕分辨率的四倍大小,当后台缓冲区切换到前台时,通过降低采样合并像素,通过软件的办法来实现抗锯齿,但是代价是很大的,需要消耗四倍资源。DirectX支持多重采样,消耗更少资源。同样使用4X多重采样,超级采样需要显示每个像素,而多重采样会将四个像素取平均显示。
8、Feature Level 特性等级
Feature Level对应着DirectX9-11当使用者的硬件平台不支持当前的feature level时,就会回到更老的feature level。所以为了更好的支持大部分硬件平台,应用程序会首先检查支持的DirectX版本。
Direct3D初始化的过程可以总结为:
1、使用D3D11CreateDevice创建ID3D11Device和ID3D11DeviceContext接口。
2、使用ID3D11Device::CheckMultisampleQualityLevels检查4X MSAA等级。
3、配置Swap Chain。
4、IDXGIFactory实例来创建设备,创建IDXGISwapChain实例。
5、为后台缓冲区的指定渲染目标视图。
6、创建深度/模板缓冲区和关联的深度/模板视图。
7、绑定渲染目标视图和深度缓冲区视图。
8、设置视口。
为了正确的播放动画,需要记录时间,尤其是动画播放帧之间的时间。为了完成时间记录,使用<windows.h>中的the performance timer。这个timer记录的单位叫做counts。通过自定义GameTimer来完成时间的记录,在消息循环中调用。
书中提供了一个通用的D3D11程序框架,方便开发。
//*************************************************************************************** // d3dUtil.h by Frank Luna (C) 2011 All Rights Reserved. //*************************************************************************************** #ifndef D3DUTIL_H #define D3DUTIL_H #if defined(DEBUG) || defined(_DEBUG) #define _CRTDBG_MAP_ALLOC #include <crtdbg.h> #endif #include <d3dx11.h> #include <xnamath.h> #include <DxErr.h> #include <cassert> #include <ctime> #include <algorithm> #include <string> #include <sstream> #include <fstream> #include <vector> //--------------------------------------------------------------------------------------- // Simple d3d error checker for book demos. //--------------------------------------------------------------------------------------- #if defined(DEBUG) | defined(_DEBUG) #ifndef HR #define HR(x) \ { \ HRESULT hr = (x); \ if (FAILED(hr)) \ { \ DXTrace(__FILE__, (DWORD)__LINE__, hr, L#x, true); \ } \ } #endif #else #ifndef HR #define HR(x) (x) #endif #endif //--------------------------------------------------------------------------------------- // Convenience macro for releasing COM objects. //--------------------------------------------------------------------------------------- #define ReleaseCOM(x) { if(x){ x->Release(); x = 0; } } //--------------------------------------------------------------------------------------- // Convenience macro for deleting objects. //--------------------------------------------------------------------------------------- #define SafeDelete(x) { delete x; x = 0; } //--------------------------------------------------------------------------------------- // Utility classes. //--------------------------------------------------------------------------------------- class d3dHelper { public: ///<summary> /// /// Does not work with compressed formats. ///</summary> static ID3D11ShaderResourceView* CreateTexture2DArraySRV( ID3D11Device* device, ID3D11DeviceContext* context, std::vector<std::wstring>& filenames, DXGI_FORMAT format = DXGI_FORMAT_FROM_FILE, UINT filter = D3DX11_FILTER_NONE, UINT mipFilter = D3DX11_FILTER_LINEAR); static ID3D11ShaderResourceView* CreateRandomTexture1DSRV(ID3D11Device* device); }; class TextHelper { public: template<typename T> static D3DX11INLINE std::wstring ToString(const T& s) { std::wostringstream oss; oss << s; return oss.str(); } template<typename T> static D3DX11INLINE T FromString(const std::wstring& s) { T x; std::wistringstream iss(s); iss >> x; return x; } }; // Order: left, right, bottom, top, near, far. void ExtractFrustumPlanes(XMFLOAT4 planes[6], CXMMATRIX M); // #define XMGLOBALCONST extern CONST __declspec(selectany) // 1. extern so there is only one copy of the variable, and not a separate // private copy in each .obj. // 2. __declspec(selectany) so that the compiler does not complain about // multiple definitions in a .cpp file (it can pick anyone and discard // the rest because they are constant--all the same). namespace Colors { XMGLOBALCONST XMVECTORF32 White = { 1.0f, 1.0f, 1.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Black = { 0.0f, 0.0f, 0.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Red = { 1.0f, 0.0f, 0.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Green = { 0.0f, 1.0f, 0.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Blue = { 0.0f, 0.0f, 1.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Yellow = { 1.0f, 1.0f, 0.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Cyan = { 0.0f, 1.0f, 1.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Magenta = { 1.0f, 0.0f, 1.0f, 1.0f }; XMGLOBALCONST XMVECTORF32 Silver = { 0.75f, 0.75f, 0.75f, 1.0f }; XMGLOBALCONST XMVECTORF32 LightSteelBlue = { 0.69f, 0.77f, 0.87f, 1.0f }; } ///<summary> /// Utility class for converting between types and formats. ///</summary> class Convert { public: ///<summary> /// Converts XMVECTOR to XMCOLOR, where XMVECTOR represents a color. ///</summary> static D3DX11INLINE XMCOLOR ToXmColor(FXMVECTOR v) { XMCOLOR dest; XMStoreColor(&dest, v); return dest; } ///<summary> /// Converts XMVECTOR to XMFLOAT4, where XMVECTOR represents a color. ///</summary> static D3DX11INLINE XMFLOAT4 ToXmFloat4(FXMVECTOR v) { XMFLOAT4 dest; XMStoreFloat4(&dest, v); return dest; } static D3DX11INLINE UINT ArgbToAbgr(UINT argb) { BYTE A = (argb >> 24) & 0xff; BYTE R = (argb >> 16) & 0xff; BYTE G = (argb >> 8) & 0xff; BYTE B = (argb >> 0) & 0xff; return (A << 24) | (B << 16) | (G << 8) | (R << 0); } }; #endif // D3DUTIL_H
//*************************************************************************************** // d3dApp.cpp by Frank Luna (C) 2011 All Rights Reserved. //*************************************************************************************** #include "d3dApp.h" #include <WindowsX.h> #include <sstream> namespace { // This is just used to forward Windows messages from a global window // procedure to our member function window procedure because we cannot // assign a member function to WNDCLASS::lpfnWndProc. D3DApp* gd3dApp = 0; } LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // Forward hwnd on because we can get messages (e.g., WM_CREATE) // before CreateWindow returns, and thus before mhMainWnd is valid. return gd3dApp->MsgProc(hwnd, msg, wParam, lParam); } D3DApp::D3DApp(HINSTANCE hInstance) : mhAppInst(hInstance), mMainWndCaption(L"D3D11 Application"), md3dDriverType(D3D_DRIVER_TYPE_HARDWARE), mClientWidth(800), mClientHeight(600), mEnable4xMsaa(false), mhMainWnd(0), mAppPaused(false), mMinimized(false), mMaximized(false), mResizing(false), m4xMsaaQuality(0), md3dDevice(0), md3dImmediateContext(0), mSwapChain(0), mDepthStencilBuffer(0), mRenderTargetView(0), mDepthStencilView(0) { ZeroMemory(&mScreenViewport, sizeof(D3D11_VIEWPORT)); // Get a pointer to the application object so we can forward // Windows messages to the object's window procedure through // the global window procedure. gd3dApp = this; } D3DApp::~D3DApp() { ReleaseCOM(mRenderTargetView); ReleaseCOM(mDepthStencilView); ReleaseCOM(mSwapChain); ReleaseCOM(mDepthStencilBuffer); // Restore all default settings. if( md3dImmediateContext ) md3dImmediateContext->ClearState(); ReleaseCOM(md3dImmediateContext); ReleaseCOM(md3dDevice); } HINSTANCE D3DApp::AppInst()const { return mhAppInst; } HWND D3DApp::MainWnd()const { return mhMainWnd; } float D3DApp::AspectRatio()const { return static_cast<float>(mClientWidth) / mClientHeight; } bool D3DApp::Init() { if (!InitMainWindow()) return false; if (!InitDirect3D()) return false; return true; } int D3DApp::Run() { MSG msg = { 0 }; mTimer.Reset(); while (msg.message != WM_QUIT) { // If there are Window messages then process them. if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Otherwise, do animation/game stuff. else { mTimer.Tick(); if (!mAppPaused) { CalculateFrameStats(); UpdateScene(mTimer.DeltaTime()); DrawScene(); } else { Sleep(100); } } } return (int)msg.wParam; } bool D3DApp::InitMainWindow() { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = MainWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = mhAppInst; wc.hIcon = LoadIcon(0, IDI_APPLICATION); wc.hCursor = LoadCursor(0, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH); wc.lpszMenuName = 0; wc.lpszClassName = L"D3DWndClassName"; if (!RegisterClass(&wc)) { MessageBox(0, L"RegisterClass Failed.", 0, 0); return false; } // Compute window rectangle dimensions based on requested client area dimensions. RECT R = { 0, 0, mClientWidth, mClientHeight }; AdjustWindowRect(&R, WS_OVERLAPPEDWINDOW, false); int width = R.right - R.left; int height = R.bottom - R.top; mhMainWnd = CreateWindow(L"D3DWndClassName", mMainWndCaption.c_str(), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0, 0, mhAppInst, 0); if (!mhMainWnd) { MessageBox(0, L"CreateWindow Failed.", 0, 0); return false; } ShowWindow(mhMainWnd, SW_SHOW); UpdateWindow(mhMainWnd); return true; } bool D3DApp::InitDirect3D() { UINT createDeviceFlags = 0; //添加debug flag #if defined(DEBUG)||defined(_DEBUG) //createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; #endif D3D_FEATURE_LEVEL featureLevel; //1、使用D3D11CreateDevice创建ID3D11Device和ID3D11DeviceContext接口。 HRESULT hr = D3D11CreateDevice( 0,//默认适配器 D3D_DRIVER_TYPE_HARDWARE,//硬件类型 0,//无软件驱动 createDeviceFlags, 0, 0,//默认等级 最高为D3D11 D3D11_SDK_VERSION, &md3dDevice, &featureLevel, //返回当前支持的特性等级 &md3dImmediateContext ); if (FAILED(hr)) { MessageBox(0, L"创建d3d11设备失败", 0, 0); return false; } if (featureLevel!=D3D_FEATURE_LEVEL_11_0) { MessageBox(0, L"不支持d3d11", 0, 0); return false; } //2、使用ID3D11Device::CheckMultisampleQualityLevels检查4X MSAA等级。 UINT m4xMsaaQuality; HR(md3dDevice->CheckMultisampleQualityLevels( DXGI_FORMAT_R8G8B8A8_UNORM, 4, &m4xMsaaQuality)); assert(m4xMsaaQuality > 0); //3、配置Swap Chain。 DXGI_SWAP_CHAIN_DESC sd; sd.BufferDesc.Width = mClientWidth; sd.BufferDesc.Height = mClientHeight; sd.BufferDesc.RefreshRate.Numerator = 60;//刷新率分子 sd.BufferDesc.RefreshRate.Denominator = 1;//刷新率分母 sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; sd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; sd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; if ( mEnable4xMsaa) { sd.SampleDesc.Count = 4; sd.SampleDesc.Quality = m4xMsaaQuality - 1; } else { sd.SampleDesc.Count = 1; sd.SampleDesc.Quality = 0; } sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; sd.BufferCount = 1;//双缓冲只需一个后台缓冲区 sd.OutputWindow = mhMainWnd; sd.Windowed = true; sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; sd.Flags = 0; //4、IDXGIFactory实例来创建设备,创建IDXGISwapChain实例。 IDXGIDevice *dxgiDevice = 0; md3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgiDevice); IDXGIAdapter *dxgiAdapter = 0; dxgiDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&dxgiAdapter); IDXGIFactory *dxgiFactory = 0; dxgiAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&dxgiFactory); dxgiFactory->CreateSwapChain(md3dDevice, &sd, &mSwapChain); ReleaseCOM(dxgiDevice); ReleaseCOM(dxgiAdapter); ReleaseCOM(dxgiFactory); //剩余的步骤在每次窗口大小变化时都要执行 因此放在OnResize函数中 OnResize(); } void D3DApp::OnResize() { assert(md3dImmediateContext); assert(md3dDevice); assert(mSwapChain); ReleaseCOM(mRenderTargetView); ReleaseCOM(mDepthStencilView); ReleaseCOM(mDepthStencilBuffer); //5、为后台缓冲区的指定渲染目标视图。 HR(mSwapChain->ResizeBuffers(1, mClientWidth, mClientHeight, DXGI_FORMAT_R8G8B8A8_UNORM, 0)); ID3D11Texture2D* backBuffer; HR(mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&backBuffer))); HR(md3dDevice->CreateRenderTargetView(backBuffer, 0, &mRenderTargetView)); ReleaseCOM(backBuffer); //6、创建深度/模板缓冲区和关联的深度/模板视图。 D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = mClientWidth; depthStencilDesc.Height = mClientHeight; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; if (mEnable4xMsaa) { depthStencilDesc.SampleDesc.Count = 4; depthStencilDesc.SampleDesc.Quality = m4xMsaaQuality - 1; } // No MSAA else { depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; } depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; HR(md3dDevice->CreateTexture2D(&depthStencilDesc, 0, &mDepthStencilBuffer)); HR(md3dDevice->CreateDepthStencilView(mDepthStencilBuffer, 0, &mDepthStencilView)); //7、绑定渲染目标视图和深度缓冲区视图。 md3dImmediateContext->OMSetRenderTargets(1, &mRenderTargetView, mDepthStencilView); //8、设置视口。 mScreenViewport.TopLeftX = 0; mScreenViewport.TopLeftY = 0; mScreenViewport.Width = static_cast<float>(mClientWidth); mScreenViewport.Height = static_cast<float>(mClientHeight); mScreenViewport.MinDepth = 0.0f; mScreenViewport.MaxDepth = 1.0f; md3dImmediateContext->RSSetViewports(1, &mScreenViewport); } void D3DApp::CalculateFrameStats() { //计算帧率FPS //对于每个应用程序该函数是通用的 static int frameCnt = 0; static float timeElapsed = 0.0f; frameCnt++; // 计算一秒内渲染的帧数和帧的持续时间 if ((mTimer.TotalTime() - timeElapsed) >= 1.0f) { float fps = (float)frameCnt; // fps = frameCnt / 1 float mspf = 1000.0f / fps; //每帧时间 std::wostringstream outs; outs.precision(6); outs << mMainWndCaption << L" " << L"FPS: " << fps << L" " << L"Frame Time: " << mspf << L" (ms)"; SetWindowText(mhMainWnd, outs.str().c_str()); // 重置计数器 frameCnt = 0; timeElapsed += 1.0f; } } //消息处理 LRESULT D3DApp::MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { // WM_ACTIVATE is sent when the window is activated or deactivated. // We pause the game when the window is deactivated and unpause it // when it becomes active. case WM_ACTIVATE: if (LOWORD(wParam) == WA_INACTIVE) { mAppPaused = true; mTimer.Stop(); } else { mAppPaused = false; mTimer.Start(); } return 0; // WM_SIZE is sent when the user resizes the window. case WM_SIZE: // Save the new client area dimensions. mClientWidth = LOWORD(lParam); mClientHeight = HIWORD(lParam); if (md3dDevice) { if (wParam == SIZE_MINIMIZED) { mAppPaused = true; mMinimized = true; mMaximized = false; } else if (wParam == SIZE_MAXIMIZED) { mAppPaused = false; mMinimized = false; mMaximized = true; OnResize(); } else if (wParam == SIZE_RESTORED) { // Restoring from minimized state? if (mMinimized) { mAppPaused = false; mMinimized = false; OnResize(); } // Restoring from maximized state? else if (mMaximized) { mAppPaused = false; mMaximized = false; OnResize(); } else if (mResizing) { // If user is dragging the resize bars, we do not resize // the buffers here because as the user continuously // drags the resize bars, a stream of WM_SIZE messages are // sent to the window, and it would be pointless (and slow) // to resize for each WM_SIZE message received from dragging // the resize bars. So instead, we reset after the user is // done resizing the window and releases the resize bars, which // sends a WM_EXITSIZEMOVE message. } else // API call such as SetWindowPos or mSwapChain->SetFullscreenState. { OnResize(); } } } return 0; // WM_EXITSIZEMOVE is sent when the user grabs the resize bars. case WM_ENTERSIZEMOVE: mAppPaused = true; mResizing = true; mTimer.Stop(); return 0; // WM_EXITSIZEMOVE is sent when the user releases the resize bars. // Here we reset everything based on the new window dimensions. case WM_EXITSIZEMOVE: mAppPaused = false; mResizing = false; mTimer.Start(); OnResize(); return 0; // WM_DESTROY is sent when the window is being destroyed. case WM_DESTROY: PostQuitMessage(0); return 0; // The WM_MENUCHAR message is sent when a menu is active and the user presses // a key that does not correspond to any mnemonic or accelerator key. case WM_MENUCHAR: // Don't beep when we alt-enter. return MAKELRESULT(0, MNC_CLOSE); // Catch this message so to prevent the window from becoming too small. case WM_GETMINMAXINFO: ((MINMAXINFO*)lParam)->ptMinTrackSize.x = 200; ((MINMAXINFO*)lParam)->ptMinTrackSize.y = 200; return 0; case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: OnMouseDown(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: OnMouseUp(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; case WM_MOUSEMOVE: OnMouseMove(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); }
//*************************************************************************************** // d3dApp.h by Frank Luna (C) 2011 All Rights Reserved. //*************************************************************************************** #ifndef D3DAPP_H #define D3DAPP_H #include "d3dUtil.h" #include "GameTimer.h" class D3DApp { public: D3DApp(HINSTANCE hInstance); virtual ~D3DApp(); HINSTANCE AppInst()const; HWND MainWnd()const; float AspectRatio()const; int Run(); // Framework methods. Derived client class overrides these methods to // implement specific application requirements. virtual bool Init(); virtual void OnResize(); virtual void UpdateScene(float dt) = 0; virtual void DrawScene() = 0; virtual LRESULT MsgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); // Convenience overrides for handling mouse input. virtual void OnMouseDown(WPARAM btnState, int x, int y){ } virtual void OnMouseUp(WPARAM btnState, int x, int y) { } virtual void OnMouseMove(WPARAM btnState, int x, int y){ } protected: bool InitMainWindow(); bool InitDirect3D(); void CalculateFrameStats(); protected: HINSTANCE mhAppInst; HWND mhMainWnd; bool mAppPaused; bool mMinimized; bool mMaximized; bool mResizing; UINT m4xMsaaQuality; GameTimer mTimer; ID3D11Device* md3dDevice; ID3D11DeviceContext* md3dImmediateContext; IDXGISwapChain* mSwapChain; ID3D11Texture2D* mDepthStencilBuffer; ID3D11RenderTargetView* mRenderTargetView; ID3D11DepthStencilView* mDepthStencilView; D3D11_VIEWPORT mScreenViewport; // Derived class should set these in derived constructor to customize starting values. std::wstring mMainWndCaption; D3D_DRIVER_TYPE md3dDriverType; int mClientWidth; int mClientHeight; bool mEnable4xMsaa; }; #endif
//*************************************************************************************** // GameTimer.h by Frank Luna (C) 2011 All Rights Reserved. //*************************************************************************************** #ifndef GAMETIMER_H #define GAMETIMER_H class GameTimer { public: GameTimer(); float TotalTime()const; // in seconds float DeltaTime()const; // in seconds void Reset(); // Call before message loop. void Start(); // Call when unpaused. void Stop(); // Call when paused. void Tick(); // Call every frame. private: double mSecondsPerCount; double mDeltaTime; __int64 mBaseTime; __int64 mPausedTime; __int64 mStopTime; __int64 mPrevTime; __int64 mCurrTime; bool mStopped; }; #endif // GAMETIMER_H
//*************************************************************************************** // GameTimer.cpp by Frank Luna (C) 2011 All Rights Reserved. //*************************************************************************************** #include <windows.h> #include "GameTimer.h" GameTimer::GameTimer(): mSecondsPerCount(0.0), mDeltaTime(-1.0), mBaseTime(0), mPausedTime(0), mPrevTime(0), mCurrTime(0), mStopped(false) { __int64 countsPerSec; QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec); mSecondsPerCount = 1.0 / (double)countsPerSec; //一次count的时间 单位是s } float GameTimer::TotalTime()const { if (mStopped) { return (float)(((mStopTime - mPausedTime) - mBaseTime)*mSecondsPerCount); } else { return (float)(((mCurrTime - mPausedTime) - mBaseTime)*mSecondsPerCount); } } float GameTimer::DeltaTime()const { return (float)mDeltaTime; //每帧之间间隔 单位:秒 } void GameTimer::Reset() { __int64 currTime; QueryPerformanceCounter((LARGE_INTEGER*)&currTime); mBaseTime = currTime; mPrevTime = currTime; mStopTime = 0; mStopped = false; } void GameTimer::Start() { __int64 startTime; QueryPerformanceCounter((LARGE_INTEGER*)&startTime); if (mStopped) { //累计暂停的时间 mPausedTime += (startTime - mStopTime); mPrevTime = startTime; mStopTime = 0; mStopped = false; } } void GameTimer::Stop() { if (!mStopped) { __int64 currTime; QueryPerformanceCounter((LARGE_INTEGER*)&currTime); mStopTime = currTime; mStopped = true; } } void GameTimer::Tick() { if (mStopped) { mDeltaTime = 0.0; return; } __int64 currTime; QueryPerformanceCounter((LARGE_INTEGER*)&currTime); mCurrTime = currTime; // Time difference between this frame and the previous. mDeltaTime = (mCurrTime - mPrevTime)*mSecondsPerCount; // Prepare for next frame. mPrevTime = mCurrTime; // Force nonnegative. The DXSDK's CDXUTTimer mentions that if the // processor goes into a power save mode or we get shuffled to another // processor, then mDeltaTime can be negative. if (mDeltaTime < 0.0) { mDeltaTime = 0.0; } }
//demo.cpp #include "d3dApp.h" class Demo :public D3DApp { public: Demo(HINSTANCE hInstance); ~Demo(); bool Init(); void OnResize(); void UpdateScene(float dt); void DrawScene(); }; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, PSTR cmdLine, int showCmd) { #if defined(DEBUG) |defined(_DEBUG) _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif Demo app(hInstance); if (!app.Init()) return 0; else return app.Run(); } Demo::Demo(HINSTANCE hInstance) :D3DApp(hInstance) { } Demo::~Demo() { } bool Demo::Init() { if (!D3DApp::Init()) return false; else return true; } void Demo::OnResize() { D3DApp::OnResize(); } void Demo::UpdateScene(float dt) { } void Demo::DrawScene() { assert(md3dImmediateContext); assert(mSwapChain); md3dImmediateContext->ClearRenderTargetView(mRenderTargetView, reinterpret_cast<const float*>(&Colors::Blue)); md3dImmediateContext->ClearDepthStencilView(mDepthStencilView, D3D11_CLEAR_DEPTH | D3D10_CLEAR_STENCIL, 1.0f, 0); HR(mSwapChain->Present(0, 0)); }