DirectX学习经验总结
acmilan2016/10/19软件综合 IP:四川

DirectX是众所周知的比较难以入门的3D渲染引擎,除了概念比较抽象以外,还有一点是程序的逻辑比较难以处理。

经过比较长时间的探索,我发现程序逻辑方面的难点主要在几个方面:第一是庞大的结构体,第二是错误处理的姿势,第三是对象的释放。

第一个难点是庞大的结构体

这是学习DirectX 10/11面对的第一个结构体(WNDCLASSEX不算),其实这个结构体并不算大,但是看起来信息量有点大。

<code class="language-cpp">DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );
sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = TRUE;
</code>

如果不习惯的话,可以使用C++的列表初始化语法进行初始化。要注意的是,如果有嵌套结构体,最好不要用嵌套的括号,而是先初始化内层结构体,再初始化外层结构体,这样可以方便在Visual Studio中使用“查看定义”功能进行实时参考。

<code class="language-cpp">DXGI_RATIONAL refrate = { 60, 1 };
DXGI_SAMPLE_DESC smpdesc = { 1, 0 };
DXGI_MODE_DESC bufdesc = {
	width, height, refrate, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED
};
DXGI_SWAP_CHAIN_DESC sd = { bufdesc, smpdesc, DXGI_USAGE_RENDER_TARGET_OUTPUT, 1, g_hwnd, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0 };
</code>

另外,联合体在C++中只能初始化第一项,要想初始化其它项,必须用赋值操作。

<code class="language-cpp">D3D10_DEPTH_STENCIL_VIEW_DESC descDSV = { descDepth.Format, D3D10_DSV_DIMENSION_TEXTURE2D };
descDSV.Texture2D.MipSlice = 0; // Texture2D是匿名联合体的第三个成员,只能通过赋值初始化
</code>

第二个难点是错误处理的姿势

DirectX返回一个叫做HRESULT的错误码,其中>=0表示成功,用SUCCEEDED(hr)表示,<0表示错误,用FAILED(hr)表示。但是通过if (SUCCEEDED(hr))和if (FAILED(hr))处理错误,会使得程序流程严重改变,还会产生冗余代码,这使得编写程序变得非常痛苦。

其实DirectX返回错误值大多意味着严重错误,这时一般需要终止程序。但是又要保证程序可调试性强,这个条件下C++的异常机制就非常适合使用了。将DirectX返回值转换为C++异常,最简单的方法是if (FAILED(hr)) throw hr;。但是这样有时并不合适,最好使用强类型的异常。

为了让返回值转换为异常的过程变得简单,这里编写了一个“一行工具类”如下:

<code class="language-cpp">struct comexcept { explicit comexcept(HRESULT ret) : hr(ret) { if (FAILED(ret)) throw *this; } HRESULT hr; };
</code>

使用方法举例:

<code class="language-cpp">(comexcept)D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_WARP, NULL, 0, D3D10_SDK_VERSION, &sd, &g_swpch, &g_dev);

(comexcept)g_swpch->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer);
(comexcept)g_dev->CreateRenderTargetView(pBackBuffer, NULL, &g_rtv);

(comexcept)g_dev->CreateTexture2D(&descDepth, NULL, &g_dsbuf);
(comexcept)g_dev->CreateDepthStencilView(g_dsbuf, &descDSV, &g_dsv);
</code>

类似的,WinAPI一般返回0表示错误,也有相应的“一行工具类”:

<code class="language-cpp">struct zeroexcept { template <typename t> explicit zeroexcept(T ret) { if (ret == (T)0) throw *this; } };
</typename></code>

使用方法与上述DirectX工具类类似。

第三个难点是资源的释放

DirectX对象使用引用计数来管理生命周期,程序使用完毕需使用->Release()成员函数释放所有权,但是比较麻烦的地方在于,对象释放了以后便不再有效,对无效的对象释放会导致程序崩溃,为了避免这种情况,标准的释放对像流程如下:

<code class="language-cpp">if (g_dev)
{
	g_dev->Release();
	g_dev = NULL;
}
</code>

