小白学习DirectX11:第一个demo

源代码下载

 

简单说说

由于工作需要处理视频流,对处理速度要求比较高。已经完成的图像处理的代码(有空补上博客)处理每帧(3*1920*1080)串行需要160ms左右,不能满足要求,所以在别人推荐下开始研究并行处理计算---DirectX。大致想法是想利用里面的渲染和着色器等功能实现图像处理的算法,由于是并行计算,所以速度肯定要快很多。

昨天开始大致看了很多博客文章还有几本书,现在这儿推荐两个博客

毛星云的DirectX游戏开发

X_Jun很详细的教程

大牛的博客只是浏览了一下,因为自己不太需要那么多功能。但是觉得还是模模糊糊,没有写过代码始终理解不上去。于是今天开始跟着一本书《Beginning DirectX11 Game Programming》的第二章,整合里面的例子,加上一些自己的注释。水平不高,注释一个学习笔记吧。以前做完事情都没有立即写,今天做完决心今天就写完这篇博客

 

DirectX配置

DX的配置问题比较简单,有过常见库配置经验的都能够很简单完成。看了很多的文章,似乎较新版本的VS和系统已经集成了DX库在Windows Kit目录中,也不用另外安装。此处不再赘述,有需要的可以再查一查。

 

主程序

#include<Windows.h>
#include<memory>
#include<dx11demo.h>

//系统消息处理函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	PAINTSTRUCT paintStruct;
	HDC hDC;

	switch (message)
	{
	case WM_PAINT:
		hDC = BeginPaint(hwnd, &paintStruct);
		EndPaint(hwnd, &paintStruct);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hwnd, message, wParam, lParam);
	}

	return 0;

}


int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance,
	LPWSTR cmdLine, int cmdShow)
{
	UNREFERENCED_PARAMETER(prevInstance);       //避免对于未使用变量的系统警告
	UNREFERENCED_PARAMETER(cmdLine);

	WNDCLASSEX wndClass = { 0 };               //窗口的类WNDCLASSEX,并初始化
	wndClass.cbSize = sizeof(WNDCLASSEX);      //窗口大小
	wndClass.style = CS_HREDRAW | CS_VREDRAW;   //窗口风格
	wndClass.lpfnWndProc = WndProc;           //回调函数,处理系统发来的时间消息
	wndClass.hInstance = hInstance;             
	wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
	wndClass.lpszMenuName = NULL;
	wndClass.lpszClassName = "DX11BookWindowClass";      //窗口的名字

	if (!RegisterClassEx(&wndClass))				//注册窗口,创造窗口前必须注册
		return -1;

	RECT rc = { 0,0,640,480 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);

	HWND hwnd = CreateWindowA("DX11BookWindowClass", "Blank Win32 Window",
		WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left,
		rc.bottom - rc.top, NULL, NULL, hInstance, NULL);       //创造窗口

	if (!hwnd)
		return -1;

	ShowWindow(hwnd, cmdShow);        //显示窗口

	std::auto_ptr<Dx11DemoBase> demo(new Dx11DemoBase());
	
	// 初始化
	bool result = demo->Initialize(hInstance, hwnd);
	
	if (!result)
		return -1;

	//Demo 初始化
	MSG msg = { 0 };
	while (msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			//更新 画图
			demo->Update(0.0f);
			demo->Render();
		}
	}
	//Demo 关闭

	demo->Shutdown();
	return static_cast<int>(msg.wParam);
}

主程序就是一个基本的win32下面的窗口函数。简单来说就是先声明一个新窗口,对窗口这个类继续赋值,比如大小,名字,回调函数等等,然后注册窗口,再创造该窗口。这些代码都很常见,也是模块化的,不容易出错。除去以上代码段中有关DX的几个语句,再编译运行,显示出来的就是一个空白窗口。

 

DX的初始化

头文件包含的主要是DX主要头文件,在自己编译运行过程中,由于自己之前设置的库的问题,会报错,所以自己就把链接库也加上了。DX功能对应的库自己还不太熟悉,这个坑以后再来填。


#ifndef _DEMO_BASE_H_
#define _DEMO_BASE_H_
#include<d3d11.h>
#include<d3dx11.h>
#include<DxErr.h>

#pragma comment(lib, "DXGI.lib")
#pragma comment(lib, "D3D11.lib")
#pragma comment(lib, "D3DX11.lib")
#pragma comment(lib, "D3DX10.lib")
#pragma comment(lib, "dxerr.lib")
#pragma comment(lib, "legacy_stdio_definitions.lib")

