DirectX11学习笔记一 渲染一个三角形

本文是DirectX11学习笔记的第一部分,主要介绍了Direct3D的基本概念,如设备和设备上下文,以及D3D初始化的步骤,包括创建D3D设备和设备上下文、创建交换链以及设置交换链与设备的交互。内容详细解释了D3D初始化的各个环节,如驱动类型、交换链描述和创建过程,并以创建并渲染一个三角形为例,展示了Direct3D的基本操作流程。
摘要由CSDN通过智能技术生成

*  这两个月我看了看数据可视化,又看了看OpenCV,草稿箱里的东西阅存越多,最后还是决定入坑计算机图形学了。因为本科的时候学过一点OpenGLES和计算机图形学原理,再加上自己对游戏开发感兴趣,所以不打算再继续跟老师的方向走了。本来选现在的老师是打算学学游戏服务器的,奈何失算了,o(︶︿︶)o 唉。
  本学习笔记的原始素材来自龙书DirectX11,全部代码和部分文字来自GitHub及其整理龙书代码的大佬的博客
  本博文记录的是我自己的思维方式和理解方法,还有部分内容来自其他博客,MSDN,StackOverflow,视频网站等,比他们的更详细,尽量不一笔带过(因为我比较笨o(︶︿︶)o )。
本文主要使用DirectX11.1,Win10,VS2019,代码里各个类之间的调用关系比较简单,就不一一对应了。

一.Direct3D概述

  Direct3D是一种底层绘图API(application programming interface,应用程序接口),它可以让我们可以通过3D硬件加速绘制3D世界。从本质上讲,Direct3D提供的是一组软件接口,我们可以通过这组接口来控制绘图硬件。例如,要命令绘图设备清空渲染目标(例如屏幕),我们可以调用Direct3D的ID3D11DeviceContext::ClearRenderTargetView方法来完成这一工作。Direct3D层位于应用程序和绘图硬件之间,这样我们就不必担心3D硬件的实现细节,只要设备支持Direct3D 11,我们就可以通过Direct3D 11 API来控制3D硬件了。
  支持Direct3D 11的设备必须支持Direct3D 11规定的整个功能集合以及少数的额外附加功能(有一些功能,比如多重采样数量,仍然需要以查询方式实现,这是因为不同的Direct3D硬件这个值可能并不一样)。在Direct3D 9中,设备可以只支持Direct3D 9的部分功能;所以,当一个Direct3D 9应用程序要使用某一特性时,应用程序就必须先检查硬件是否支持该特性。如果要调用的是一个不为硬件支持Direct3D函数,那应用程序就会出错。而在Direct3D 11中,不需要再做这种设备功能检查,因为Direct3D 11强制要求设备实现Direct3D 11规定的所有功能特性。
  组件对象模型(COM)技术使DirectX独立于任何编程语言,并具有版本向后兼容的特性。我们经常把COM对象称为接口,并把它当成一个普通的C++类来使用。当使用C++编写DirectX程序时,许多COM的底层细节都不必考虑。唯一需要知道的一件事情是,我们必须通过特定的函数或其他的COM接口方法来获取指向COM接口的指针,而不能用C++的new关键字来创建COM接口。另外,当我们不再使用某个接口时,必须调用它的Release方法来释放它(所有的COM接口都继承于IUnknown接口,而Release方法是IUnknown接口的成员),而不能用delete语句——COM对象在其自身内部实现所有的内存管理工作。
  当然,有关COM的细节还有很多,但是在实际工作中只需知道上述内容就足以有效地使用DirectX了。
  注意:COM接口都以大写字母“I”为前缀。例如,表示2D纹理的接口为ID3D11Texture2D。

二.D3D初始化

  D3D初始化前还要初始化Window窗口程序的一些属性,因为不属于D3D,略过了,有兴趣的可以直接看代码,都是API的东西。
  D3D的初始化其实也是完全代码上的东西,可以先不了解图形渲染管线的知识,就当是扫盲和入门了。
  从bool D3DApp::InitDirect3D()方法出发,需要以下几个步骤:

    1. 创建D3D设备和设备上下文
    1. 创建交换链
    1. 设置DXGI交换链与Direct3D设备的交互