这段程序每个对象都打一遍是很痛苦的事情,为了减轻这种痛苦,可以定义一个“一行工具函数”:

<code class="language-cpp">template <typename t> inline void release(T& i){ if (i) { i->Release(); i = NULL; } }
</typename></code>

使用方法很简单,release(g_dev);即可。

虽然已经很方便了,但是这样做不到防止资源泄漏的效果。如果要做到没有资源泄露,那么就需要用到ATL的一个类模板:CComPtr<T>。

这个模板是用来包装COM对象指针的,由于DirectX也是基于COM的,因此也可以拿来用。只是要注意三点:

  1. &xxx只能用于初始化,不能用于初始化后取地址,初始化后若需要取地址,应取成员p的地址,即&xxx.p
  2. 手动释放直接赋值NULL即可
  3. 没有QueryInterface、AddRef、Release成员函数的对象不需要使用CComPtr<T>,直接裸指针即可

使用示例:

<code class="language-cpp">#include <atlbase.h> // CComPtr<t>

CComPtr<id3d10device> g_dev;
CComPtr<idxgiswapchain> g_swpch;
CComPtr<id3d10rendertargetview> g_rtv;
CComPtr<id3d10texture2d> g_dsbuf;
CComPtr<id3d10depthstencilview> g_dsv;

CComPtr<id3d10effect> g_effect;
ID3D10EffectTechnique *g_technique; // 不需要Release,因此不需要CComPtr<t>
CComPtr<id3d10inputlayout> g_inlayout;
CComPtr<id3d10buffer> g_vertexbuf;
CComPtr<id3d10rasterizerstate> g_raststate;

// 初始化
(comexcept)g_dev->CreateRenderTargetView(pBackBuffer, NULL, &g_rtv);

// 手动释放
pBackBuffer = NULL;

// 取地址
g_dev->OMSetRenderTargets(1, &g_rtv.p, g_dsv); // 注意是&g_rtv.p不是&g_rtv,后者只能用于初始化
</id3d10rasterizerstate></id3d10buffer></id3d10inputlayout></t></id3d10effect></id3d10depthstencilview></id3d10texture2d></id3d10rendertargetview></idxgiswapchain></id3d10device></t></atlbase.h></code>

一个简单的DirectX 10程序

绘制一个旋转的三角形。其中TODO: begin和TODO: end之间的程序可以替换为自己的程序,因此也算是一个模板程序。

dx10.png

<code>// main.fx - HLSL效果文件

// TODO: 编写效果文件 begin

float4x4 matWorld;
float4x4 matView;
float4x4 matProj;

struct VS_OUTPUT
{
	float4 Pos : SV_POSITION;
	float4 Color : COLOR;
};

VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
	VS_OUTPUT Out;
	Out.Pos = mul(mul(mul(Pos, matWorld), matView), matProj);
	Out.Color = Color;
	return Out;
}

float4 PS(VS_OUTPUT In) : SV_Target
{
	return In.Color;
}

technique10 Render
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0, VS()));
		SetGeometryShader(NULL);
		SetPixelShader(CompileShader(ps_4_0, PS()));
	}
}

// TODO: 编写效果文件 end
</code>
<code class="language-cpp">// main.cpp - 主程序

// 头文件和库
#pragma warning(disable: 4005) // macro redefinition
#undef UNICODE
#define UNICODE
#include <windows.h>
#include <d3d10.h>
#include <d3dx10.h>
#include <atlbase.h> // CComPtr<t>
#pragma comment(lib, "d3d10.lib")
#pragma comment(lib, "d3dx10.lib")

// CComPtr<t>注意三点:
// 初始化用&xxx,取地址用&xxx.p
// 手动释放直接赋值NULL
// 不需要Release的对象不需要使用CComPtr<t>

// 将值转换为异常的类
struct zeroexcept { template <typename t> explicit zeroexcept(T ret) { if (ret == (T)0) throw *this; } };
struct comexcept { explicit comexcept(HRESULT ret) : hr(ret) { if (FAILED(ret)) throw *this; } HRESULT hr; };