class Dx11DemoBase
{
public:
	Dx11DemoBase();
	virtual ~Dx11DemoBase();
	bool Initialize(HINSTANCE hInstance, HWND hwnd);  //初始化
	void Shutdown();    //关闭释放
    bool LoadContent();   //加载
	void UnloadContent();  //卸载
	void Update(float dt);  //更新
	void Render();   //渲染
protected:
	HINSTANCE hInstance_;    //窗口实例
	HWND hwnd_;              //窗口句柄
	D3D_DRIVER_TYPE driverType_;    //设备类型
	D3D_FEATURE_LEVEL featureLevel_;  //特征等级
	ID3D11Device* d3dDevice_;         //D3D设备实际对象
	ID3D11DeviceContext* d3dContext_; //上下文,理解为接口更好
	IDXGISwapChain* swapChain_;       //交换链
	ID3D11RenderTargetView* backBufferTarget_;    //后缓冲区目标视图
};
#endif

头文件就不说了,主要是定义了一些基本的函数,和一些DX的变量,比如设备号之类和feature level,看完初始化,再去看这些东西印象会更深刻一些。下面开始来说一下DX初始化的流程和代码段。

bool Dx11DemoBase::Initialize(HINSTANCE hInstance, HWND hwnd)
hInstance_ = hInstance;        //获得当前实例
hwnd_ = hwnd;                  //获取当前窗口句柄

RECT dimensions;                
GetClientRect(hwnd, &dimensions);    //获取当前client area大小(窗口大小)

初始化函数传入就是当前窗口的实例和句柄,因为显示在原来定义的窗口上显示。GetClientRect函数可以获得窗口的大小,RECT结构表示的是窗口的四个点坐标。

设置设备类型和特征等级

    //设置设备类型和特征等级(feature level)
        D3D_DRIVER_TYPE driverTypes[] =                     
     //设备类型(以下包括硬件,软件,WARP,参考四种)
    {
	D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,
	D3D_DRIVER_TYPE_REFERENCE, D3D_DRIVER_TYPE_SOFTWARE
    };

	unsigned int totalDriverTypes = ARRAYSIZE(driverTypes);     //ARRAYSIZE返回数组大小

	D3D_FEATURE_LEVEL featureLevels[] =      //feature level不同的硬件支持不同
		{
	  D3D_FEATURE_LEVEL_11_0,
	  D3D_FEATURE_LEVEL_10_1,
	  D3D_FEATURE_LEVEL_10_0
		};

	unsigned int totalFeatureLevels = ARRAYSIZE(featureLevels);   //ARRAYSIZE返回数组大小

D3D_DRIVER_TYPE和D3D_FEATURE_LEVEL分别就是设备类型和设备特征等级。设备包括软件、硬件、参考和WARP四种设备,特征等级包括这三种。这里用数组存储了,是因为后面读取不同的电脑硬件时,可能会遇到支持的设备类型和等级不同,需要循环设置。只要有一个设备类型和等级,就可以成功创建设备和后续的交换链。

设备和交换链创建

这个代码段主要是完成了交换链这个东西的一些参数的赋值,交换链是一个新概念,但是理解上来还是比较简单的。

     //设备及交换链创建
DXGI_SWAP_CHAIN_DESC swapChainDesc;      //变量类型为  交换链形容子(swap chain description)
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc));   //用零填充该变量的所有区域
swapChainDesc.BufferCount = 1;               //缓冲区数(此处猜测为1,则有两个缓冲区,2有个)
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;            //缓冲区存储的大小(和要显示的一致)
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;   //缓冲区存储图像的格式()
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;        //刷新率,60/1表示60Hz
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;   //缓冲区使用
swapChainDesc.OutputWindow = hwnd;           //窗口,就是win32要做显示的窗口句柄
swapChainDesc.Windowed = true;               //决定保存窗口或者到全屏的变量
swapChainDesc.SampleDesc.Count = 1;          
swapChainDesc.SampleDesc.Quality = 0;        //sample description