1.创建D3D设备和设备上下文

  什么是D3D设备和设备上下文?
  它们是是最重要的Direct3D接口,可以被看成是物理图形设备硬件的软控制器;也就是说,我们可以通过该接口与硬件进行交互,命令硬件完成一些工作(比如:在显存中分配资源、清空后台缓冲区、将资源绑定到各种管线阶段、绘制几何体)。具体而言:
  ID3D11Device接口用于检测显示适配器功能和分配资源。
  ID3D11DeviceContext接口用于设置管线状态、将资源绑定到图形管线和生成渲染命令。
  本文的设备上下文是ID3D11DeviceContext* md3dImmediateContext;,叫做立即执行上下文。
  还有种上下文叫延迟执行上下文(ID3D11Device::CreateDeferredContext)。该上下文主要用于DirectX11支持的多线程程序。
  创建D3D设备和设备上下文的方法:

HRESULT WINAPI D3D11CreateDevice(
    IDXGIAdapter* pAdapter,         // 1. [In_Opt]适配器
    D3D_DRIVER_TYPE DriverType,     // 2. [In]驱动类型
    HMODULE Software,               // 3. [In_Opt]若上面为D3D_DRIVER_TYPE_SOFTWARE则这里需要提供程序模块
    UINT Flags,                     // 4. [In]使用D3D11_CREATE_DEVICE_FLAG枚举类型
    D3D_FEATURE_LEVEL* pFeatureLevels,  // 5. [In_Opt]若为nullptr则为默认特性等级,否则需要提供特性等级数组
    UINT FeatureLevels,             // 6. [In]特性等级数组的元素数目
    UINT SDKVersion,                // 7. [In]SDK版本,默认D3D11_SDK_VERSION
    ID3D11Device** ppDevice,        // [Out_Opt]输出D3D设备
    D3D_FEATURE_LEVEL* pFeatureLevel,   // [Out_Opt]输出当前应用D3D特性等级
    ID3D11DeviceContext** ppImmediateContext ); //[Out_Opt]输出D3D设备上下文

pAdapter(适配器)

  我们可以将它看做是对显示卡设备的一层封装,通过该参数,我们可以指定需要使用哪个显示卡设备。通常该参数我们设为nullptr,这样就可以交由上层驱动来帮我们决定使用哪个显卡,或者在NVIDIA控制面板来设置当前程序要使用哪个显卡。如果想要在应用层决定,使用IDXGIFactory::EnumAdapters方法可以枚举当前可用的显示卡设备。

DriverType 驱动类型

D3D_DRIVER_TYPE原型:

typedef enum D3D_DRIVER_TYPE {
   
  D3D_DRIVER_TYPE_UNKNOWN,
  D3D_DRIVER_TYPE_HARDWARE,
  D3D_DRIVER_TYPE_REFERENCE,
  D3D_DRIVER_TYPE_NULL,
  D3D_DRIVER_TYPE_SOFTWARE,
  D3D_DRIVER_TYPE_WARP
} ;
驱动类型 描述
D3D_DRIVER_TYPE_UNKNOWN 表示未知驱动类型
D3D_DRIVER_TYPE_HARDWARE 一种可以应用D3D特性的硬件驱动。这是你D3D程序中应该主要使用的驱动,因为它效率最好。硬件驱动可以在支持的设备上使用硬件加速,也同样可以在设备中不支持的渲染管线部分使用软件加速。硬件驱动经常是硬件设备的一种抽象。
D3D_DRIVER_TYPE_REFERENCE 一种由软件驱动的支持所有D3D特性的引用驱动。引用驱动的目的是提高精度而不是速度。只用来作特性测试,或者debug,只是为了开发和测试来使用。
D3D_DRIVER_TYPE_NULL 空驱动,代表不支持渲染的引用驱动。主要用来debug非渲染API调用。这个驱动只有DirectX SDK有。
D3D_DRIVER_TYPE_SOFTWARE 软件驱动,纯粹用软件应用D3D特性。不适合高性能场景,因为它很慢。是硬件加速失效的替代品。
D3D_DRIVER_TYPE_WARP 高性能软件渲染器。

  一般都用D3D_DRIVER_TYPE_HARDWARE,但是我们要先检查一下当前系统环境到底支持哪一个,顺序从硬件-WARP-软件驱动来轮询,依次尝试能否成功。

④Flags 枚举值

Flags对应的是D3D11_CREATE_DEVICE_FLAG枚举值,如果需要D3D设备调试的话(在Debug模式下),可以指定D3D11_CREATE_DEVICE_DEBUG枚举值。指定该值后,可以在出现程序异常的时候观察调试输出窗口的信息。

⑤⑥pFeatureLevels 特征等级

D3D_FEATURE_LEVEL本身是一个枚举类型,里面是各种D3D的版本。只需要在D3D11CreateDevice方法里传入一个包含你想支持的等级的数组就可以了,⑥是数组的格元素个数。
代码