// 全局变量
HINSTANCE g_hinst;
HWND g_hwnd;
CComPtr<id3d10device> g_dev;
CComPtr<idxgiswapchain> g_swpch;
CComPtr<id3d10rendertargetview> g_rtv;
CComPtr<id3d10texture2d> g_dsbuf;
CComPtr<id3d10depthstencilview> g_dsv;

// TODO: 其它全局定义 begin

struct SimpleVertex
{
	D3DXVECTOR3 Pos;
	D3DXCOLOR Color;
};

CComPtr<id3d10effect> g_effect;
ID3D10EffectTechnique *g_technique; // 不需要Release,因此不需要CComPtr<t>
CComPtr<id3d10inputlayout> g_inlayout;
CComPtr<id3d10buffer> g_vertexbuf;
CComPtr<id3d10rasterizerstate> g_raststate;

// TODO: 其它全局定义 end

// 窗口消息处理函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	if (message == WM_DESTROY)
	{
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

// 程序入口点
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
	g_hinst = hInstance;

	// 关闭显示缩放
	SetProcessDPIAware();

	// 注册窗口类
	WNDCLASSEX wcex = {
		sizeof wcex, CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, GetModuleHandle(NULL), LoadIcon(NULL, IDI_APPLICATION),
		LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, L"MainWndClass", LoadIcon(NULL, IDI_APPLICATION)
	};
	(zeroexcept)RegisterClassEx(&wcex);
	
	// 计算预期客户区大小对应的窗口大小,并创建和显示窗口
	RECT rc = { 0, 0, 640, 480 };
	DWORD style = WS_OVERLAPPED | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX;
	AdjustWindowRect(&rc, style, FALSE);
	g_hwnd = CreateWindow(
		L"MainWndClass", L"Main Window", style, CW_USEDEFAULT, CW_USEDEFAULT,
		rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
	(zeroexcept)g_hwnd;
	ShowWindow(g_hwnd, SW_SHOWDEFAULT);
	UpdateWindow(g_hwnd);

	// 获取准确的客户区大小
	GetClientRect(g_hwnd, &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;

	// 创建设备和交换链
	DXGI_RATIONAL refrate = { 60, 1 };
	DXGI_SAMPLE_DESC smpdesc = { 1, 0 };
	DXGI_MODE_DESC bufdesc = {
		width, height, refrate, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED
	};
	DXGI_SWAP_CHAIN_DESC sd = { bufdesc, smpdesc, DXGI_USAGE_RENDER_TARGET_OUTPUT, 1, g_hwnd, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0 };
	(comexcept)D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_WARP, NULL, 0, D3D10_SDK_VERSION, &sd, &g_swpch, &g_dev);

	// 获取后台缓冲区,绑定为渲染目标视图
	CComPtr<id3d10texture2d> pBackBuffer = NULL;
	(comexcept)g_swpch->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&pBackBuffer);
	(comexcept)g_dev->CreateRenderTargetView(pBackBuffer, NULL, &g_rtv);
	pBackBuffer = NULL; // 手动释放

	// 创建深度/模板缓冲区,绑定为深度/模板视图
	D3D10_TEXTURE2D_DESC descDepth = {
		width, height, 1, 1, DXGI_FORMAT_D32_FLOAT_S8X24_UINT, smpdesc, D3D10_USAGE_DEFAULT, D3D10_BIND_DEPTH_STENCIL, 0, 0
	};
	D3D10_DEPTH_STENCIL_VIEW_DESC descDSV = { descDepth.Format, D3D10_DSV_DIMENSION_TEXTURE2D };
	descDSV.Texture2D.MipSlice = 0;
	(comexcept)g_dev->CreateTexture2D(&descDepth, NULL, &g_dsbuf);
	(comexcept)g_dev->CreateDepthStencilView(g_dsbuf, &descDSV, &g_dsv);

	// 设置设备OM阶段的渲染目标和深度/模板视图
	g_dev->OMSetRenderTargets(1, &g_rtv.p, g_dsv); // 注意是&g_rtv.p不是&g_rtv,后者只能用于初始化

	// 设置设备RS阶段的视口参数
	D3D10_VIEWPORT vp = { 0, 0, width, height, 0.0f, 1.0f, };
	g_dev->RSSetViewports(1, &vp);

	// TODO: 初始化其它对象 begin

	// 编译效果文件,并获取其中的"Render"技术
	(comexcept)D3DX10CreateEffectFromFile(L"main.fx", NULL, NULL, "fx_4_0", 0, 0, g_dev, NULL, NULL, &g_effect, NULL, NULL);
	g_technique = g_effect->GetTechniqueByName("Render");
	
	// 建立数据输入布局
	D3D10_PASS_DESC passdesc;
	(comexcept)g_technique->GetPassByIndex(0)->GetDesc(&passdesc);
	D3D10_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};
	(comexcept)g_dev->CreateInputLayout(layout, ARRAYSIZE(layout), passdesc.pIAInputSignature, passdesc.IAInputSignatureSize, &g_inlayout);

	// 建立顶点缓冲区
	SimpleVertex vertices[] =
	{
		D3DXVECTOR3(0.0f, 1.0f, 0.0f), D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f),
		D3DXVECTOR3(1.0f, -1.0f, 0.0f), D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f),
		D3DXVECTOR3(-1.0f, -1.0f, 0.0f), D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f),
	};
	D3D10_BUFFER_DESC vertexbufdesc = { sizeof vertices, D3D10_USAGE_DEFAULT, D3D10_BIND_VERTEX_BUFFER, 0, 0 };
	D3D10_SUBRESOURCE_DATA vertexinitdata = { vertices, 0, 0 };
	(comexcept)g_dev->CreateBuffer(&vertexbufdesc, &vertexinitdata, &g_vertexbuf);

	// 设置RS阶段的栅格化参数,不剔除背面
	D3D10_RASTERIZER_DESC rastdesc = { D3D10_FILL_SOLID, D3D10_CULL_NONE, 0, 0, 0, 0, 0, 0, 0, 0 };
	(comexcept)g_dev->CreateRasterizerState(&rastdesc, &g_raststate);
	g_dev->RSSetState(g_raststate);

	// TODO: 初始化其它对象 end

	// 消息循环
	MSG msg = { 0 };
	while (msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			// 清空渲染目标和深度/模板
			float ClearColor[4] = { 0.0f, 0.125f, 0.3f, 1.0f };
			g_dev->ClearRenderTargetView(g_rtv, ClearColor);
			g_dev->ClearDepthStencilView(g_dsv, D3D10_CLEAR_DEPTH | D3D10_CLEAR_STENCIL, 1.0f, 0);

			// TODO: 绘制图元 begin

			// 初始化世界矩阵
			float angle = GetTickCount() % 1000 / 1000.0f * (float)D3DX_PI * 2.0f;
			D3DXMATRIX matWorld;
			D3DXMatrixRotationY(&matWorld, angle);
			(comexcept)g_effect->GetVariableByName("matWorld")->AsMatrix()->SetMatrix((float*)&matWorld);

			// 初始化观察矩阵
			D3DXMATRIX matView;
			D3DXVECTOR3 eye(0.0f, 1.0f, -2.0f);
			D3DXVECTOR3 lookat(0.0f, 0.0f, 0.0f);
			D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
			D3DXMatrixLookAtLH(&matView, &eye, &lookat, &up);
			(comexcept)g_effect->GetVariableByName("matView")->AsMatrix()->SetMatrix((float*)&matView);
			
			// 初始化投影矩阵
			D3DXMATRIX matProj;
			D3DXMatrixPerspectiveFovLH(&matProj, (float)D3DX_PI * 0.5f, width / (FLOAT)height, 0.1f, 100.0f);
			(comexcept)g_effect->GetVariableByName("matProj")->AsMatrix()->SetMatrix((float*)&matProj);

			// 应用"Render"技术并绘制三角形
			D3D10_TECHNIQUE_DESC techdesc;
			(comexcept)g_technique->GetDesc(&techdesc);
			for (UINT i = 0; i < techdesc.Passes; i++)
			{
				// 应用"Render"技术中的一个通道
				(comexcept)g_technique->GetPassByIndex(0)->Apply(0);
				
				// 设置IA阶段输入布局、顶点缓冲区、图元类型,绘制图元
				g_dev->IASetInputLayout(g_inlayout);
				UINT strides = sizeof(SimpleVertex), offset = 0;
				g_dev->IASetVertexBuffers(0, 1, &g_vertexbuf.p, &strides, &offset); // 注意是&g_vertexbuf.p
				g_dev->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
				g_dev->Draw(3, 0);
			}

			// TODO: 绘制图元 end

			// 提交后台缓冲区以显示
			(comexcept)g_swpch->Present(0, 0);
		}
	}

	// 清空状态设置
	g_dev->ClearState();
	
	return (int)msg.wParam;
}
</id3d10texture2d></id3d10rasterizerstate></id3d10buffer></id3d10inputlayout></t></id3d10effect></id3d10depthstencilview></id3d10texture2d></id3d10rendertargetview></idxgiswapchain></id3d10device></typename></t></t></t></atlbase.h></d3dx10.h></d3d10.h></windows.h></code>

