本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
http://blog.csdn.net/zhmxy555/article/details/7707628
作者:毛星云 邮箱: happylifemxy@qq.com 期待着与志同道合的朋友们相互交流
上一节里我们介绍了在迈入DirectX 11的学习旅程之后第一个demo创建的全过程。但由于知识衔接的需要,我们的第一个demo里面涉及到的大部分知识都是关于Win32的。而为了使之前讲解的Blank Win32 Window Demo蜕变成我们期望的Direct3D的模样,我们将在这节的笔记里面对Direct3D的入门级的基础知识做一个详细的介绍,以便在下节笔记里轻车熟路地写出属于我们的第一个完整的Direct3D11 Demo。
由于DirectX 11庞杂的知识体系,初学DirectX11在所难免地会面对浩如烟海而略显枯燥的基础知识的介绍,在依次介绍完必要的知识之后,浅墨会按照【Visual C++】游戏开发笔记系列专栏的传统风格,逐步放出几个有趣的基于DirectX 11的小游戏demo,敬请期待。
入门知识的第一步当然是进行DirectX开发环境的配置,这在笔记二十五里面有详细介绍,详情请移步:
【Visual C++】游戏开发笔记二十五 最简化的DirectX 11开发环境的配置
下面就开始正题,我们将分八个部分对入门级的Direct3D知识进行一个讲解。
一、 Direct3D的初始化
初始化Direct3D,我们需要完成以下四个步骤:
1.定义我们需要检查的设备类型(device types)和特征级别(feature levels)
2.创建Direct3D设备,渲染设备(context)和交换链(swap chain)。
3.创建渲染目标(render target)。
4.设置视口(viewport)
这里只是给大家一个框架的概念,各个部分下面会详细展开讲解。
二、驱动设备类型与特征等级
在Direct3D 11中我们能使用的设备有硬件设备(hardware device),参考设备(reference device),软件驱动设备(software driver device), 以及WARP设备 (WARP device)。
硬件设备(hardware device)是一个运行在显卡上的D3D设备,在所有设备中运行速度是最快的。这将是我们日后讨论最多的一种类型。
参考设备(reference device)是用于没有可用的硬件支持时在CPU上进行渲染的设备。
简言之,参考设备就是利用软件,在CPU对硬件渲染设备的一个模拟。但是不幸的是,这种方式非常的低效,所以在开发过程中,没有其他可用选择的时候,我们才采用这种方式。比如新一代的DirectX发布了,市面上还没有支持这种新版本DirectX的硬件,我们在开发过程中就只能采用这种方式来跑了。
软件驱动设备(software driverdevice)是开发人员自己编写的用于Direct3D的渲染驱动软件。这种方式通常不推荐用于高性能或者对性能要求苛刻的应用程序,下面介绍的WARP设备将是更好的选择。
WARP设备(WARPdevice)是一种高效的CPU渲染设备,可以模拟现阶段所有的Direct3D特性。WARP使用了Windows Vista /Windows 7/Winodws 8中的Windows Graphic 运行库中高度优化过的代码作为支撑,这让这种方式出类拔萃,相比与上文提到的参考设备(reference device)模式更加优秀。WARP设备在配置不高的机器上面可以达到化腐朽为神奇的功效。在我们的硬件不支持实时应用程序(real-time application)的情况下,用WARP设备作为替补是一个明智的选择,因为相比而言,参考设备(reference device)的执行效率实在是无法令人恭维。即便如此,WARP设备的执行效率还是不能和硬件设备同日而语,毕竟它依旧是对硬件的一种模拟,即使这种模拟是非常高效的。
注意:这不是对设备类型一个完整的列举,还有很多细枝末节的设备类型,在这里没必要一一列举
Direct3D的特征等级用于指定需要设定的设备目标。在这个专栏之中,我们将针对三种设备,第一种当然是我们的Direct3D 11设备,第二种为Direct3D 10.1设备,第三种为Direct3D 10.0设备。再这三种设备都无法支持的情况下,我们再选择WARP设备或者参考设备作为后援。
下面贴出来的代码段1为后面我们需要用到的驱动类型和特征级别的一个声明。通过创建各种类型的数组,我们可以使用循环来尝试首先创建我们最需要的设备,然后若执行失败则继续创建其他的设备类型。浅墨记得我们之前提到过,Win32宏ARRAYSIZE能够用来返回一个数组的大小,Win32函数GetClientRect可以用来计算应用程序客户区的大小。算出来的值会用于设置之后的D3D设备渲染的宽度和高度。
另外,需要记住Win32应用程序是分客户区和非客户区的,我们仅能在客户区上进行渲染。
代码段1 指明驱动设备类型和特征等级
RECT dimensions;
GetClientRect( hwnd, &dimensions );
unsigned int width = dimensions.right - dimensions.left;
unsigned int height = dimensions.bottom - dimensions.top;
D3D_DRIVER_TYPE driverTypes[] =
{
D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP,D3D_DRIVER_TYPE_SOFTWARE
};
unsigned int totalDriverTypes = ARRAYSIZE( driverTypes );
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0
};
unsigned int totalFeatureLevels = ARRAYSIZE( featureLevels );
三、设备与交换链的创建
下一步便是创建一个交换链,交换链在Direct3D中为一个设备渲染目标的集合。每一个设备都有至少一个交换链,而多个交换链能够被多个设备所创建。一个交换目标可以为一个渲染和显示到屏幕上的颜色缓存(在后面会讨论),等等。
通常在游戏中有,有两种颜色缓存,分别叫做主缓存和辅助缓存,他们一起被称为前后台缓存组合。主缓存中的内容(前台缓存)会显示在屏幕上,而辅助缓存(后台缓存)用于绘制下一帧(真是两个好基友-o-)。
渲染的发生非常之快,屏幕的一部分可以在显示器完成显示更新之前,在先前的结果为基础上进行绘制。缓存之间的切换,可以进行一个良性的运作,前台在显示图像,后台正在为前台准备下一刻将要显示的图像,这样做可以避免很多棘手的问题,提高了效率。
这种技术在计算机图形学中叫做双缓冲(doublebuffering),或者叫页面翻转(page flipping)(这种技术我们之前的一系列Win32 GDI demo中使用得比较勤,研究了之前的demo的朋友们应该已经耳濡目染了吧)。一个交换链能拥有一个或者多个这样的缓冲。
代码段2中列出了创建一个交换链的代码。一个交换链的描述用来定义和创建符合我们需要的交换链。
代码段2 对交换链的设置
DXGI_SWAP_CHAIN_DESC swapChainDesc;
ZeroMemory( &swapChainDesc, sizeof( swapChainDesc ) );
swapChainDesc.BufferCount = 1;
swapChainDesc.BufferDesc.Width = width;
swapChainDesc.BufferDesc.Height = height;
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator = 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator = 1;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.OutputWindow = hwnd;
swapChainDesc.Windowed = true;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
这个范例中定义了D3D的多种取样属性,多重取样(Multisampling)是一种用于采样和平衡渲染像素的创建亮丽色彩变化之间的平滑过渡的一种技术。
缓存的使用和交换链的描述有大量的成员需要设置,但这些设置都是非常简单的。缓存的对交换链的使用是设置下DXGI_USAGE_RENDER_TARGET_OUTPUT,以便交换链能够用于输出,或者换句话说,它能被渲染。
下一步是创建渲染上下文,渲染设备,以及我们拥有的交换链描述。D3D设备一般都是设备本身和硬件之间的通信,而D3D上下文是一种描述设备如何绘制的渲染设备上下文,这也包含了渲染状态和其他的绘图信息。
正如我们讨论过的,交换链是设备和上下文将要绘制的渲染目标。
创建设备上下文,渲染上下文和交换链所需的代码在代码段3中详细列出了,.这段代码为下次内容即将展示的Direct3D 11 BlankWindows Demo的一个片段。
代码段3 Direct3D设备,设备上下文,以及交换链的创建
ID3D11Device device_;
ID3D11Context d3dContext_;
IDXGISwapChain swapChain_;
unsigned int creationFlags = 0;
#ifdef _DEBUG
creationFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
HRESULT result;
unsigned int driver = 0;
for( driver = 0; driver < totalDriverTypes; ++driver )
{
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( "Failed to create the Direct3D device!");
return false;
}
交换链,设备和渲染上下文可以在单独的Direct3D函数调用中被创建,或者通过特定对象的Direct3D来调用(例如用CreateSwapChain函数来专门创建一个交换链)。
这个函数为D3D11CreateDeviceAndSwapChain。在代码段2中我们在每个驱动类型中循环,试图创建一个合适得设备,或为一个硬件设备,或为一个WARP设备,抑或一个参考设备(reference device)。因为如果创建失败,我们就无法初始化我们的Direct3D。
D3D11CreateDeviceAndSwapChain函数中包含了特征等级作为其参数, 所以如果至少有一个这样的特征等级存在,而且若我们的设备类型也存在,这个函数才会执行成功。
其中D3D11CreateDeviceAndSwapChain函数具有如下的函数原型:
HRESULT D3D11CreateDeviceAndSwapChain(
IDXGIAdapter *pAdapter,
D3D_DRIVER_TYPEDriverType,
HMODULE Software,
UINT Flags,
const D3D_FEATURE_LEVEL*pFeatureLevels,
UINT FeatureLevels,
UINT SDKVersion,
const DXGI_SWAP_CHAIN_DESC *pSwapChainDesc,
IDXGISwapChain **ppSwapChain,
ID3D11Device **ppDevice,
D3D_FEATURE_LEVEL*pFeatureLevel,
ID3D11DeviceContext**ppImmediateContext
);
四、 创建渲染目标视图
一个渲染目标视图是一个由Output MergerStage读取的D3D资源。为了output merger能渲染一个后台缓存的交换链,我们为其创建一个渲染目标视图。
由于纹理的概念说来话长,目前我们将纹理理解为一副图像就行了,后面中我们将展开讨论纹理的很多细节内容,。交换链的主缓存和辅助缓存为彩色的图像,为了获得它们的指针,我们一般会调用交换链中的函数GetBuffer。
得到指向缓存的指针后,我们调用Direct3D中的函数CreateRenderTargetView,来创建一个渲染目标视图(rendertarget view.)。渲染目标视图含有ID3D11RenderTargetView类型,而CreateRenderTargetView函数将创建我们视图的2D纹理,渲染目标描述,我们创建的ID3D11RenderTargetView的对象地址为其函数变量。将渲染目标描述变量设为空给我们所有的表面的MIP映射水平都为0级,MIP映射水平也将在后面进行详细讨论。
我们完成渲染目标的创建之后,就能够释放指针到交换链的后台缓存了。因为得到了COM对象的一个引用,我们必须调用COM中的Release函数来减少引用的数量。这样做会避免内存的泄露,因为我们不想应用程序退出后,系统仍然保留着这里内存,这将导致系统资源的浪费,而这种浪费是不科学的。
在每次我们想渲染一个特定的渲染目标的时候,必须在所有的绘制的函数调用之前对它进行设置。这个重任就交给了我们的OMSetRenderTarget函数,这个函数隶属于output merger,在之后会讲到。
代码段4 渲染目标视图的创建和绑定
ID3D11RenderTargetView* backBufferTarget_;
ID3D11Texture2D* backBufferTexture;
HRESULT result = swapChain_->GetBuffer( 0, __uuidof(ID3D11Texture2D ),
( LPVOID* )&backBufferTexture );
if( FAILED( result ) )
{
DXTRACE_MSG( "Failed to get the swap chain backbuffer!" );
return false;
}
result = d3dDevice_->CreateRenderTargetView(backBufferTexture, 0,
&backBufferTarget_ );
if( backBufferTexture )
backBufferTexture->Release( );
if( FAILED( result ) )
{
DXTRACE_MSG( "Failed to create the render targetview!" );
return false;
}
d3dContext_->OMSetRenderTargets( 1, &backBufferTarget_, 0);
在代码段4中你会注意到我们采用了一个叫做DXTRACE_MSG的宏。这个宏用作debugging来用。在之后将进行更详细的讲解。
五、 视口
Direct3D中 的一个重点同时也是难点在于创建和设置视口。
视口定义了我们渲染到屏幕上的面积。在单人或者非分割画面的多人游戏中一般都为全屏,所以我们设置视口的宽度和高度即为交换链的宽度和高度。对于分屏游戏,我们可以创建两个视口,一个视口定义在屏幕上方,另一个定义在屏幕下方。为了渲染分屏视口,我们可以分别以两位不同玩家的角度来渲染。
视点的创建由填充D3D11_VIEWPORT函数和设置调用上下文的RSSetViewports函数将其设置到渲染上下文中来完成。RSSetViewports函数需要我们设置的视口数量和视口对象的列举。全屏视口的创建和设置的相关代码在代码段五中有列举,其中X和Y标明左侧和顶部屏幕的位置,最小和最大深度是0到1之间的值,表明了视口深度的最小和最大值。
代码段5 全屏视口的创建和设置
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 );
六、清除与显示屏幕
渲染到屏幕需要几个不同的步骤。第一步通常是清除相关渲染目标的表面。在大部分游戏中这一步包含了深度缓存等一系列内容。在下一节即将呈现的demo中我们将在本章稍后实施,我们将清除渲染目标视图的颜色缓冲区到一种特定的颜色。这由调用D3D中的ClearRenderTargetView函数来完成。ClearRenderTargetView拥有如下的函数原型:
void ClearRenderTargetView( ID3D11RenderTargetView*pRenderTargetView,
const FLOAT ColorRGBA[4] );
注:目前的大部分商业游戏中在渲染之前清除颜色缓存并不是必须的,因为像天空这样的环境图形要确保每个像素都会被颜色缓存所覆盖着。
ClearRenderTargetView函数以将被清理的渲染目标视图作为其变量。为了清除屏幕,我们设定某种颜色作为我们需要的背景阴影的颜色。这种颜色可以是红色,绿色,蓝色,和透明色Alpha数组中任意指定的0.0到1.0之间的颜色。这里0.0表示强度为0,而1.0表示完全饱满的强度。若对应于字节,1.0对应255。如果为红绿蓝颜色组合都为1.0,则会得到纯白的颜色。下一步就是绘制场景的几何形状了,最后一步是调用交换链的Present函数在屏幕上显示渲染缓冲区的内容。
Present函数具有以下的声明:
HRESULT IDXGISwapChain::Present( UINT SyncInterval, UINT Flags);
对Present函数的参数一个简单的理解:syncinterval 同步间隔,
flags 演示的标志。
在第n个垂直空白之后,Syncinterval能被设置为0,1,2,3,4来显示。垂直空白是当前帧的最后一列更新时间与下一帧的第一列更新时间的时间差。像电脑显示器这样的设备显示更新像素为垂直的,一列一列进行更新的。
Present函数的flags值可被设为0,表示输出到每一个缓冲区,设为DXGI_PRESENT_ TEST时则表示测试时不进行输出,或为DXGI_PRESENT_DO_ NOT_SEQUENCE表示不进行排序地利用垂直空白同步输出来显示输出。为达到预期的目的,我们可以只是传递0到Present函数来显示我们的渲染结果。
代码段五 展示了一个清屏和显示视图的例子。在后面我们将深入探究颜色缓存,深度存,使画面流畅无比的双缓冲等等。
代码段6 清除渲染目标然后显示显得渲染场景
float clearColor[4] = { 0.0f, 0.0f, 0.25f, 1.0f };
d3dContext_->ClearRenderTargetView( backBufferTarget_,clearColor );
swapChain_->Present( 0, 0 );
七、关于格式
有时候我们需要创建指定的DXGI格式。格式可以用于描述一张图像的布局,每种颜色的位数,或者顶点缓存的布局(后面会讲到)。大多数情况下,DXGI格式用于描述交换链中的顶点布局。
举个例子,DXGI_FORMAT_R8G8B8A8_UNORM,它表示我们定义的每一个RGBA组成部分的数据都为8位。
没指名类型的格式我们称作无类型格式(typeless formats)。他们为每个部分保存相同的位数,但是并不注重包含了什么类型的数据。如DXGI_FORMAT_R32G32B32A32_TYPELESS。常用的清单类型在下面中列出了。
常用的数据格式类型清单:
相关推荐
### Direct3D 11 入门级知识 **1. Direct3D 11 的安装与配置** 在开发基于 Direct3D 11 的游戏之前,首先需要确保开发环境中正确安装了必要的 SDK 和工具。这通常包括安装最新的 Windows SDK 和 Visual Studio 等。...
《Visual+C++游戏开发经典案例详解》是一本深入探讨使用Microsoft Visual C++进行游戏开发的专业书籍,其随书代码提供了丰富的实例,旨在帮助读者更好地理解和掌握游戏编程的核心技术。本书覆盖了从基础到高级的游戏...
《Visual C++游戏设计入门》是一本面向初学者的编程教程,主要讲解如何利用Microsoft的Visual C++环境进行游戏开发。这本书的配套源码包含了多个章节的实例代码,旨在帮助读者通过实践理解游戏设计的基本原理和技术...
本文将深入探讨利用Visual C++进行游戏编程框架和引擎的构建,并结合Michael Morrison的《游戏编程入门》一书中的理论与实践,为读者提供详尽的知识点解析。 首先,Visual C++提供了MFC(Microsoft Foundation ...
### C++游戏编程指南知识点概览 #### 一、C++基础编程概念 **1.1 VC.net概述** - **环境介绍**:本指南基于Visual C++ 7.0(简称VC.net)进行讲解,该环境是Microsoft Visual Studio的一个组成部分,用于支持C++...
在本资源中,"Visual C++/DirectX9 3D游戏开发导引"是由叶至军编著的一本深入3D游戏编程的教程,主要针对使用Microsoft的Visual C++和DirectX9技术进行游戏开发的初学者和进阶者。这本书通过实例源程序的方式,帮助...
《Visual C++游戏编程基础》是一本面向初学者的教程,旨在帮助读者掌握使用Microsoft的Visual C++开发游戏的基本技能。这本书可能包含了从基础的编程概念到更高级的游戏引擎组件的详细讲解。以下是对该书内容的一些...
1. **DirectX基础**:介绍DirectX的组成,如Direct3D、DirectInput、DirectSound等组件,以及它们在游戏开发中的作用。 2. **Direct3D 11**:讲解3D图形编程的基础,如顶点、索引、多边形、坐标系统、视图和投影...
《DirectX+10+3D游戏编程入门》是一本专为希望深入了解Direct3D 10及其在3D游戏开发中应用的读者准备的书籍。由Frank D. Luna撰写,汤毅翻译,该书旨在教授Direct3D与着色器编程的基础知识,特别适合于中级C++程序员...
### Direct3D9入门教程知识点概览 #### 直接3D DirectX游戏开发基础 **Direct3D9**是Microsoft DirectX套件的一部分,主要用于3D图形处理,特别适合于游戏开发和其他高性能图形应用。本教程旨在引导初学者通过一...
在标签中提到的“DirectX 源代码”可能包含在`d3dlesson`目录下,比如`d3dlesson1`和`d3dlesson7`,这些文件可能分别代表Direct3D的入门教程和进阶教程,涵盖了设备初始化、顶点缓冲区、索引缓冲区、纹理贴图等概念...
### DirectX 10 3D 游戏编程入门知识点概览 #### 一、DirectX 10与3D游戏编程基础 - **DirectX 10简介**: - DirectX 10是由微软开发的一套多媒体编程接口,主要用于Windows操作系统上的游戏和其他多媒体软件的...
《Visual C++数字图像处理开发入门与编程实践》是一本专为C++初学者和有经验的开发者设计的书籍,旨在介绍如何利用Microsoft的Visual C++环境进行数字图像处理的编程实践。随书源码提供了丰富的示例和项目,帮助读者...
- **DirectDraw与3D图形**:学习使用DirectDraw进行2D绘图,以及Direct3D进行3D渲染。 - **游戏逻辑与物理**:掌握游戏中的逻辑处理和物理模拟技巧。 6. **实战项目经验** - **小项目实践**:通过实际编写简单的...
总之,《Visual C++从入门到精通视频教程》第15章将引导你探索多媒体编程的世界,无论你是初学者还是有一定经验的开发者,都能从中获得宝贵的知识和实践经验。通过学习和实践,你将能够创建出具有丰富多媒体功能的...