HRESULT hr = S_OK;

	// 创建D3D设备 和 D3D设备上下文
	UINT createDeviceFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)  
	createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
	// 驱动类型数组
	D3D_DRIVER_TYPE driverTypes[] =
	{
   
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE(driverTypes);

	// 特性等级数组
	D3D_FEATURE_LEVEL featureLevels[] =
	{
   
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
	};
	UINT numFeatureLevels = ARRAYSIZE(featureLevels);

	D3D_FEATURE_LEVEL featureLevel;
	D3D_DRIVER_TYPE d3dDriverType;
	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
	{
   
		d3dDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDevice(nullptr, d3dDriverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
			D3D11_SDK_VERSION, m_pd3dDevice.GetAddressOf(), &featureLevel, m_pd3dImmediateContext.GetAddressOf());

		if (hr == E_INVALIDARG)
		{
   
			// Direct3D 11.0 的API不承认D3D_FEATURE_LEVEL_11_1,所以我们需要尝试特性等级11.0以及以下的版本
			hr = D3D11CreateDevice(nullptr, d3dDriverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
				D3D11_SDK_VERSION, m_pd3dDevice.GetAddressOf(), &featureLevel, m_pd3dImmediateContext.GetAddressOf());
		}

		if (SUCCEEDED(hr))
			break;
	}

	if (FAILED(hr))
	{
   
		MessageBox(0, L"D3D11CreateDevice Failed.", 0, 0);
		return false;
	}

	// 检测是否支持特性等级11.0或11.1
	if (featureLevel != D3D_FEATURE_LEVEL_11_0 && featureLevel != D3D_FEATURE_LEVEL_11_1)
	{
   
		MessageBox(0, L"Direct3D Feature Level 11 unsupported.", 0, 0);
		return false;
	}

2. 创建交换链

  什么是交换链?
当模型的图元经过层层计算和测试后,就会显示到我们的屏幕上。我们的屏幕显示的就是颜色缓冲区中的颜色值。但是,为了避免我们看到那些正在进行光栅化的图元,GPU会使用双重缓冲(Double Buffering)的策略。这意味着,对场景的渲染时在幕后发生的,即在后置缓冲(Back Buffer)中。一旦场景已经被渲染到了后置缓冲中,GPU就会交换后置缓冲区和前置缓冲区(Front Buffer)中的内容,而前置缓冲区是之前显示在屏幕上的图像。由此,保证了我们看到的图像总是连续的。图4.1说明了这一过程。
在这里插入图片描述
  我们首先渲染缓冲区B,它是当前的后台缓冲区。一旦帧渲染完成,前后缓冲区的指针会相互交换,缓冲区B会变为前台缓冲区,而缓冲区A会变为新的后台缓冲区。之后,我们将在缓冲区A中进行下一帧的渲染。一旦帧渲染完成,前后缓冲区的指针会再次进行交换,缓冲区A会变为前台缓冲区,而缓冲区B会再次变为后台缓冲区。
  前后缓冲区形成了一个交换链(swap chain)。在Direct3D中,交换链由IDXGISwapChain接口表示。该接口保存了前后缓冲区纹理,并提供了用于调整缓冲区尺寸的方法(IDXGISwapChain::ResizeBuffers)和呈现方法(IDXGISwapChain::Present)。我们会在4.4节中详细讨论些方法。
  使用(前后)两个缓冲区称为双缓冲(double buffering)。缓冲区的数量可多于两个;比如,当使用三个缓冲区时称为三缓冲(triple buffering)。不过,两个缓冲区已经足够用了。
  注意:虽然后台缓冲区是一个纹理(纹理元素称为texel),但是我们更习惯于将纹理元素称为像素(pixel),因为后台缓冲区存储的是颜色信息。有时,即使纹理中存储的不是颜色信息,人们还是会将纹理元素称为像素(例如,“法线贴图像素”)。
  但是创建交换链还要做一些准备工作。

①描述交换链

  要创建交换链,首先需要填充一个DXGI_SWAP_CHAIN_DESC1结构体来描述我们将要创建的交换链的特性。该结构体的定义如下。

typedef struct DXGI_SWAP_CHAIN_DESC1
{
   
    UINT Width;                     // 缓冲区宽度
    UINT Height;                    // 缓冲区高度
    DXGI_FORMAT Format;             // 缓冲区数据格式
    BOOL Stereo;                    // 忽略   
    DXGI_SAMPLE_DESC SampleDesc;    // 采样描述
    DXGI_USAGE BufferUsage;         // 缓冲区用途
    UINT BufferCount;               // 缓冲区数目
    DXGI_SCALING Scaling;           // 忽略
    DXGI_SWAP_EFFECT SwapEffect;    // 交换效果
    DXGI_ALPHA_MODE AlphaMode;      // 忽略
    UINT Flags;                     // 使用DXGI_SWAP_CHAIN_FLAG枚举类型
} DXGI_SWAP_CHAIN_DESC1;

typedef struct DXGI_SAMPLE_DESC
{
   
    UINT Count;                     // MSAA采样数
    UINT Quality;                   // MSAA质量等级
} DXGI_SAMPLE_DESC;

typedef struct DXGI_SWAP_CHAIN_FULLSCREEN_DESC
{
   
    DXGI_RATIONAL RefreshRate;                  // 刷新率
    DXGI_MODE_SCANLINE_ORDER ScanlineOrdering;  // 忽略
    DXGI_MODE_SCALING Scaling;                  // 忽略
    BOOL Windowed;                              // 是否窗口化
} DXGI_SWAP_CHAIN_FULLSCREEN_DESC;

typedef struct DXGI_RATIONAL
{
   
    UINT Numerator;                 // 刷新率分子
    UINT Denominator;               // 刷新率分母
} DXGI_RATIONAL;

②得到创建交换链的句柄

  本文使用DirectX11.1创建交换链的方法:

IDXGIFactory2::CreateSwapChainForHwnd( 
            _In_  IUnknown *pDevice, // 1.D3D设备
            _In_  HWND hWnd,  // 2.窗体程序句柄
            _In_  const DXGI_SWAP_CHAIN_DESC1 *pDesc, // 3.传入交换链描述
            _In_opt_  const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, // 4.全屏运行用的交换链描述
            _In_opt_  IDXGIOutput *pRestrictToOutput, // 5.传空指针就行
            _COM_Outptr_  IDXGISwapChain1 **ppSwapChain // 接收创建的交换链

  显然易见,我们得先得到IDXGIFactory2的对象,然后再调用这个方法。
  之前在创建D3D设备时使用的是默认的显卡适配器IDXGIAdapter(对于双显卡的笔记本大概率使用的是集成显卡),而创建出来的D3D设备本身实现了IDXGIDevice接口,通过该对象,我们可以获取到当前所用的显卡适配器IDXGIAdapter对象,这样我们再通过查询它的父级找到是哪个IDXGIFactory枚举出来的适配器。
  具体什么意思呢?就是先得到D3D对象保存的适配器,然后通过HR(dxgiAdapter->GetParent(__uuidof(IDXGIFactory1), reinterpret_cast<void**>(dxgiFactory1.GetAddressOf())));得到IDXGIFactory2对象。
  那么IDXGIFactory2,IDXGIAdapter,IDXGIDevice之间到底是什么关系呢???GetParent意义是什么???
  先看一下StackOverflow上关于创建交换链的一些解释。

  这段代码是为了用DX11或之后的版本的接口创建交换链,同时这是特别为确保你使用的DXGI factor实例就是你创建D3D设备所使用的factory实例而开发的。
  基本上,当你第一次创建D3D11设备时,你可以选择提供一个IDXGIAdapter适配器对象来使用。大多数都用一个空指针让D3D设备自动指定一个默认的适配器了。为了完成交换链的初始化,然而,你必须得有一个DXGI factory对象。理论上你可以用DXGICreateFactory1来创建一个,但是你很容易搞砸而调用了传入错误枚举值的DXGICreateFactoryDXGICreateFactory2
  相反,最安全的方法是从你的ID3D11Device中得到IDXGIDevice。使用标准COM组件的IUnknown::QueryInterface:

IDXGIDevice * dxgiDevice = 0;
HRESULT hr = mD3dDevice->QueryInterface( __uuidof( IDXGIDevice ),( void ** ) & dxgiDevice );
if ( SUCCEEDED(hr) )

  接着从IDXGIDevice对象中使用IDXGIObject::GetParent得到IDXGIAdapter的适配器对象。

IDXGIAdapter * dxgiAdapter = 0;
hr = dxgiDevice->GetParent( __uuidof( IDXGIAdapter ),( void ** ) & dxgiAdapter );
if ( SUCCEEDED(hr) )

  然后再从IDXGIAdapter对象中再次使用IDXGIObject::GetParent

IDXGIFactory * dxgiFactory = 0;
hr = dxgiAdapter->GetParent( __uuidof( IDXGIFactory ),( void ** ) & dxgiFactory );
if ( SUCCEEDED(hr) )

  现在你就得到了与你的D3D设备关联的IDXGIFactory,不管你之前是怎么创建的D3D设备。一定记得COM引用计数器的意思是你现在得到了所有的这些引用还得释放掉。

dxgiFactory->Release();
dxgiAdapter->Release();
dxgiDevice->Release();

  注意IDXGIFactory::CreateSwapChain是DX11.0创建交换链的方法,而且D3D11CreateDeviceAndSwapChain方法的结果类似,而D3D11CreateDevice就不是。对于DX11.1或以后的版本,你最好用IDXGIFactory2::CreateSwapChainForHwnd,如果不是Win32桌面应用的话。对于Windows商店应用,Windows phone 8和XBOX One,你可以一直用IDXGIFactory2::CreateSwapChainForCoreWindow
  对于Win32桌面应用,你可以用以下的代码。

IDXGIFactory2* dxgiFactory2 = 0;
hr = dxgiFactory->QueryInterface( __uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2) );
if ( SUCCEEDED(hr) )
{
    
   // This system has DirectX 11.1 or later installed, so >we can use this interface
   dxgiFactory2->CreateSwapChainForHwnd( /* >parameters */ );
   dxgiFactory2->Release();
}
else
{
    
   // This system only has DirectX 11.0 installed
    dxgiFactory->CreateSwapChain( /* parameters */ >);
}

  See

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
不错的dx11入门教程 Tutorial 1: Setting up DirectX 11 with Visual Studio Tutorial 2: Creating a Framework and Window Tutorial 3: Initializing DirectX 11 Tutorial 4: Buffers, Shaders, and HLSL Tutorial 5: Texturing Tutorial 6: Diffuse Lighting Tutorial 7: 3D Model Rendering Tutorial 8: Loading Maya 2011 Models Tutorial 9: Ambient Lighting Tutorial 10: Specular Lighting Tutorial 11: 2D Rendering Tutorial 12: Font Engine Tutorial 13: Direct Input Tutorial 14: Direct Sound Tutorial 15: FPS, CPU Usage, and Timers Tutorial 16: Frustum Culling Tutorial 17: Multitexturing and Texture Arrays Tutorial 18: Light Maps Tutorial 19: Alpha Mapping Tutorial 20: Bump Mapping Tutorial 21: Specular Mapping Tutorial 22: Render to Texture Tutorial 23: Fog Tutorial 24: Clipping Planes Tutorial 25: Texture Translation Tutorial 26: Transparency Tutorial 27: Reflection Tutorial 28: Screen Fades Tutorial 29: Water Tutorial 30: Multiple Point Lights Tutorial 31: 3D Sound Tutorial 32: Glass and Ice Tutorial 33: Fire Tutorial 34: Billboarding Tutorial 35: Depth Buffer Tutorial 36: Blur Tutorial 37: Coming Soon... DirectX 10 Tutorials: Tutorial 1: Setting up DirectX 10 with Visual Studio Tutorial 2: Creating a Framework and Window Tutorial 3: Initializing DirectX 10 Tutorial 4: Buffers, Shaders, and HLSL Tutorial 5: Texturing Tutorial 6: Diffuse Lighting Tutorial 7: 3D Model Rendering Tutorial 8: Loading Maya 2011 Models Tutorial 9: Ambient Lighting Tutorial 10: Specular Lighting Tutorial 11: 2D Rendering Tutorial 12: Font Engine Tutorial 13: Direct Input Tutorial 14: Direct Sound Tutorial 15: FPS, CPU Usage, and Timers Tutorial 16: Frustum Culling Tutorial 17: Multitexturing and Texture Arrays Tutorial 18: Light Maps Tutorial 19: Alpha Mapping Tutorial 20: Bump Mapping Tutorial 21: Specular Mapping Tutorial 22: Render to Texture Tutorial 23: Fog Tutorial 24: Clipping Planes Tutorial 25: Texture Translation Tutorial 26: Transparency Tutorial 27: Reflection Tutorial 28: Screen Fades Tutorial 29: Water Tutorial 30: Multiple Point Lights Tutorial 31: 3D Sound Tutorial 32: Glass and Ice Tutorial 33: Fire Tutorial 34: Billboarding Tutorial 35: Depth Buffer Tutorial 36: Blur Tutorial 37: Coming Soon... DirectX 10 Terrain Tutorials: Tutorial 1: Grid and Camera Movement Tutorial 2: Height Maps Tutorial 3: Terrain Lighting Tutorial 4: Terrain Texturing Tutorial 5: Color Mapped Terrain Tutorial 6: Quad Trees Tutorial 7: Coming Soon... 。。。。。。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值