环境配置 龙书dx12_DX12龙书系列一:DX的初始化(2)

继续我们的DX12初始化。

我们前面提到了没处理WM_SIZE消息,导致我们改变窗口大小的时候会有问题,现在我们来处理这个部分。窗口大小改变了,我们需要更新高度和宽度,同时堆积在队列里面的命令我们需要Flush掉,然后Reset我们的各种变量,包括命令分配器,命令列表,交换缓冲,深度目标缓冲等。然后因为我们的大小改了,导致我们各种视图的尺寸也要一起修改。不过DX12做了足够多的优化,我们可以直接复用前面申请的内存。

void D3D12App::OnResize()

{

assert(device);

assert(swapChain);

assert(alloctor);

FlushCommandQueue();

cmdList->Reset(alloctor.Get(), nullptr);

for (int i = 0; i < swapChainBufferCount; i++)

{

swapChainBuffer[i].Reset();

}

depthStencilBuffer.Reset();

swapChain->ResizeBuffers(swapChainBufferCount, clientWidth, clientHeight, backBufferFormat, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH);

currentBackBufferIndex = 0;

createRTV();

createDSVBuffer();

createDSV();

createViewPortAndScissorRect();

}

和initD3D其实有很多重复的部分,而这个部分多执行一些其实没什么大问题,所以我们修改一下原始的代码:

bool D3D12App::initD3D12()

{

#if defined(DEBUG) | defined(_DEBUG)

ComPtr debugController;

ThrowIfFailed(D3D12GetDebugInterface(IID_PPV_ARGS(&debugController)));

debugController->EnableDebugLayer();

#endif

createDevice();

createFence();

collectDescriptorSize();

msaaSet();

createCMDQueueListAlloctor();

createSwapChain();

createHeap();

OnResize();

return true;

}

稍微精简了一些,然后我们要处理最大化最小化。最小化的时候我们需要支持暂停游戏逻辑,而玩家在移动窗口的过程中,我们希望能够暂停Resize过程,防止不必要的卡顿。

case WM_SIZE:

clientWidth = LOWORD(lParam);

clientHeight = HIWORD(lParam);

if (device)

{

if (wParam == SIZE_MINIMIZED)

{

isMinimized = true;

isMaximized = false;

isAppPause = true;

}

else if (wParam == SIZE_MAXIMIZED)

{

isMinimized = false;

isMaximized = true;

isAppPause = false;

OnResize();

}

else if (wParam == SIZE_RESTORED)

{

if (isMinimized)

{

isMinimized = false;

isMaximized = false;

OnResize();

}

else if (isMaximized)

{

isMinimized = false;

isMaximized = false;

OnResize();

}

else if (isResizing)

{

//do nothing

}

else

{

OnResize();

}

}

}

break;

接着,我们想要添加一个高精度的计时器,这是非常基础的一个模块,我们这里想用它来显示帧时间。

#include "GameTimer.h"

#include

GameTimer::GameTimer():

baseTime(0), pausedTime(0), currTime(0), isStop(false),

deltaTime(-1.0)

{

__int64 countsPerSec;

QueryPerformanceFrequency((LARGE_INTEGER*)&countsPerSec);

secondsPerCount = 1.0 / countsPerSec;

}

void GameTimer::Start()

{

__int64 startTime;

QueryPerformanceCounter((LARGE_INTEGER*)&startTime);

if (isStop)

{

pausedTime += (startTime - stopTime);

isStop = false;

stopTime = 0;

prevTime = startTime;

}

}

void GameTimer::Stop()

{

if (isStop == false)

{

__int64 currTime;

QueryPerformanceCounter((LARGE_INTEGER*)&currTime);

stopTime = currTime;

isStop = true;

}

}

float GameTimer::TotalTime()

{

if (isStop)

{

return (float)((stopTime - pausedTime - baseTime) * secondsPerCount);

}

return (float)((currTime - pausedTime - baseTime) * secondsPerCount);

}

float GameTimer::getDeltaTime()

{

return (float)deltaTime;

}

void GameTimer::Reset()

{

__int64 time;

QueryPerformanceCounter((LARGE_INTEGER*)&time);

baseTime = time;

prevTime = time;

stopTime = 0;

isStop = false;

}

void GameTimer::Tick()

{

if (isStop)

{

deltaTime = 0.0;

return;

}

__int64 time;

QueryPerformanceCounter((LARGE_INTEGER*)&time);

currTime = time;

deltaTime = (currTime - prevTime) * secondsPerCount;

prevTime = currTime;

if (deltaTime < 0.0)

{

deltaTime = 0.0;

}

}

然后我们可以显示一下帧率:

void D3D12App::ShowFrameStat()

{

static int frameCount = 0;

static float timeElapsed = 0.0f;

frameCount++;

if (timer.TotalTime() - timeElapsed > 1.0f)

{

float fps = (float)frameCount;

float mspf = 1000.0f / fps;

std::wstring fpsStr = std::to_wstring(fps);

std::wstring mspfStr = std::to_wstring(mspf);

std::wstring windowText = Title + L" fps: " + fpsStr +

L" mspf: " + mspfStr;

SetWindowText(mhMainWnd, windowText.c_str());

frameCount = 0;

timeElapsed += 1.0f;

}

}

然后我们添加一些常用的鼠标事件。

case WM_MBUTTONDOWN:

case WM_RBUTTONDOWN:

OnMouseDown(wParam, GET_X_LPARAM(lParam), GET_X_LPARAM(lParam));

return;

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;

最后,我们还要做一个简单的重构,那就是Draw函数其实是需要根据实际情况变化的,所以考虑到后面会有不同的内容,我们派生出一个类,去做具体的行为,对于初始化,我们就叫做InitClass吧。然后我们考虑到以后还会有游戏逻辑,所以就顺手把Update函数也加上。

最后再来一个析构函数:

D3D12App::~D3D12App()

{

if (device != nullptr)

{

FlushCommandQueue();

}

}

到这里,我们的整个初始化流程就写完了。

这里附加一下深度相关的知识:

DX里面,深度图存的值是反向的,也就是说1表示离摄像机最近,0表示离摄像机最远。因为深度图里面存的其实是一个1/z的正比值,也就是k/z,所以如果直接采样出来,你会发现在视觉上,它并不是线性的,你需要反变换回到摄像机空间,才能获得一个线性的视觉效果。

使用1/z来存储的主要原因是,顶点位置经过MVP变换后,像素的属性需要通过顶点位置进行插值得到,但因为透视导致近大远小的关系,如果直接对z进行线性插值,得出来的坐标其实是错的,需要用1/z来进行插值,这样得到的坐标才是对的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值