[修改于 7年6个月前 - 2016/11/22 17:49:46]

来自:计算机科学 / 软件综合
3
已屏蔽 原因:{{ notice.reason }}已屏蔽
{{notice.noticeContent}}
~~空空如也
acmilan 作者
7年7个月前 修改于 7年7个月前 IP:四川
826896

Direct3D显示工作的大致原理

Direct3D设备对象(device)使用缓冲区(buffer)、顶点/索引/常量缓冲区(vertex/index/constant buffer)、绑定为shader资源视图(shader-resource view)的纹理缓冲区(texture)等资源,以及顶点/几何/像素shader(vertex/geometry/pixel shader)等GPU程序,将图元(geometry primitives)经过流水线(pipeline)各阶段(stages)生成像素,输出到交换链(swap chain)中称为后台缓冲区(back buffer)的纹理缓冲区中(绑定为渲染目标视图render-target view),深度值(depth)和模板值(stencil)输出到单独的纹理缓冲区中(绑定为深度/模板视图depth-stencil view)用于下一次输出合并(output merge),交换链将后台缓冲区提交到前台缓冲区(front buffer)以允许显卡显示画面。

stages.png

引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年7个月前 修改于 7年6个月前 IP:四川
826930

DirectX 10的Geometry Shader

虽然DirectX 9也有Vertex Shader和Pixel Shader,但是这两个只能对顶点和像素一对一进行操作,而对于图元的增删,以及对周围信息的获取,就无能为力了。

