wstring 初始化_DX12龙书系列一:DX的初始化(2)

94d2eb0dd50fdc93f494f70782f0c01e.png

继续我们的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<ID3D12Debug> 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 <Windows.h>


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、付费专栏及课程。

余额充值