交换链:交换始于前缓冲区和后缓冲区的交换。在显示器的显示过程中,一般还会有渲染处理的过程。前缓冲区中存储的就是现在正在显示的图像,后缓冲区是处理结束等待显示的下一张图像。当显示下一张图像时,前后缓冲区就会交换,前变成后,后变成前。在DX里面,形容交换链的容器就是DXGI_SWAP_CHAIN_DESC。值得注意的是,后缓冲区可以是两个也可以是一个。

上面代码里注释都比较详细,也没有太多解释和说明的,还有问题可以参考文章开头给出的那本书。

下面代码就是创建设备和交换链了,for循环检索之前给的数组,有一个存在,就可以创建成功。当然,如果熟悉自己的硬件支持的设备和等级,也可以直接使用那个函数创建。

for (driver = 0; driver < totalDriverTypes; ++driver)  
//循环表示对所有类型设备都尝试创建,有一个设备存在和特征level存在,创建成功
{
//D3D11CreateDeviceAndSwapChain()创建交换链,设备,渲染context
      result = D3D11CreateDeviceAndSwapChain(0, driverTypes[driver], 0, creationFlags,
			   featureLevels, totalFeatureLevels,
				D3D11_SDK_VERSION, &swapChainDesc, &swapChain_,
				&d3dDevice_, &featureLevel_, &d3dContext_);

			if (SUCCEEDED(result))
			{
				driverType_ = driverTypes[driver];    
				break;
			}
		}

if (FAILED(result))
{
	DXTRACE_MSG("创建 Direct3D 设备失败!");
	return false;
	}
  //渲染目标视图创建Render Target View Creation
		ID3D11Texture2D* backBufferTexture;

		result = swapChain_->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferTexture);

		if (FAILED(result))
		{
			DXTRACE_MSG("获取交换链后台缓存失败!");
			return false;
		}

		//创建渲染目标视图
		result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0, &backBufferTarget_);

		if (backBufferTexture)
			backBufferTexture->Release(); //释放缓冲区指针,防止内存泄漏

		if (FAILED(result))
		{
			DXTRACE_MSG("创建渲染目标视图失败!");
			return false;
		}

		//渲染到一个固定视图
		d3dContext_->OMSetRenderTargets(1, &backBufferTarget_, 0);

		//视窗创建(定义了要显示的视窗范围)
		D3D11_VIEWPORT viewport;
		viewport.Width = static_cast<float>(width);   
		viewport.Height = static_cast<float>(height);     //高度和宽度
		viewport.MinDepth = 0.0f;            //深度范围
		viewport.MaxDepth = 1.0f;
		viewport.TopLeftX = 0.0f;            //视窗的顶部左坐标
		viewport.TopLeftY = 0.0f;

		d3dContext_->RSSetViewports(1, &viewport);      //设置到视窗范围

初始化剩下的部分就是比较简单:获取内存,创建目标视图数据,显示等等。。这儿涉及到几个device,context,发现一篇文章简单的说明了一下,可以参考。

代码主要侧重点还是在设备的初始化部分,所以更深奥的部分还没有涉及到。整理的渲染函数里,就只是简单的让窗口显示一个给定的颜色,由clearColor给定。另外一定是,显示设备一般实际上有四个通道,分别是RGB和alpha,最后一个是透明度。

在render函数中,修改clearColor就可以得到不同的窗口显示颜色。


	void Dx11DemoBase::Render()
	{
		if (d3dContext_ == 0)
			return;
		float clearColor[4] = { 0.5f, 0.5f, 0.25f, 1.0f };
		d3dContext_->ClearRenderTargetView(backBufferTarget_, clearColor);    //清除屏幕,显示一个特别的颜色
		swapChain_->Present(0, 0);        //显示后缓冲区的渲染场景,也就是新的
	}

在render函数中,修改clearColor就可以得到不同的窗口显示颜色,如下图所示。

 

总结

本文简单介绍了一些D3D初始化的代码,来源于《Beginning DirectX11 Game Programming》的第二章,简单给代码加上了注释,并写了一些自己的理解。初始化部分: 获取当前设备的类型和特征等级,设置交换链的参数,创建设备,设置后缓冲区内存等等。每一步都需要D3D11库里面的函数实现,主要的函数已经包括在代码中,这部分函数知道固定的输入参数即可。我觉得我将面临的难点还是在后面移植算法过程中,如何用D3D11的功能实现算法。明天继续学习这部分内容,希望自己有空就把自己做过的写成博客,这样就可以越写越好了。

在写博客的时候有些内容没有想到,文章也一般,以后若是想到问题再回来修改。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值