为了解决这个问题,DirectX 10新增了Geometry Shader,它以几何图元为单位进行处理,可获取图元和邻接图元的所有顶点信息,可以增删图元,解决了上述DirectX 9的问题。

但是Geometry Shader过于万能,性能上有些问题,因此DirectX 11新增了Hull Shader、Tesselator、Domain Shader以高效实现Geometry Shader的部分功能。

虽然DirectX 10和11非常强大,但是DirectX 9并未完全过时,由于库的丰富性和使用的便利性,有时候DirectX 9还是非常好用的。

将上边的main.fx文件修改一下,加入Geometry Shader,便可以在三角形中间挖个洞。

dx10gslerp.png

<code>// main.fx - HLSL效果文件

// TODO: 编写效果文件 begin

float4x4 matWorld;
float4x4 matView;
float4x4 matProj;

struct VS_OUTPUT
{
	float4 Pos : SV_POSITION;
	float4 Color : COLOR;
};

VS_OUTPUT VS(float4 Pos : POSITION, float4 Color : COLOR)
{
	VS_OUTPUT Out;
	Out.Pos = mul(mul(mul(Pos, matWorld), matView), matProj);
	Out.Color = Color;
	return Out;
}

[maxvertexcount(9)]
void GS(triangle VS_OUTPUT tri[3], inout TriangleStream<vs_output> tristream)
{
	VS_OUTPUT mid1, mid2, mid3;
	mid1.Pos = lerp(tri[1].Pos, tri[0].Pos, 0.5);
	mid1.Color = lerp(tri[1].Color, tri[0].Color, 0.5);
	mid2.Pos = lerp(tri[2].Pos, tri[0].Pos, 0.5);
	mid2.Color = lerp(tri[2].Color, tri[0].Color, 0.5);
	mid3.Pos = lerp(tri[2].Pos, tri[1].Pos, 0.5);
	mid3.Color = lerp(tri[2].Color, tri[1].Color, 0.5);
	tristream.Append(tri[0]);
	tristream.Append(mid1);
	tristream.Append(mid2);
	tristream.RestartStrip();
	tristream.Append(mid1);
	tristream.Append(tri[1]);
	tristream.Append(mid3);
	tristream.RestartStrip();
	tristream.Append(mid2);
	tristream.Append(mid3);
	tristream.Append(tri[2]);
	tristream.RestartStrip();
}

float4 PS(VS_OUTPUT In) : SV_Target
{
	return In.Color;
}

technique10 Render
{
	pass P0
	{
		SetVertexShader(CompileShader(vs_4_0, VS()));
		SetGeometryShader(CompileShader(gs_4_0, GS()));
		SetPixelShader(CompileShader(ps_4_0, PS()));
	}
}

// TODO: 编写效果文件 end
</vs_output></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论
acmilan作者
7年7个月前 修改于 7年6个月前 IP:四川
826998

DirectX 10/11禁止Alt Enter全屏的方法

DirectX 10/11的交换链有全屏功能,但是这个全屏并不是想象的那样,缓冲区大小不改变,画面会模糊,关掉的方法:

<code class="language-cpp">// 禁止Alt+Enter全屏
CComPtr<idxgifactory> dxgifac;
(comexcept)g_swpch->GetParent(__uuidof(IDXGIFactory), (void**)&dxgifac);
dxgifac->MakeWindowAssociation(g_hwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER);
dxgifac = NULL;
</idxgifactory></code>

DXUT也有全屏切换,但实际上是重新建立了适合全屏的设备和交换链,不会模糊。

Win10中Debug Layer的安装

DirectXDebugWin10.png

DirectX 9/10/11比较

DirectX 9.0用起来傻瓜化,直接用API就可以绘图,但是如果想实现高级特性的话,没有集成Shader编译器,需要先行编译或者使用D3DX库编译。

DirectX 10去掉了用API直接画图的功能,但是集成了Shader编译器,加入了Geometry Shader,同时分离了DXGI,用起来稍显复杂,初始化的复杂度增加了。

DirectX 11是基于DirectX 10小幅改进的版本,加上很多功能,包括Tessellation和DirectCompute以及CommandList。但同时,DirectX 11分离了编译器,必须安装运行库(2010或之前)或者附带dll(2012或之后)才能使用,剥离了Effects11必须自己编译。但同时2012又剥离了D3DX成为DirectXTK、DirectXTex、DirectXMesh等数个开源库,需要自己编译。

DirectX 9.0需要配合GDI+出图,DirectX 10/11可以用Direct2D和DirectWrite出图。

一个更理想的DirectX 10框架程序

实现了窗口大小变化,而且使用WndProc而不是C++类管理生命周期

<code class="language-cpp">// main.cpp - 主文件

#undef UNICODE
#define UNICODE 1
#include <windows.h>
#include <d3d10.h>
#include <xnamath.h> // <directxmath.h> for VS2012+
#pragma comment(lib, "d3d10.lib")

HINSTANCE hInst;
HWND hMainWnd;

#define WM_APPIDLE (WM_APP + 10)

struct WndData
{
	ID3D10Device *dev;
	D3D10_DRIVER_TYPE dt;
	IDXGISwapChain *swpch;
	ID3D10RenderTargetView *rtv;
	ID3D10Texture2D *dsbuf;
	ID3D10DepthStencilView *dsv;
};

HRESULT CreateRTV(HWND hWnd, WndData *data)
{
	HRESULT hr = S_OK;
	ID3D10Texture2D *backbuf = NULL;
	hr = data->swpch->GetBuffer(0, __uuidof(ID3D10Texture2D), (void**)&backbuf);
	if (FAILED(hr)) return hr;

	hr = data->dev->CreateRenderTargetView(backbuf, NULL, &data->rtv);
	backbuf->Release();
	return hr;
}

HRESULT CreateDSBufAndDSV(HWND hWnd, WndData *data, int width, int height)
{
	HRESULT hr = S_OK;
	D3D10_TEXTURE2D_DESC dsbufdesc = {
		width, height, 1, 1, DXGI_FORMAT_D32_FLOAT, { 1, 0 }, D3D10_USAGE_DEFAULT, D3D10_BIND_DEPTH_STENCIL, 0, 0
	};
	hr = data->dev->CreateTexture2D(&dsbufdesc, NULL, &data->dsbuf);
	if (FAILED(hr)) return hr;

	D3D10_DEPTH_STENCIL_VIEW_DESC dsvdesc = {
		DXGI_FORMAT_D32_FLOAT, D3D10_DSV_DIMENSION_TEXTURE2D
	};
	dsvdesc.Texture2D.MipSlice = 0;
	hr = data->dev->CreateDepthStencilView(data->dsbuf, &dsvdesc, &data->dsv);
	return hr;
}

void SetViewport(HWND hWnd, WndData *data, int width, int height)
{
	D3D10_VIEWPORT vp = {
		0, 0, width, height, 0.0f, 1.0f
	};
	data->dev->RSSetViewports(1, &vp);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	WndData *data = (WndData *)GetWindowLongPtr(hWnd, 0);

	if (msg == WM_CREATE)
	{
		data = (WndData *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof (WndData));
		if (data == NULL) DestroyWindow(hWnd);
		SetWindowLongPtr(hWnd, 0, (LONG_PTR)data);

		RECT rc = { 0 };
		GetClientRect(hWnd, &rc);
		int width = rc.right - rc.left;
		int height = rc.bottom - rc.top;

		DXGI_MODE_DESC mode = {
			width, height, { 60, 1 }, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED,
		};
		DXGI_SWAP_CHAIN_DESC swpchdesc = {
			mode, { 1, 0 }, DXGI_USAGE_RENDER_TARGET_OUTPUT, 1, hWnd, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0
		};
		D3D10_DRIVER_TYPE drvtype[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_WARP, D3D10_DRIVER_TYPE_REFERENCE };
		HRESULT hr = S_OK;
		for (int i = 0; i < sizeof drvtype / sizeof drvtype[0]; i++)
		{
			data->dt = drvtype[i];
			hr = D3D10CreateDeviceAndSwapChain(NULL, data->dt, NULL, 0, D3D10_SDK_VERSION, &swpchdesc, &data->swpch, &data->dev);
			if (SUCCEEDED(hr)) break;
		}
		if (FAILED(hr)) DestroyWindow(hWnd);

		hr = CreateRTV(hWnd, data);
		if (FAILED(hr)) DestroyWindow(hWnd);

		hr = CreateDSBufAndDSV(hWnd, data, width, height);
		if (FAILED(hr)) DestroyWindow(hWnd);

		data->dev->OMSetRenderTargets(1, &data->rtv, data->dsv);

		SetViewport(hWnd, data, width, height);
		
		// TODO - create objects

		return 0;
	}

	if (msg == WM_DESTROY)
	{
		if (data != NULL)
		{
			if (data->dev) data->dev->ClearState();
			if (data->dsv) data->dsv->Release();
			if (data->dsbuf) data->dsbuf->Release();
			if (data->rtv) data->rtv->Release();
			if (data->swpch) data->swpch->Release();
			if (data->dev) data->dev->Release();
			HeapFree(GetProcessHeap(), 0, data);
		}
		PostQuitMessage(0);
		return 0;
	}

	if (data == NULL)
		return DefWindowProc(hWnd, msg, wParam, lParam); // 下边的消息都是需要data的

	if (msg == WM_SIZE)
	{
		int width = LOWORD(lParam);
		int height = HIWORD(lParam);

		char buf[1024] = "";
		wsprintfA(buf, "%d %d\r\n", width, height);
		OutputDebugStringA(buf);

		data->dev->ClearState();
		data->rtv->Release(); data->rtv = NULL;
		data->dsv->Release(); data->dsv = NULL;
		data->dsbuf->Release(); data->dsbuf = NULL;

		HRESULT hr = data->swpch->ResizeBuffers(1, width, height, DXGI_FORMAT_R8G8B8A8_UNORM, 0);
		if (FAILED(hr)) DestroyWindow(hWnd);

		hr = CreateRTV(hWnd, data);
		if (FAILED(hr)) DestroyWindow(hWnd);

		hr = CreateDSBufAndDSV(hWnd, data, width, height);
		if (FAILED(hr)) DestroyWindow(hWnd);

		data->dev->OMSetRenderTargets(1, &data->rtv, data->dsv);

		SetViewport(hWnd, data, width, height);
		
		return 0;
	}

	if (msg == WM_APPIDLE)
	{
		float backcolor[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
		data->dev->ClearRenderTargetView(data->rtv, backcolor);
		data->dev->ClearDepthStencilView(data->dsv, D3D10_CLEAR_DEPTH, 1.0f, 0);

		// TODO - draw frames

		data->swpch->Present(0, 0);
		return 0;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
	hInst = hInstance;

	WNDCLASSEX wcex = {
		sizeof wcex, CS_VREDRAW|CS_HREDRAW, WndProc, 0, sizeof (WndData *), hInstance, LoadIcon(NULL, IDI_APPLICATION), LoadCursor(NULL, IDC_ARROW),
		(HBRUSH)GetStockObject(NULL_BRUSH), NULL, L"MainWndClass", LoadIcon(NULL, IDI_APPLICATION),
	};
	RegisterClassEx(&wcex);

	RECT rc = { 0, 0, 640, 480 };
	AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
	hMainWnd = CreateWindow(L"MainWndClass", L"Main Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
		rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
	ShowWindow(hMainWnd, nShowCmd);
	UpdateWindow(hMainWnd);

	MSG msg = { 0 };
	while (msg.message != WM_QUIT)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else
		{
			SendMessage(hMainWnd, WM_APPIDLE, 0, 0);
		}
	}

	return 0;
}
</directxmath.h></xnamath.h></d3d10.h></windows.h></code>
引用
评论
加载评论中,请稍候...
200字以内,仅用于支线交流,主线讨论请采用回复功能。
折叠评论

想参与大家的讨论?现在就 登录 或者 注册

所属专业
所属分类
上级专业
同级专业
acmilan
进士 学者 笔友
文章
461
回复
2934
学术分
4
2009/05/30注册,5年3个月前活动
暂无简介
主体类型:个人
所属领域:无
认证方式:邮箱
IP归属地:未同步
文件下载
加载中...
{{errorInfo}}
{{downloadWarning}}
你在 {{downloadTime}} 下载过当前文件。
文件名称:{{resource.defaultFile.name}}
下载次数:{{resource.hits}}
上传用户:{{uploader.username}}
所需积分:{{costScores}},{{holdScores}}下载当前附件免费{{description}}
积分不足,去充值
文件已丢失

当前账号的附件下载数量限制如下:
时段 个数
{{f.startingTime}}点 - {{f.endTime}}点 {{f.fileCount}}
视频暂不能访问,请登录试试
仅供内部学术交流或培训使用,请先保存到本地。本内容不代表科创观点,未经原作者同意,请勿转载。
音频暂不能访问,请登录试试
支持的图片格式:jpg, jpeg, png
插入公式
评论控制
加载中...
文号:{{pid}}
投诉或举报
加载中...
{{tip}}
请选择违规类型:
{{reason.type}}

空空如也

加载中...
详情
详情
推送到专栏从专栏移除
设为匿名取消匿名
查看作者
回复
只看作者
加入收藏取消收藏
收藏
取消收藏
折叠回复
置顶取消置顶
评学术分
鼓励
设为精选取消精选
管理提醒
编辑
通过审核
评论控制
退修或删除
历史版本
违规记录
投诉或举报
加入黑名单移除黑名单
查看IP
{{format('YYYY/MM/DD HH:mm:ss', toc)}}