direct3d_direct3d9 - CSDN
精华内容
参与话题
  • Direct3D 程序结构   与普通的win32 窗口程序不同的地方:在初始化时需要对Direct3D进行初始化,并且在程序中具有推行绘制过程。         第一步: 创建 IDirect3D9接口对象,用于获取本机显示硬件...

    Direct3D 程序结构

     

    与普通的win32 窗口程序不同的地方:在初始化时需要对Direct3D进行初始化,并且在程序中具有推行绘制过程。

     

     


     

     

    第一步: 创建 IDirect3D9接口对象,用于获取本机显示硬件设备的信息,以及创建用于绘制3D图形的 IDirect3DDevice9 接口对象

     LPDIRECT3D9 pD3D=NULL;

    pD3D=Direct3DCreate9(D3D_SDK_VERSION);

    第二步: 获取硬件设备信息

    获取硬件设备信息包括取得系统中所有可用的图形卡的性能、显示模式、格式及其他相关信息。例如:通常用来检测图形卡(或显卡)是否支持硬件顶点运算

     

     

     

    Direct3D可以有两种不同的顶点运算方式,即 软件顶点运算和硬件顶点运算。其中,硬件顶点运算需要有显卡的支持,这种凡事将使用硬件专有的加速功能,其执行速度将远远快于软件顶点运算方式。

     

     

     

    第三步:创建Direct3D设备接口

    在创建 IDirect3DDevice9 接口对象之前,需要准备一个 D3DPRESENT_PARAMETERS

    结构类型的参数,用于说明如何创建Direct3D 设备

     

      BackBufferWidth  BackBufferHeight   BackBufferCount 字段指定后台缓冲区的宽度、高度和数量。后台缓冲区是临时保存绘制内存的一片内存块,在通常情况下,其大小与窗口客户区的大小相同。

        BackBufferFormat 字段指定了后台缓冲区保存像素的格式,它可以用D3DFORMAT 枚举定义

     

    MultiSampleType  和 MultiSampleQuality  成员指定了 多重采样的类型及格式,通常设为

    D3DMULTISAMPLE_NONE 0

    SwapEffect  成员指定 Direct3D  如何将后台缓冲区的内容复制到前台的缓存中,通常设为D3DSWAPEFFECT_DISCARD ,表示当后台缓冲区赋值到前台时清楚后台缓冲区内容

      hDeviceWindow 成员用于指定绘制图形的窗口句柄,为NULL时表示使用当前被激活的窗口。绘制窗体的显示模式由 Windowed 成员指定,为TRUE时表示使用窗口模式,为FASLE表示使用全屏模式

    EnableAutoDepthStencil 表示 Direct3D 是否为应用程序自动管理深度缓存,若为TRUE,那么需要AutoDepthStencilFormat 成员指定深度缓冲的像素格式

     

    FullScreen_RefreshRateInHz  表示当全屏模式时指定屏幕的刷新模式,在全屏模式下可以设为默认模式D3DPRESENT_RATE_DEFAULT,  在窗口模式下,要设为0

    Flags 用于表示附加属性,通常可以设为 0:  而 PresentationInterval 成员用于指定平面的翻转模式,在窗口模式下 其值为0

     

     

    设置好 D3DPRESENT_PARAMETERS 结构后,调用IDirect3D9接口的 CreateDeivce 方法创建IDirect3DDevice9 接口对象

     

     

    Adapter  表示将创建的IDirect3DDevice9 接口对象所代表的显卡类型,可以为D3DDEVTYPE_HAL  D3DDEVTYPE_REF  D3DDEVTYPE_SW

    BehaviorFlags 指定了 Direct3D 设备进行3D 运算的方式,通常为 D3DCREATE_HARDWARE_VERTEXPROCESSING或者 D3DCREATE_SOFTWARE_VERTEXPROCESSING  分别表示进行硬件顶点运算或 软件顶点运算

     

    上面这些都是为绘图做的准备工作,创建 IDirect3D9 接口对象,然后围绕着接口对象 先获取设备信息判断是否支持硬件顶点运算,然后创建 IDirect3DDevice9 接口对象。

    在这些准备工作做好之后,开始 Direct3D 图形绘制

    计算机绘图的工作方式与电影放映其实十分类似,影片图像以帧为单位,以每秒24帧的速度连续播放,由于这些图像之间的差别十分小,以及人眼滞留作用,所有我们可以看到连续的动画。同理,计算机绘图,是通过每秒渲染 30 或更多的图像,产生动画的效果。Direct3D 使用了一种称为 交换链(Page Flipping)的技术让画面平滑的过渡。

    对于 3D 图形,还需要一个深度信息,Direct3D 使用深度缓存为最终绘制的图像的每个像素存储一个深度信息。深度缓存只包含了特定像素的深度信息而不含图像数据的表面信息。

     

    前台缓存和后台缓存是位于系统内存或显卡里的内存块,对应于将要显示的二维显示区域。前台缓冲区主要用于刷新屏幕,是用户可见的部分,后台缓冲区则主要用于图形的绘制,堆用户是不可见的。绘制完成后,两者进行交换链的页翻动的工作,把处理完的图形显示出来。

     

     

     

    Direct3D 的绘制过程就是 : 绘制 ->显示 ->绘制 -> 显示。

    每当开始绘制图形之前,都需要通过 IDirect3DDevice9 接口的 Clear 方法将后台缓存中的内容进行清空,并设置表面的填充颜色

     

     

     

    pRects 用于指定堆表面指定的矩形区域进行清除操作,数组中包含的矩形的数量由Count 参数指定

    Flags  指定需要清除的表面,该参数可以取值于 D3DCLEAR_TARGET D3DCLEAR_ZBUFFER D3DCLEAR_STENCIL, 分别表示对后台缓存、深度缓存和模板缓存进行清除

    Color 参数用于指定在清除缓存内容后设置的背景色,可以通过 D3DCOLOR_XRGB 宏将 RGB值转换给该参数

    Z参数用于指定当清除缓存内容后设置深度缓存的值,通常设为1.0f 而 Stencil参数则表示设置模板缓存的值,通常设为0

    图形的实际绘制工作是在调用  IDirect3DDevice9 接口的 BeginScene 和 EndScene 方法之间进行的。调用 BeginScene  方法通知 Direct3D 设备开始进行图形渲染工作,而 EndScene 方法则通知Direct3D 设备结束图形渲染工作

     g_pd3dDevice->BeginScene();                     // 开始绘制

     

        /* 图形的实际绘制过程*/

     

        g_pd3dDevice->EndScene();                       // 结束绘制

    绘制图形结束后,此时所有已绘制的内容将保存在后台缓存里,为了能够在屏幕上显示这些内容,就需要通知 Direct3D将后台缓存中的内容复制到前台缓存中。 IDirect3DDevice9 接口的 Present 方法 用于前台缓存与后台缓存之间的翻转

     

    最后的收尾工作-------结束并清理对象资源

    由于COM 接口对象是通过控制对它的引用计数器来决定声明周期的,所以当对某个COM 接口对象引用结束后,应用程序必须调用 Release 方法释放接口,而此时该对象的引用计数器减。如果最终引用计数器为,那么该对象占用的内存将被释放

     

     

    展开全文
  • DirectX11 Direct3D基本概念

    千次阅读 2015-09-27 20:20:09
    Direct3D基本概念 1. Direct3D概述 Direct3D是一种底层绘图API(application programming interface,应用程序接口),它可以让我们可以通过3D硬件加速绘制3D世界。从本质上讲,Direct3D提供的是一组软件接口,...

    Direct3D基本概念
    1. 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规定的所有功能特性。

    2. 关于COM(组件对象模型)接口
    组件对象模型(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。

    3. 纹理和数据资源格式
    2D纹理(texture)是一种数据元素矩阵。2D纹理的用途之一是存储2D图像数据,在纹理的每个元素中存储一个像素颜色。但这不是纹理的唯一用途;例如, 有一种称为法线贴图映射(normal mapping)的高级技术在纹理元素中存储的不是颜色,而是3D向量。因此,从通常意义上讲,纹理用来存储图像数据,但是在实际应用中纹理可以有更广泛的用途。1D纹理类似于一个1D数据元素数组,3D 纹理类似于一个3D数据元素数组。但是在随后的章节中我们会讲到,纹理不仅仅是一个数据数组;纹理可以带有多级渐近纹理层(mipmap level),GPU可以在纹理上执行特殊运算,比如使用过滤器(filter)和多重采样(multisampling)。此外,不是任何类型的数据都能存储到纹理中的;纹理只支持特定格式的数据存储,这些格式由DXGI_FORMAT枚举类型描述。一些常用的格式如下:

    DXGI_FORMAT_R32G32B32_FLOAT:每个元素包含3个32位浮点分量。
    DXGI_FORMAT_R16G16B16A16_UNORM:每个元素包含4个16位分量,分量的取值范围在[0,1]区间内。
    DXGI_FORMAT_R32G32_UINT:每个元素包含两个32位无符号整数分量。
    DXGI_FORMAT_R8G8B8A8_UNORM:每个元素包含4个8位无符号分量,分量的取值范围在[0,1]区间内的浮点数。
    DXGI_FORMAT_R8G8B8A8_SNORM:每个元素包含4个8位有符号分量,分量的取值范围在[−1,1] 区间内的浮点数。
    DXGI_FORMAT_R8G8B8A8_SINT:每个元素包含4个8位有符号整数分量,分量的取值范围在[−128, 127] 区间内的整数。
    DXGI_FORMAT_R8G8B8A8_UINT:每个元素包含4个8位无符号整数分量,分量的取值范围在[0, 255]区间内的整数。

    注意,字母R、G、B、A分别表示red(红)、green(绿)、blue(蓝)和alpha(透明度)。每种颜色都是由红、绿、蓝三种基本颜色组成的(例如,黄色是由红色和绿色组成的)。alpha通道(或alpha分量)用于控制透明度。不过,正如我们之前所述,纹理存储的不一定是颜色信息;例如,格式DXGI_FORMAT_R32G32B32_FLOAT包含3个浮点分量,可以存储一个使用浮点坐标的3D向量。另外,还有一种弱类型(typeless)格式,可以预先分配内存空间,然后在纹理绑定到管线时再指定如何重新解释数据内容(这一过程与C++中的数据类型转换颇为相似);例如,下面的弱类型格式为每个元素预留4个8位分量,且不指定数据类型(例如:整数、浮点数、无符号整数):
    DXGI_FORMAT_R8G8B8A8_TYPELESS

    4. 交换链和页面交换
    为了避免在动画中出现闪烁,最好的做法是在一个离屏(off-screen)纹理中执行所有的动画帧绘制工作,这个离屏纹理称为后台缓冲区(back buffer)。当我们在后台缓冲区中完成给定帧的绘制工作后,便可以将后台缓冲区作为一个完整的帧显示在屏幕上;使用这种方法,用户不会察觉到帧的绘制过程,只会看到完整的帧。从理论上讲,将一帧显示到屏幕上所消耗的时间小于屏幕的垂直刷新时间。硬件会自动维护两个内置的纹理缓冲区来实现这一功能,这两个缓冲区分别称为前台缓冲区(front buffer)和后台缓冲区。前台缓冲区存储了当前显示在屏幕上的图像数据,而动画的下一帧会在后台缓冲区中执行绘制。当后台缓冲区的绘图工作完成之后,前后两个缓冲区的作用会发生翻转:后台缓冲区会变为前台缓冲区, 而前台缓冲区会变为后台缓冲区,为下一帧的绘制工作提前做准备。我们将前后缓冲区功能互换的行为称做呈现(presenting)。提交是一个运行速度很快的操作,因为它只是将前台缓冲区的指针和后台缓冲区的指针做了一个简单的交换。
    这里写图片描述
    (我们首先渲染缓冲区B,它是当前的后台缓冲区。一旦帧渲染完成,前后缓冲区的指针会相互交换,缓冲区B会变为前台缓冲区,而缓冲区A会变为新的后台缓冲区。之后,我们将在缓冲区A中进行下一帧的渲染。一旦帧渲染完成,前后缓冲区的指针会再次进行交换,缓冲区A会变为前台缓冲区,而缓冲区B会再次变为后台缓冲区。)

    前后缓冲区形成了一个交换链(swap chain)。在Direct3D中,交换链由IDXGISwapChain接口表示。该接口保存了前后缓冲区纹理,并提供了用于调整缓冲区尺寸的方法(IDXGISwapChain::ResizeBuffers)和呈现方法(IDXGISwapChain::Present)。我们会在4.4节中详细讨论些方法。

    使用(前后)两个缓冲区称为双缓冲(double buffering)。缓冲区的数量可多于两个;比如,当使用三个缓冲区时称为三缓冲(triple buffering)。不过,两个缓冲区已经足够用了。

    注意:虽然后台缓冲区是一个纹理(纹理元素称为texel),但是我们更习惯于将纹理元素称为像素(pixel),因为后台缓冲区存储的是颜色信息。有时,即使纹理中存储的不是颜色信息,人们还是会将纹理元素称为像素(例如,“法线贴图像素”)。

    5. 深度缓冲区
    深度缓冲区(depth buffer)是一个不包含图像数据的纹理对象。在一定程度上,深度信息可以被认为是一种特殊的像素。常见的深度值范围在0.0到1.0之间,其中0.0表示离观察者最近的物体,1.0表示离观察者最远的物体。深度缓冲区中的每个元素与后台缓冲区中的每个像素一一对应(即,后台缓冲区的第ij个元素对应于深度缓冲区的第ij个元素)。所以,当后台缓冲区的分辨率为1280×1024时,在深度缓冲区中有1280×1024个深度元素。
    这里写图片描述
    为了判定物体的哪些像素位于其他物体之前,Direct3D使用了一种称为深度缓存(depth buffering)或z缓存(z-buffering)的技术。我们所要强调的是在使用深度缓存时,我们不必关心所绘物体的先后顺序。

    注意:要处理深度的问题,有人可能会建议按照从远至近的顺序绘制场景中的物体。使用这种方法,离得近的物体会覆盖在离得远的物体之上,这样就会产生正确的绘制结果,这也是画家作画时用到的方法。但是,这种方法会导致另一个问题——如何将大量的物体和相交的几何体按从远到近的方式进行排序?此外,图形硬件本身就提供了深度缓存供我们使用,因此我们不会采用画家算法。

    为了说明深度缓存的工作方式,让我们来看一个例子。如图所示,它展示的是观察者看到的立体空间(左图)以及该立体空间的2D侧视图(右图)。从这个图中我们可以发现,3个不同的像素会被渲染到视图窗口的同一个像素点P上。(当然,我们知道只有最近的像素会被渲染到P上,因为它挡住了后面的其他像素,可是计算机不知道这些事情。)首先,在渲染之前,我们必须把后台缓冲区清空为一个默认颜色(比如黑色或白色),把深度缓冲区清空为默认值——通常设为1.0(像素所具有的最远深度值)。

    这里写图片描述
    (图窗口相当于从3D场景生成的2D图像(后台缓冲区)。我们看到,有3个不同的像素可以被投影到像素P上。直觉告诉我们,P1是P的最终颜色,因为它离观察者最近,而且遮挡了其他两个像素。深度缓冲区算法提供了一种可以在计算机上实现的判定过程。注意,我们所说的深度值是相对于观察坐标系而言的。实际上,当深度值存入深度缓冲区时,它会被规范到[0.0,1.0]区间内。)

    现在,假设物体的渲染顺序依次为:圆柱体、球体和圆锥体。下面的表格汇总了在绘制些物体时像素P及相关深度值的变化过程;其他像素的处理过程与之类似。

    当我们发现某个像素具有更小的深度值时,就更新该像素以及它在深度缓冲区中的相应深度值。通过一方式,在最终得到的渲染结果中只会包含那些离观察者最近的像素。(如果读者对此仍有疑虑,那么可以试着交换本例的绘图顺序,看看得到的计算结果是否相同。)

    综上所述,深度缓冲区用于为每个像素计算深度值和实现深度测试。深度测试通过比较像素深度来决定是否将该像素写入后台缓冲区的特定像素位置。只有离观察者最近的像素才会胜出,成为写入后台缓冲区的最终像素。这很容易理解,因为离观察者最近的像素会遮挡它后面的其他像素。

    深度缓冲区是一个纹理,所以在创建它时必须指定一种数据格式。用于深度缓存的格式如下:

    DXGI_FORMAT_D32_FLOAT_S8X24_UINT:32位浮点深度缓冲区。为模板缓冲区预留8位(无符号整数),每个模板值的取值范围为[0,255]。其余24位闲置。
    DXGI_FORMAT_D32_FLOAT:32位浮点深度缓冲区。
    DXGI_FORMAT_D24_UNORM_S8_UINT:无符号24位深度缓冲区,每个深度值的取值范围为[0,1]。为模板缓冲区预留8位(无符号整数),每个模板值的取值范围为[0,255]。
    DXGI_FORMAT_D16_UNORM:无符号16位深度缓冲区,每个深度值的取值范围为[0,1]。

    注意:模板缓冲区对应用程序来说不是必须的,但是如果用到了模板缓冲区,那么模板缓冲区必定是与深度缓冲区存储在一起的。例如,32位格式DXGI_FORMAT_D24_UNORM_S8_UINT使用24位用于深度缓冲区,8位用于模板缓冲区。 所以,将深度缓冲区称为“深度/模板缓冲区”更为合适。模板缓冲区是一个比较高级的主题,我们会在第10章讲解模板缓冲区的用法。

    6. 纹理资源视图
    纹理可以被绑定到渲染管线(rendering pipeline)的不同阶段(stage);例如,比较常见的情况是将纹理作为渲染目标(即,Direct3D渲染到纹理)或着色器资源(即,在着色器中对纹理进行采样)。当创建用于这两种目的的纹理资源时,应使用绑定标志值:

    D3D11_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE

    指定纹理所要绑定的两个管线阶段。其实,资源不能被直接绑定到一个管线阶段;我们只能把与资源关联的资源视图绑定到不同的管线阶段。无论以哪种方式使用纹理,Direct3D始终要求我们在初始化时为纹理创建相关的资源视图(resource view)。这样有助于提高运行效率,正如SDK文档指出的那样:“运行时环境与驱动程序可以在视图创建执行相应的验证和映射,减少绑定时的类型检查”。所以,当把纹理作为一个渲染目标和着色器资源时,我们要为它创建两种视图:渲染目标视图(ID3D11RenderTargetView)和着色器资源视图(ID3D11ShaderResourceView)。资源视图主要有两个功能:(1)告诉Direct3D如何使用资源(即,指定资源所要绑定的管线阶段);(2)如果在创建资源时指定的是弱类型(typeless)格式,那么在为它创建资源视图时就必须指定明确的资源类型。对于弱类型格式,纹理元素可能会在一个管线阶段中视为浮点数,而在另一个管线阶段中视为整数。为了给资源创建一个特定视图,我们必须在创建资源时使用特定的绑定标志值。例如,如果在创建资源没有使用D3D11_BIND_DEPTH_STENCIL绑定标志值(该标志值表示纹理将作为一个深度/模板缓冲区绑定到管线上),那我们就无法为该资源创建ID3D11DepthStencilView视图。只要你试一下就会发现Direct3D会给出如下调试错误:

    ERROR: ID3D11Device::CreateDepthStencilView:
    A DepthStencilView cannot be created of a Resource that did not specify D3D10_BIND_DEPTH_STENCIL.

    注意:2009年8月的SDK文档指出:“当创建资源时,为资源指定强类型(fully-typed)格式,把资源的用途限制在格式规定的范围内,有利于提高运行时环境对资源的访问速度……”。所以,你只应该在真正需要弱类型资源时(使用弱类型的优点是可以使用不同的视图将数据用于不同的用途),才创建弱类型资源;否则,应尽量创建强类型资源。

    7. 多重采样
    超级采样抗锯齿(Super-Sampling Anti-aliasing,简称SSAA)此是早期抗锯齿方法,比较消耗资源,但简单直接,先把图像映射到缓存并把它放大,再用超级采样把放大后的图像像素进行采样,一般选取2个或4个邻近像素,把这些采样混合起来后,生成的最终像素,令每个像素拥有邻近像素的特征,像素与像素之间的过渡色彩,就变得近似,令图形的边缘色彩过渡趋于平滑。再把最终像素还原回原来大小的图像,并保存到帧缓存也就是显存中,替代原图像存储起来,最后输出到显示器,显示出一帧画面。这样就等于把一幅模糊的大图,通过细腻化后再缩小成清晰的小图。如果每帧都进行抗锯齿处理,游戏或视频中的所有画面都带有抗锯齿效果。
    多重采样抗锯齿(MultiSampling Anti-Aliasing,简称MSAA)是一种特殊的超级采样抗锯齿(SSAA)。MSAA首先来自于OpenGL。具体是MSAA只对Z缓存(Z-Buffer)和模板缓存(Stencil Buffer)中的数据进行超级采样抗锯齿的处理。可以简单理解为只对多边形的边缘进行抗锯齿处理。这样的话,相比SSAA对画面中所有数据进行处理,MSAA对资源的消耗需求大大减弱,不过在画质上可能稍有不如SSAA。

    这里写图片描述
    (如左图所示,一个像素与多边形的边缘相交,像素中心的绿颜色存储在可见的三个子像素中,而第4个子像素没有被多边形覆盖,因此不会被更新为绿色,它仍保持为原来绘制的几何体颜色或Clear操作后的颜色。如右图所示,要获得最后的像素颜色,我们需要对4个子像素(3个绿色和一个白色)取平均值,获得淡绿色,通过这个操作,可以减弱多边形边缘的阶梯效果,实现更平滑的图像。)

    注意:在上图中,我们用标准的网格图形表示一个像素的4个子像素,但由于硬件的不同,实际的子像素放置图形也是不同的,Direct3D并不定义子像素的放置方式,在特定情况下,某些放置方式会优于其他的放置方式。

    8. Direct3D中的多重采样
    我们要填充一个DXGI_SAMPLE_DESC结构体。该结构体包含两个成员,其定义如下:

    typedef struct DXGI_SAMPLE_DESC
    {
        UINT Count;
        UINT Quality;
    } DXGI_SAMPLE_DESC, *LPDXGI_SAMPLE_DESC;

    Count成员用于指定每个像素的采样数量,Quality成员用于指定希望得到的质量级别(不同硬件的质量级别表示的含义不一定相同)。质量级别越高,占用的系统资源就越多,所以我们必须在质量和速度之间权衡利弊。质量级别的取值范围由纹理格式和单个像素的采样数量决定。我们可以使用如下方法,通过指定纹理格式和采样数量来查询相应的质量级别:

    HRESULT ID3D11Device::CheckMultisampleQualityLevels(
        DXGI_FORMAT Format, UINT SampleCount, UINT *pNumQualityLevels);

    如果纹理格式和采样数量的组合不被设备支持,则该方法返回0。反之,通过pNumQualityLevels参数返回符合给定的质量等级数值。有效的质量级别范围为0到pNumQualityLevels−1。

    采样的最大数量可以由以下语句定义:

    #define D3D11_MAX_MULTISAMPLE_SAMPLE_COUNT(32)

    采样数量通常使用4或8,可以兼顾性能和内存消耗。如果你不使用多重采样,可以将采样数量设为1,将质量级别设为0。所有符合Direct3D 11功能特性的设备都支持用于所有渲染目标格式的4X多重采样。

    注意:我们需要为交换链缓冲区和深度缓冲区各填充一个DXGI_SAMPLE_DESC结构体。当创建后台缓冲区和深度缓冲区时,必须使用相同的多重采样设置。

    8. 特征级别
    Direct3D 11提出了特征等级(feature levels,在代码中由枚举类型D3D_FEATURE_LEVEL表示)的概念,对应了定义了d3d11中定义了如下几个等级以代表不同的d3d版本:

    typedef enum D3D_FEATURE_LEVEL {
        D3D_FEATURE_LEVEL_9_1 = 0x9100,
        D3D_FEATURE_LEVEL_9_2 = 0x9200,
        D3D_FEATURE_LEVEL_9_3 = 0x9300,
        D3D_FEATURE_LEVEL_10_0 = 0xa000,
        D3D_FEATURE_LEVEL_10_1 = 0xa100,
        D3D_FEATURE_LEVEL_11_0 = 0xb000
    } D3D_FEATURE_LEVEL;

    特征等级定义了一系列支持不同d3d功能的相应的等级(每个特征等级支持的功能可参见SDK文档),用意即如果一个用户的硬件不支持某一特征等级,程序可以选择较低的等级。例如,为了支持更多的用户,应用程序可能需要支持Direct3D 11,10.1,9.3硬件。程序会从最新的硬件一直检查到最旧的,即首先检查是否支持Direct3D 11,第二检查Direct3D 10.1,然后是Direct3D 10,最后是Direct3D 9。要设置测试的顺序,可以使用下面的特征等级数组(数组内元素的顺序即特征等级测试的顺序):

    D3D_FEATURE_LEVEL featureLevels [4] =
    {
        D3D_FEATURE_LEVEL_11_0, // First check D3D 11 support
        D3D_FEATURE_LEVEL_10_1, // Second check D3D 10.1 support
        D3D_FEATURE_LEVEL_10_0, // Next,check D3D 10 support
        D3D_FEATURE_LEVEL_9_3 // Finally,check D3D 9.3 support
    } ;

    这个数组可以放置在Direct3D初始化方法(4.2.1节)中,方法会输出数组中第一个可被支持的特征等级。例如,如果Direct3D报告数组中第一个可被支持的特征等级是D3D_FEATURE_LEVEL_10_0,程序就会禁用Direct3D 11和Direct3D 10.1的特征,而使用Direct3D 10的绘制路径。

    展开全文
  • Direct3D简介

    千次阅读 2010-12-18 11:41:00
    Direct3D简介 Direct 3D是基于微软的通用对象模式COM(Common Object Mode)的3D图形API。可绕过图形显示接口(GDI)直接进行支持该API的各种硬件的底层操作,大大提高了图形处理速度。 D3D在系统中的位置 D3D ...

    Direct3D简介

     

    Direct 3D是基于微软的通用对象模式COMCommon Object Mode)的3D图形API。可绕过图形显示接口(GDI)直接进行支持该API的各种硬件的底层操作,大大提高了图形处理速度。

     

    D3D在系统中的位置

     

     

    D3D Device分为 硬件抽象层设备(HAL Device)和参考设备(REF Device)。前者支持硬件加速的光栅化和软件、硬件顶点处理,与硬件相关,用于最后的发布版本。后者以软件模拟方式完成三维处理,速度慢,但是与硬件无关,可用于效果的测试。


     

     

     

     

    Direct3D 9图形管道

     

     

     

    Vertex Data未经转换的模型的顶点数据,存放在顶点内存缓冲里。

     

     

    Primitive Data基本几何数据(包括点、线、三角形以及多边形),它们存放在索引缓冲区中,被顶点数据所引用。

     

     

    Tessellation:一种细化模型技术。实际上Tessellation就是把一些粗大无序的几何模型图形分成很多更小的图形,从而实现更细致的几何模型表现。可以将低细节模型转换为高细节效果。当然用高细节建模也可以达到同样的效果;不过执行速度只有Tessellation的几分之一。而且建模复杂量还高出几百倍。

     

    效果对比:

     

            低细节模型                                        使用Tessellation之后的高细节效果

     

     

    性能对比:

     

    由图像效果和执行效率上来看,Tessellation是一个提高画质的至关重要的技术。

     

     

    Vertex Processing顶点处理。对顶点缓冲里的数据进行一些转换。

     

     

    Geometry Processing几何处理。对经过转换了的顶点进行剪切、背面挑选、评估属性以及光栅化处理。

     

     

    Textured Surface给表面附加纹理。为D3D的表面添加纹理坐标。

     

     

    Texture Sampler纹理取样。对输入的纹理值进行细节级别的过滤。

     

     

    Pixel Processing像素处理。像素渲染器(Pixel shader)使用几何数据来修改输入的顶点和纹理数据,从而得到像素颜色值。

     

     

    Pixel Rendering像素渲染。最后的渲染过程要经过 透明度(alpha)混合, 深度(depth)测试, 以及模板(stencil)测试 之后得到了最终的像素值,并将其显示到显示器上。

     

     

     

    展开全文
  • Direct3D-四大变换

    千次阅读 2014-11-01 14:33:27
    从本篇文章开始我们就来开始来学习固定渲染流水线这套渲染体系。 其实固定渲染流水线和之后我们要学习的可编程渲染流水线体系有很多异曲同工之妙,所以先学习固定功能渲染流水线体系,再学可编程渲染流水线体系,...

    从本篇文章开始我们就来开始来学习固定渲染流水线这套渲染体系。

    其实固定渲染流水线和之后我们要学习的可编程渲染流水线体系有很多异曲同工之妙,所以先学习固定功能渲染流水线体系,再学可编程渲染流水线体系,就可以循序渐进,步步为营地掌握好DirectX。

     

    空间中的物体需要使用三维坐标来描述,而我们的显示器是显然是二维的,所以在屏幕上渲染一个三维场景时,首先需要将物体描述空间物体的三维坐标变换为二维坐标(也就是世界坐标到屏幕坐标),这在Direct3D中称为顶点坐标变换。顶点坐标变换通常通过矩阵来完成。我们之前的几个demo中,演示的是如何显示一个二维的平面图形,它的顶点是以屏幕坐标系的二维值表示的,也是经过顶点坐标变换之后的顶点坐标数据,可以把顶点坐标变换想象成摄像的过程,三维世界的景物通过摄像机拍摄显示在二维的相片之上,有所不同的是把相片换成了屏幕。

    文章开头,我们先来看一下固定功能渲染流水线这套渲染体系的核心思想。

     

     

    一、固定功能渲染流水线概述

     

    在固定功能渲染流水线这套体系中,大体分为两个阶段,第一阶段我们将它称为坐标变换和光照处理阶段(Transforming &Lighting,简称T&L阶段)。在这个阶段中,每个对象的顶点从一个抽象的、浮点坐标变换到基于像素的屏幕空间当中。这里需要注意的是,坐标变换不仅包含物体顶点位置,它还可能包括顶点的法线、纹理坐标等等。并根据场景中光源和物体表面的材质对物体顶点应用不同类型的光照效果。还有其他一些比较重要的任务,比如视口的设置和裁剪也是在第一阶段进行的。讲完第一阶段,我们再来看看第二阶段,第二阶段称为光栅化处理阶段。顶点在经过第一阶段也就是变换与光照阶段的“洗礼”之后,已经略有雏形,在第二阶段,Direct3D将这些已经完成变换和光照阶段的顶点组织为以点、线、面为基础的图元,应用纹理贴图和物体顶点的颜色属性,并根据相关渲染状态的设置(比如着色模式等)

    ,决定每个像素最终的颜色值,并且在屏幕上显示出来。为了大家更宏观和更深入的理解,浅墨依然是配了一幅图。通过这幅图,大家可以对固定功能渲染流水线的结构脉络做到一目了然。

     

    需要注意的是,渲染流水线中的步骤并不一定都要有的,根据实际情况可以省略一些。比如之前我们给出的几个demo,都是省略了变换和光照阶段,直接将顶点作为屏幕坐标输出显示。因为我们在定义顶点的属性的时候,给我们的顶点定的“标签”是D3DFVF_XYZRHW,表示包含经过坐标变换的顶点坐标值,这样Direct3D就知道这些顶点坐标不需要再经过顶点坐标变换了,他们的坐标值就是最终显示屏幕上的坐标值了。

     

    目前我们重点介绍坐标变换和光照处理阶段(Transforming &Lighting,简称T&L阶段),也就是T&L阶段。在这个过程中,未经过变换和光照的顶点从一端进入,在流水线内部这些顶点将完成几个连续的操作,这几个操作按顺序分别为世界变换,取景变换,光照处理,投影变换以及视口变换。经过这些处理之后的顶点从另一端出来,表示已经完成坐标变换和光照处理了。我们的应用程序是通过指定几个矩阵、视口以及所使用的光线来建立T&L流水线的,然后应用程序将顶点送入流水线,并对这些顶点在流水线中进行坐标变换、照明以及裁剪,将其投影到屏幕空间当中,并根据视口的规定对其进行缩放。顶点在T&L流水线中进过“涅槃”之后,就可以去到第二阶段——光栅化处理阶段去完成新的试炼了。

    关于变换和光照渲染流水线阶段,也就是T&L阶段,浅墨也配了一幅图,方便大家的记忆与理解:

     

     

     

     

     

     

    二、携手迈向三维世界:四大变换显身手

     

     

    首先来讲讲周边的概念。

    在Direct3D中,如果我们未进行任何空间坐标变换而来绘制图形的话,图形将始终处于应用程序窗口的中心位置,在默认情况下这个位置就会成为世界坐标系的原点(0,0,0)。另外,我们也不能改变观察图形的视角方向。默认情况下的观察方向是世界坐标系的z轴正方向,也就是垂直程序窗口并向里观察的方向。之前我们的几个demo都是默认的z轴正方向为观察方向的。

    而为了迈向三维世界,为了能够随心所欲地在三维世界中绘制和观察游戏画面,就是四大变换的show time了。

    所谓四大变换,其实是浅墨自己在学习Direct3D的时候自己归纳的一个东西- -,也就是世界变换,取景变换,投影变换和视口变换的总称。

     

    下面我们先来大致概括一下这四大变换的用途。

     

    1.     为了能在世界空间中的指定位置来绘制图形,就需要在绘制图形前进行四大变换之一的世界变换运算。

    2.     为了以不同的视角观察图形,就需要用到四大变换之二的取景变换运算。

    3.     为了将相对较远的图形投影到同一个平面上并体现出“近大远小”的真实视觉效果,就需要用到四大变换之三的投影变换。

    4.     为了控制显示图形的窗口的大小,比例以及深度等等信息,就要用到四大变换之四的视口变换。

     

     

     

    下面我们就开始分别展开讲解这四大变换的细节知识。在讲解之前,有必要先跟大家说明一下。这里的四大变换的前三都是以矩阵作为载体的。这四大变换相关知识中或多或少会涉及到矩阵、向量,平面和射线等相关的数学知识,这些数学知识浅墨不准备专门花篇幅讲,因为讲起来都是概念,漫无目的,而且篇幅很长或许会花费几次更新的时间,况且写出来的东西肯定没新意而且没劲,所以没有写出来的必要。如果去学习Direct3D这款API的话,大可不必去纠结那么多数学概念。对矩阵和向量稍微有些了解就足够了,余下的高中数学(高中数学中没有矩阵相关的知识,所以矩阵相关概念需要另外学习)完全可以对付。如果实际过程中有不理解的地方再有的放矢地去查书和资料,这样学习起来会轻松很多,学习效率也会高得多。关于这方面数学知识的查阅资料,可以参看Direct3D 9龙书《Direct3D 9.0 3D游戏开发编程基础》的第一部分,Frank Luna已经为我们讲解得非常到位了。

    好了,下面就开始依次讲解吧。

     

     

    Ⅰ.四大变换之一:世界变换

     

    根据物体模型的大小、方向以及其他模型之间的相对关系,世界变换将物体模型从自身的局部坐标系中转换到时间坐标系中,并将所有的物体模型组织为一个场景,如下图:

     

     

     

     

    就像这个世界上有很多不同的个体,我们每个个体都有自己独特的局部坐标系,而我们每个个体仅仅是这个世界中的一份子而已。就像那句英文名言的前半句一样,to the world,you maybe one person。

     

    世界变换包括平移、旋转和缩放变换,我们可以通过D3DX库中D3DXMatrixTranslation、D3DXMatrixRotation*和D3DXMatrixSaling函数来进行变换,并得到一个世界变换矩阵。

    其中D3DXMatrixTranslation用于矩阵的平移操作,D3DXMatrixRotation*用于矩阵的旋转操作,D3DXMatrixSaling用于矩阵的缩放操作(稍后会分别介绍)。这三种变换是矩阵的最常用的变换。

     

    调用这些函数将我们矩阵调整好之后,接着我们就调用IDirect3DDevice9接口的SetTransform方法来运用世界变换矩阵,表示认定某某矩阵就是我们的世界变换矩阵了。

    我们可以在DirectX SDK中查到SetTransform方法的原型如下:

     

    1. HRESULT SetTransform(  
    2.   [in]  D3DTRANSFORMSTATETYPE State,  
    3.   [in]  const D3DMATRIX *pMatrix  
    4. );<span style="font-family:Microsoft YaHei;font-size:14px;"> </span>  

     ■   第一个参数,D3DTRANSFORMSTATETYPE类型的State,明显的可以看到,它是一个D3DTRANSFORMSTATETYPE枚举类型,用于表示变换的类型,是四大变换前三者之中的哪一种。可以取值为D3DTS_WORLD,表示世界矩阵,或者是D3DTRANSFORMSTATETYPE枚举中的一种,这个D3DTRANSFORMSTATETYPE枚举类型定义如下: 

     

    1. typedef enum D3DTRANSFORMSTATETYPE {  
    2.   D3DTS_VIEW          = 2,  
    3.   D3DTS_PROJECTION    = 3,  
    4.   D3DTS_TEXTURE0      = 16,  
    5.   D3DTS_TEXTURE1      = 17,  
    6.   D3DTS_TEXTURE2      = 18,  
    7.   D3DTS_TEXTURE3      = 19,  
    8.   D3DTS_TEXTURE4      = 20,  
    9.   D3DTS_TEXTURE5      = 21,  
    10.   D3DTS_TEXTURE6      = 22,  
    11.   D3DTS_TEXTURE7      = 23,  
    12.   D3DTS_FORCE_DWORD   = 0x7fffffff   
    13. } D3DTRANSFORMSTATETYPE, *LPD3DTRANSFORMSTATETYPE;  

     其中D3DTS_VIEW表示取景变换,D3DTS_PROJECTION表示投影变换。而D3DTS_TEXTURE0~7显然表示的就是某层纹理(0到7层)对应的变换矩阵了 。

    第二个参数,const D3DMATRIX类型的*pMatrix,是一个实实在在有内容的矩阵,显然就是我们想要设置为和第一个参数中指定的类型相挂钩的候选人矩阵了。

     

     

     

     

    接着我们来分别介绍经常会用到的矩阵的一些基本变换方法:

     

    1.矩阵的平移

     

    首先介绍D3DXMatrixTranslation方法,D3D中的平移函数。

    在Direct3D里,平移矩阵应是使用最多的矩阵。这是因为每个物体的相对位置,都是通过平移矩阵来创造出来的。物体的整体移动,说白了就是坐标点的移动。比如从点(x,y,z)移动到新的位置(x', y', z')。在Direct3D就为我们提供了D3DXMatrixTranslation方法用于矩阵的平移。我们先来看一下这个函数的原型:

     

    1. D3DXMATRIX * D3DXMatrixTranslation(  
    2.   __inout  D3DXMATRIX *pOut,    
    3.   __in     FLOAT x,  
    4.   __in     FLOAT y,  
    5.   __in     FLOAT z  
    6. );  


    这个函数其实就是在创造一个有相对于原点(0,0,0)有偏移量的矩阵出来。我们先来看一下各个参数的意义。

    █ 第一个参数,D3DXMATRIX类型的*pOut,从类型上来看我们就知道他是一个D3DXMATRIX类型的4 X 4的矩阵,我们调用这个D3DXMatrixTranslation方法,其实就是在为这个矩阵赋值,让这个矩阵相对于原点有一个偏移量,也就是我们需要平移的距离。

    而具体的平移操作其实并不是由这个函数完成的。,这个函数其实是在创建一个平移矩阵。

    █ 第二个参数,FLOAT类型的x,显然是X轴的平移量。

    █ 第三个参数,FLOAT类型的y,显然是Y轴的平移量。

    █ 第四个参数,FLOAT类型的z,显然是Z轴的平移量。

     

    比如要沿着Z轴的正方向平移10个单位,就需要按下面来设置平移矩阵:

     

    1. D3DXMATRIX mTrans;  
    2. D3DXMatrixTranslation(&mTrans,0,0,10);  


     

    然后我们把需要进行平移操作的矩阵,乘以这个创建好mTrans矩阵,就完成了平移操作。比如说我们有一个mMtrix矩阵,我们需要这个mMtrix矩阵沿Z轴正方向平移10个单位,就让mTrans和mMtrix相乘就可以了,他们相乘的结果就是mMtrix矩阵向上平移10个单位后的矩阵。其中相乘操作用 D3DXMatrixMultiply来完成。

     

    1. D3DXMATRIX * D3DXMatrixMultiply(  
    2.   __inout  D3DXMATRIX *pOut,  
    3.   __in     const D3DXMATRIX *pM1,  
    4.   __in     const D3DXMATRIX *pM2  
    5. );  

     

    第一个参数,为输出的结果。第二和第三个参数为参加乘法的两个矩阵,且pM1在左,pM2在右,因为矩阵相乘涉及到相乘顺序的问题。这个函数的作用可以用一个式子来表示,也就是pM1 * pM2=pOut。

     

    所以,整体来看,要把mMtrix矩阵向Z轴正方向平移10个单位,就是如下的代码:

     

    1. D3DXMATRIX mTrans;  
    2. D3DXMatrixTranslation(&mTrans,0,0,10);  
    3. D3DXMatrixMultiply(&mMtrix,&mMtrix,&&mTrans);  


    再举一个例子,如果我们仅仅是将一个物体沿X轴正方向平移5个单位,Y轴负方向平移3个单位就可以了,也就是只做了平移操作,就可以直接把我们创建的这个中间矩阵作为世界矩阵使用,也就是如下代码:

    1. D3DXMATRIX mTrans;  
    2. D3DXMatrixTranslation(&mTrans,5,-3,0);  
    3. g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);  


     

    2.矩阵的旋转

     

    旋转和平移类似,也是先用一个函数创建好用于旋转的一个中间矩阵,然后让我们需要旋转的那个矩阵右乘这个中间矩阵就好了。关于这个旋转中间矩阵的创建,用的就是地d9

    dx9.lib库中的D3DXMatrixRotationX, D3DXMatrixRotationY以及D3DXMatrixRotationZ。这三个函数非常相似,我们就打包起来介绍得了,这三个函数的原型声明如下:

     

    1. D3DXMATRIX * D3DXMatrixRotationX(  
    2.   __inout  D3DXMATRIX *pOut,  
    3.   __in     FLOAT Angle  
    4. );  
    5. D3DXMATRIX * D3DXMatrixRotationY(  
    6.   __inout  D3DXMATRIX *pOut,  
    7.   __in     FLOAT Angle  
    8. );  
    9. D3DXMATRIX * D3DXMatrixRotationZ(  
    10.   __inout  D3DXMATRIX *pOut,  
    11.   __in     FLOAT Angle  
    12. );  


    █ 第一个参数,D3DXMATRIX类型的*pOut,依然是作为输出结果的旋转矩阵。

    █ 第二个参数,FLOAT 类型的angle表示要旋转的弧度值。

     

    下面继续来举例子,同样,如果我们只对某个物体在世界坐标系中进行旋转操作的话,也可以把D3DXMatrixRotationX系列函数创造出来的中间矩阵作为我们的世界矩阵。

    若要将一个对象沿Y轴旋转90度,也就是如下代码:

     

    1. D3DXMATRIX mTrans;  
    2. float fAngle=90*(2.0f*D3DX_PI)/360.0f;  
    3. D3DXMatrixRotationY(&mTrans, fAngle);  
    4. g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);  


     

     

    3.矩阵的缩放

     

    与旋转和平移类似,矩阵缩放也是先用一个函数创建好用于缩放的一个中间矩阵,然后让我们需要缩放那个矩阵右乘这个中间矩阵就好了。关于这个缩放中间矩阵的创建,为D3DXMatrixScaling函数,这个函数的原型如下:

     

    1. D3DXMATRIX * D3DXMatrixScaling(  
    2.   __inout  D3DXMATRIX *pOut,  
    3.   __in     FLOAT sx,  
    4.   __in     FLOAT sy,  
    5.   __in     FLOAT sz  
    6. );  


    █ 第一个参数,D3DXMATRIX类型的*pOut,依然是作为输出结果的缩放矩阵。

    █ 第二个参数到第四个参数,显然就是浮点型的X,Y,Z轴上的缩放比例了。

    比如,要将一个物体在Z轴上放大5倍,代码就是这样写:

     

    1. D3DXMATRIX mTrans;  
    2. D3DXMatrixScaling(&mTrans,1.0f,1.0f,5.0f);  
    3. g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);  


     

    这一小节的最后,最后我们来一个综合的调用实例,比如要将一个物体在X轴上放大3倍,然后又绕Y轴旋转120度,最后又沿Z轴平移正方向10个单位,实现代码如下:

     

    1. D3DXMATRIX matWorld;  
    2. D3DXMATRIX matTranslate,matRotation,matScale;  
    3. D3DXMatrixScaling( & matScale,3.0f,1.0f,1.0f);  
    4. float fAngle=120*(2.0f*D3DX_PI)/360.0f;  
    5. D3DXMatrixRotationY(&matRotation, fAngle);  
    6. D3DXMatrixMultiply(&matWorld,& matScale,& matRotation);  
    7. D3DXMatrixTranslation(&matTranslate,0.0f,0.0f,10.0f);  
    8. D3DXMatrixMultiply (&matWorld ,&matWorld,& matTranslate);  
    9. g_pd3dDevice->SetTransform(D3DTS_WORLD,&mTrans);  


     

     

    另外提一个单位化矩阵的函数,也就是D3DXMatrixIdentity函数。其用法非常简单,唯一的参数就是单位化之后的输出矩阵,也就是这样写:

    D3DXMATRIX matWorld;

    D3DXMatrixIdentity(&matWorld);                  // 单位化世界矩阵

     

     

    另外再提一点,关于矩阵的乘法,因为Direct3D对D3DXMATRIX矩阵类型进行了扩展,对矩阵乘法进行了重载,所以矩阵的乘法运算可以不用拘泥于上面讲到的D3DXMatrixMultiply()来实现了,可以简单地使用乘法运算符“*”就可以了,即多个矩阵的乘积可以这样写:

     

    matAnswer=mat1*mat2*mat3*mat4……*matN

     

     

     

    Ⅱ.四大变换之二:取景变换

     

    取景变换,人如其名,就是用来取景的,也就是设置Direct3D中的虚拟摄像机的位置和观察点。对于处于不同位置的虚拟摄像机和观察点,其观察物体模型的视角方向也有所差异,因此实际看到的物体模型的实际形状也有所不同。正所谓“横看成岭侧成峰”,就像这幅图所传达的观点一样:

     

    为了确定一个虚拟摄像机的位置和观察方向,需要指定虚拟摄像机在世界坐标系中的位置、观察点位置以及正方向。为了能够进行取景变换,首先需要通过D3DX库中的D3DXMatrixLookAtLH函数计算并得到一个取景变换矩阵(或观察矩阵),然后同样调用IDirect3DDevice接口的SetTransform方法应用取景变换。

     

    其中,D3DXMatrixLookAtLH函数的声明如下:

    1. D3DXMATRIX * D3DXMatrixLookAtLH(  
    2.   __inout  D3DXMATRIX *pOut,  
    3.   __in     const D3DXVECTOR3 *pEye,  
    4.   __in     const D3DXVECTOR3 *pAt,  
    5.   __in     const D3DXVECTOR3 *pUp  
    6. );  


    然后依然是函数参数的介绍:

    █   第一个参数,D3DXMATRIX类型的*pOut,最终生成的观察矩阵。

    █   第二个参数,const D3DXVECTOR3类型的*pEye,指定虚拟摄像机在世界坐标系中的位置。

    █  第三个参数,const D3DXVECTOR3类型的*pAt,为观察点在世界坐标系中的位置。

    █   第四个参数,const D3DXVECTOR3类型的*pUp,为摄像机的上向量,通常设为(0,1,0)就可以了。

     

    依然是一个使用的例子,代码如下:

     

    1. //建立并设置观察矩阵  
    2. D3DXVECTOR3 vEyePt( 0.0f, 8.0f,-10.0f ); //摄像机的位置  
    3. D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );//观察点的位置  
    4. D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );//向上的向量  
    5. D3DXMATRIX matView;//定义一个世界矩阵  
    6. D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );//计算出取景变换矩阵  
    7.        g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); //应用取景变换矩阵  


     

     

     

     

    Ⅲ.四大变换之三:投影变换

     

    经过上一步的取景变换之后,物体模型就位于观察坐标系中了,然而为了能够将三维场景显示在我们二维的显示平面上(因为我们的显示屏是二维的),还需要通过投影变换将三维物体投影到二维的平面上,这个过程我们就把它叫做透视投影。下面这幅图中以及显示得很清楚了:

     

     

    我们可以看到,摄像机的距离和角度唯一确定了落地窗的大小。

    就好比一个人站在的一间封闭房间里,房间唯一可以接收到光线的地方是一块高大的落地窗,而房间外面对着落地窗这边的,是一堵无限长无限宽,绵延到世界尽头的白色的墙。人眼透过落地窗看到房子外边三维的世界,最终投影在了那堵二维的墙上。

     

    显然,投影窗口是一个二维平面,用于描述三维物体模型经过透视投影后的二维图像,在Direct3D中投影窗口平面默认定义为z=1的平面。

    虚拟摄像机与投影窗口平面共同构成了一个对观察者可见的三维空间。在3D图形学中这部分空间被称作视截体(View Frustum),位于视截体内的物体模型被映射到二维投影投影平面上,而位于视截体外的物体模型或者其中一部分将不可见,这个过程我们称作裁剪(Clipping),(取屏幕内的景物,专注,屏幕外的一律无视)。

     

    投影变换负责将位于视截体内的物体模型映射到投影窗口中。D3DX库中的D3DXMatrixPerspectiveFovLH函数可以用来计算一个视截体,并根据该视截体的描述信息创建一个投影矩阵变换。这个D3DXMatrixPerspectiveFovLH方法可以在MSDN中查到如下函数原型;

     

    1. D3DXMATRIX * D3DXMatrixPerspectiveFovLH(  
    2.   __inout  D3DXMATRIX *pOut,  
    3.   __in     FLOAT fovy,  
    4.   __in     FLOAT Aspect,  
    5.   __in     FLOAT zn,  
    6.   __in     FLOAT zf  
    7. );  

     

    ■   第一个参数,D3DXMATRIX类型的*pOut,它为我们最终生成的投影变换矩阵。

    ■   第二个参数,FLOAT类型的fovy,用于指定以弧度为单位的虚拟摄像机在y轴上的成像角度,即视域角度(View of View),成像角度越大,映射到投影窗口中的图形就越小;反之,投影图像就越大。

    ■   第三个参数,FLOAT类型的Aspect,用于描述屏幕显示区的横纵比,他的值就为屏幕的宽度/高度。对应不同比例的显示屏幕,比如16/9,4/3等等,最终显示的投影图像可能会使图像被拉伸

    ■   第四个参数, FLOAT类型的zn,表示视截体中近裁剪面距我们摄像机的位置,即人眼到“室内落地窗”之间的距离

    ■   第五个参数,FLOAT类型的zf,表示视截体中远裁剪面距我们摄像机的位置,即人眼到“室外黑色墙壁”之间的距离

     

    依旧是一个调用实例:

     

    1. //建立并设置投影矩阵  
    2.        D3DXMATRIXA16 matProj;  
    3.        float aspect = (float)(800/600);  
    4.        D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, aspect, 1.0f, 100.0f );  
    5.        g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );  


     

    Ⅳ. 四大变换之四:视口变换

     

    终于讲到四大变换的最后一个了。其实这一步非常的简单,也就是填下填空题,然后设置一下就好了。并不像前三大变换一样要用到矩阵相关的知识。下面就一起来具体看看视口变换的相关概念吧。

    视口变换用于将投影窗口中的图形转换到显示屏幕的程序窗口中。视口是程序窗口中前的一个矩形区域,他可以是整个程序窗口,也可以是窗口的客户区,也可以是窗口中其他矩形区域,如图所示:

     

     

    在Direct3D中,视口是由D3DVIEWPROT9结构体来描述的,其中定义了视口的位置,宽度高度等信息,在DirectX SDK 中我们可以查到该结构体的声明如下:

     

    1. typedef struct D3DVIEWPORT9 {  
    2.   DWORD X;          //表示视口相对于窗口的X坐标  
    3.   DWORD Y;     //视口相对对窗口的Y坐标  
    4.   DWORD Width;//视口的宽度  
    5.   DWORD Height;//视口的高度  
    6.   float MinZ;//视口在深度缓存中的最小深度值  
    7.   float MaxZ;  //视口在深度缓存中的最大深度值  
    8. } D3DVIEWPORT9, *LPD3DVIEWPORT9;  


     

    讲解完概念下面依旧是调用的实例:

    方法一:

    先做填空题,做完之后调用IDirect3D9Device9接口的SetViewPort方法设置当前窗口中的视口,然后Direct3D将自动完成视口变换。

    1. D3DVIEWPORT9 vp={0,0,800,600,0,1};  
    2. g_pD3dDevice->SetViewport(&vp);  

     

    方法二:

    结构体的内容别开来赋值:

     

    1. D3DVIEWPORT9 vp;  
    2.          vp.X      = 0;  
    3.          vp.Y      = 0;  
    4.          vp.Width  = 800;  
    5.          vp.Height = 600;  
    6.          vp.MinZ   = 0.0f;  
    7.          vp.MaxZ   = 1.0f;  
    8.          g_pd3dDevice->SetViewport(&vp);  

    其实也就是填结构体填空题的两种方式而已。

     

     

    Ⅴ.总结

     

     

    四大变换分为:世界变换,取景变换,投影变换,视口变换。

    1.为了能在世界空间中的指定位置来绘制图形,就需要在绘制图形前进行四大变换之一的世界变换运算。

    2.为了以不同的视角观察图形,就需要用到四大变换之二的取景变换运算。

    3.为了将相对较远的图形投影到同一个平面上并体现出“近大远小”的真实视觉效果,就需要用到四大变换之三的投影变换。

    4.为了控制显示图形的窗口的大小,比例以及深度等等信息,就要用到四大变换之四的视口变换。

     

     

     

     

    三,详细注释的源代码欣赏

     

    这里依旧是通过一个小程序,来把本篇文章所学的知识融会贯通。这篇文章里我们讲解了绘制3D图形时用到的四大变换,下面就是一个利用四大变换来绘制一个转动的颜色随机的彩色立方体的demo。我们将四大变换封装在了一个全局的Matrix_Set()函数中了,并在渲染五步曲之三中进行了Matrix_Set()函数的调用。

    在放出完整的源代码之前,让我们首先我们来看一下本篇文章介绍的核心知识,也就是实现了四大变换封装的Matrix_Set()函数的具体实现方法:

     

    1. //*****************************************************************************************  
    2. // Name:Matrix_Set()  
    3. // Desc: 设置世界矩阵  
    4. // Point:【Direct3D四大变换】  
    5. //      1.【四大变换之一】:世界变换矩阵的设置  
    6. //      2.【四大变换之二】:取景变换矩阵的设置  
    7. //      3.【四大变换之三】:投影变换矩阵的设置  
    8. //      4.【四大变换之四】:视口变换的设置  
    9. //*****************************************************************************************  
    10. VOID Matrix_Set()  
    11. {  
    12.     //--------------------------------------------------------------------------------------  
    13.     //【四大变换之一】:世界变换矩阵的设置  
    14.     //--------------------------------------------------------------------------------------  
    15.     D3DXMATRIX matWorld, Rx, Ry, Rz;  
    16.     D3DXMatrixIdentity(&matWorld);                  // 单位化世界矩阵  
    17.     D3DXMatrixRotationX(&Rx, D3DX_PI *(::timeGetTime() / 1000.0f));    // 绕X轴旋转  
    18.     D3DXMatrixRotationY(&Ry, D3DX_PI *( ::timeGetTime() / 1000.0f/2));    // 绕Y轴旋转  
    19.     D3DXMatrixRotationZ(&Rz, D3DX_PI *( ::timeGetTime() / 1000.0f/3));   // 绕Z轴旋转  
    20.     matWorld = Rx * Ry * Rz * matWorld;             // 得到最终的组合矩阵  
    21.     g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);  //设置世界变换矩阵  
    22.   
    23.     //--------------------------------------------------------------------------------------  
    24.     //【四大变换之二】:取景变换矩阵的设置  
    25.     //--------------------------------------------------------------------------------------  
    26.     D3DXMATRIX matView; //定义一个矩阵  
    27.     D3DXVECTOR3 vEye(0.0f, 0.0f, -200.0f);  //摄像机的位置  
    28.     D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //观察点的位置  
    29.     D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);//向上的向量  
    30.     D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //计算出取景变换矩阵  
    31.     g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //应用取景变换矩阵  
    32.   
    33.     //--------------------------------------------------------------------------------------  
    34.     //【四大变换之三】:投影变换矩阵的设置  
    35.     //--------------------------------------------------------------------------------------  
    36.     D3DXMATRIX matProj; //定义一个矩阵  
    37.     D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 1000.0f); //计算投影变换矩阵  
    38.     g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);  //设置投影变换矩阵  
    39.   
    40.     //--------------------------------------------------------------------------------------  
    41.     //【四大变换之四】:视口变换的设置  
    42.     //--------------------------------------------------------------------------------------  
    43.     D3DVIEWPORT9 vp; //实例化一个D3DVIEWPORT9结构体,然后做填空题给各个参数赋值就可以了  
    44.     vp.X      = 0;      //表示视口相对于窗口的X坐标  
    45.     vp.Y      = 0;      //视口相对对窗口的Y坐标  
    46.     vp.Width  = SCREEN_WIDTH;   //视口的宽度  
    47.     vp.Height = SCREEN_HEIGHT; //视口的高度  
    48.     vp.MinZ   = 0.0f; //视口在深度缓存中的最小深度值  
    49.     vp.MaxZ   = 1.0f;   //视口在深度缓存中的最大深度值  
    50.     g_pd3dDevice->SetViewport(&vp); //视口的设置  
    51.   
    52. }  


     

    其中timeGetTime方法以毫秒为时间单位返回从Windows系统开机时起所经过的时间。这样就可以通过(::timeGetTime() / 1000.0f)这个式子来构造一个从0到1的连续的时间周期.而D3DX_PI 为Direct3D中定义的一个宏,原型为:

    1. #define D3DX_PI    ((FLOAT)  3.141592654f)  


     

     

    接着我们就贴出这个小程序全部的详细注释的源代码,按键盘上的1键和2键可以在线框填充模式和实体填充模式之间切换:

      

    1. //*****************************************************************************************  
    2. //  
    3. //【Visual C++】游戏开发笔记系列配套源码 三十八 浅墨DirectX提高班之六 携手迈向三维世界:四大变换展身手  
    4. //       VS2010版  
    5. // 2012年 12月23日  Create by 浅墨   
    6. //图标素材: Dota2 白虎第1技能 流星  
    7. //源码配套博文链接:    
    8. //更多内容请访问我的博客: http://blog.csdn.net/zhmxy555   
    9. //此刻心情:耿耿于怀着过去和忐忑不安着未来的人,也常常挥霍无度着现在。希望我们都做那个把握好现在的人。  
    10. //  
    11. //*****************************************************************************************   
    12.   
    13.   
    14.   
    15.   
    16. //*****************************************************************************************  
    17. // Desc: 头文件定义部分    
    18. //*****************************************************************************************                                                                                         
    19. #include <d3d9.h>  
    20. #include <d3dx9.h>  
    21. #include <tchar.h>  
    22. #include   <time.h>   
    23.   
    24.   
    25.   
    26.   
    27. //*****************************************************************************************  
    28. // Desc: 库文件定义部分    
    29. //*****************************************************************************************   
    30. #pragma comment(lib,"d3d9.lib")  
    31. #pragma comment(lib,"d3dx9.lib")  
    32. #pragma comment(lib, "winmm.lib ")  
    33.   
    34.   
    35. //*****************************************************************************************  
    36. // Desc: 宏定义部分     
    37. //*****************************************************************************************  
    38. #define SCREEN_WIDTH    800                     //为窗口宽度定义的宏,以方便在此处修改窗口宽度  
    39. #define SCREEN_HEIGHT   600                         //为窗口高度定义的宏,以方便在此处修改窗口高度  
    40. #define WINDOW_TITLE    _T("【Visual C++游戏开发笔记】博文配套demo之三十八 浅墨DirectX提高班之六 携手迈向三维世界:四大变换展身手") //为窗口标题定义的宏  
    41. #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }      //自定义一个SAFE_RELEASE()宏,便于资源的释放  
    42.   
    43.   
    44.   
    45. //*****************************************************************************************  
    46. // 【顶点缓存、索引缓存绘图四步曲之一】:设计顶点格式  
    47. //*****************************************************************************************  
    48. struct CUSTOMVERTEX  
    49. {  
    50.     FLOAT x, y, z;  
    51.     DWORD color;  
    52. };  
    53. #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE)  //FVF灵活顶点格式  
    54.   
    55.   
    56. //*****************************************************************************************  
    57. // Desc: 全局变量声明部分    
    58. //*****************************************************************************************  
    59. LPDIRECT3DDEVICE9       g_pd3dDevice = NULL; //Direct3D设备对象  
    60. ID3DXFont*              g_pFont=NULL;    //字体COM接口  
    61. float                   g_FPS = 0.0f;       //一个浮点型的变量,代表帧速率  
    62. wchar_t                 g_strFPS[50];    //包含帧速率的字符数组  
    63. LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer = NULL;    //顶点缓存对象  
    64. LPDIRECT3DINDEXBUFFER9  g_pIndexBuffer  = NULL;    // 索引缓存对象  
    65.   
    66. //*****************************************************************************************  
    67. // Desc: 全局函数声明部分   
    68. //*****************************************************************************************   
    69. LRESULT CALLBACK    WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );  
    70. HRESULT             Direct3D_Init(HWND hwnd);  
    71. HRESULT             Objects_Init();  
    72. void                Direct3D_Render( HWND hwnd);  
    73. void                Direct3D_CleanUp( );  
    74. float               Get_FPS();  
    75. VOID                Matrix_Set();  
    76.   
    77.   
    78. //*****************************************************************************************  
    79. // Name: WinMain( )  
    80. // Desc: Windows应用程序入口函数  
    81. //*****************************************************************************************  
    82. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nShowCmd)  
    83. {  
    84.   
    85.     //开始设计一个完整的窗口类  
    86.     WNDCLASSEX wndClass = { 0 };                //用WINDCLASSEX定义了一个窗口类,即用wndClass实例化了WINDCLASSEX,用于之后窗口的各项初始化      
    87.     wndClass.cbSize = sizeof( WNDCLASSEX ) ;    //设置结构体的字节数大小  
    88.     wndClass.style = CS_HREDRAW | CS_VREDRAW;   //设置窗口的样式  
    89.     wndClass.lpfnWndProc = WndProc;             //设置指向窗口过程函数的指针  
    90.     wndClass.cbClsExtra     = 0;  
    91.     wndClass.cbWndExtra     = 0;  
    92.     wndClass.hInstance = hInstance;             //指定包含窗口过程的程序的实例句柄。  
    93.     wndClass.hIcon=(HICON)::LoadImage(NULL,_T("icon.ico"),IMAGE_ICON,0,0,LR_DEFAULTSIZE|LR_LOADFROMFILE); //从全局的::LoadImage函数从本地加载自定义ico图标  
    94.     wndClass.hCursor = LoadCursor( NULL, IDC_ARROW );    //指定窗口类的光标句柄。  
    95.     wndClass.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH);  //为hbrBackground成员指定一个灰色画刷句柄  
    96.     wndClass.lpszMenuName = NULL;                       //用一个以空终止的字符串,指定菜单资源的名字。  
    97.     wndClass.lpszClassName = _T("ForTheDreamOfGameDevelop");        //用一个以空终止的字符串,指定窗口类的名字。  
    98.   
    99.     if( !RegisterClassEx( &wndClass ) )             //设计完窗口后,需要对窗口类进行注册,这样才能创建该类型的窗口  
    100.         return -1;        
    101.   
    102.     HWND hwnd = CreateWindow( _T("ForTheDreamOfGameDevelop"),WINDOW_TITLE,          //喜闻乐见的创建窗口函数CreateWindow  
    103.         WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, SCREEN_WIDTH,  
    104.         SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );  
    105.   
    106.   
    107.     //Direct3D资源的初始化,调用失败用messagebox予以显示  
    108.     if (!(S_OK==Direct3D_Init (hwnd)))  
    109.     {  
    110.         MessageBox(hwnd, _T("Direct3D初始化失败~!"), _T("浅墨的消息窗口"), 0); //使用MessageBox函数,创建一个消息窗口   
    111.     }  
    112.   
    113.   
    114.   
    115.     MoveWindow(hwnd,200,50,SCREEN_WIDTH,SCREEN_HEIGHT,true);   //调整窗口显示时的位置,窗口左上角位于屏幕坐标(200,50)处  
    116.     ShowWindow( hwnd, nShowCmd );    //调用Win32函数ShowWindow来显示窗口  
    117.     UpdateWindow(hwnd);  //对窗口进行更新,就像我们买了新房子要装修一样  
    118.   
    119.       
    120.   
    121.       
    122.   
    123.     //消息循环过程  
    124.     MSG msg = { 0 };  //初始化msg  
    125.     while( msg.message != WM_QUIT )         //使用while循环  
    126.     {  
    127.         if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )   //查看应用程序消息队列,有消息时将队列中的消息派发出去。  
    128.         {  
    129.             TranslateMessage( &msg );       //将虚拟键消息转换为字符消息  
    130.             DispatchMessage( &msg );        //该函数分发一个消息给窗口程序。  
    131.         }  
    132.         else  
    133.         {  
    134.             Direct3D_Render(hwnd);          //调用渲染函数,进行画面的渲染  
    135.         }  
    136.     }  
    137.   
    138.     UnregisterClass(_T("ForTheDreamOfGameDevelop"), wndClass.hInstance);  
    139.     return 0;    
    140. }  
    141.   
    142.   
    143.   
    144. //*****************************************************************************************  
    145. // Name: WndProc()  
    146. // Desc: 对窗口消息进行处理  
    147. //*****************************************************************************************  
    148. LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )   //窗口过程函数WndProc  
    149. {  
    150.     switch( message )               //switch语句开始  
    151.     {  
    152.     case WM_PAINT:                   // 客户区重绘消息  
    153.         Direct3D_Render(hwnd);          //调用Direct3D_Render函数,进行画面的绘制  
    154.         ValidateRect(hwnd, NULL);   // 更新客户区的显示  
    155.         break;                                  //跳出该switch语句  
    156.   
    157.     case WM_KEYDOWN:                // 键盘按下消息  
    158.         if (wParam == VK_ESCAPE)    // ESC键  
    159.             DestroyWindow(hwnd);    // 销毁窗口, 并发送一条WM_DESTROY消息  
    160.         break;  
    161.     case WM_DESTROY:                //窗口销毁消息  
    162.         Direct3D_CleanUp();     //调用Direct3D_CleanUp函数,清理COM接口对象  
    163.         PostQuitMessage( 0 );       //向系统表明有个线程有终止请求。用来响应WM_DESTROY消息  
    164.         break;                      //跳出该switch语句  
    165.   
    166.     default:                        //若上述case条件都不符合,则执行该default语句  
    167.         return DefWindowProc( hwnd, message, wParam, lParam );      //调用缺省的窗口过程来为应用程序没有处理的窗口消息提供缺省的处理。  
    168.     }  
    169.   
    170.     return 0;                   //正常退出  
    171. }  
    172.   
    173.   
    174. //*****************************************************************************************  
    175. // Name: Direct3D_Init( )  
    176. // Desc: 初始化Direct3D  
    177. // Point:【Direct3D初始化四步曲】  
    178. //      1.初始化四步曲之一,创建Direct3D接口对象  
    179. //      2.初始化四步曲之二,获取硬件设备信息  
    180. //      3.初始化四步曲之三,填充结构体  
    181. //      4.初始化四步曲之四,创建Direct3D设备接口  
    182. //*****************************************************************************************  
    183.   
    184. HRESULT Direct3D_Init(HWND hwnd)  
    185. {  
    186.   
    187.     //--------------------------------------------------------------------------------------  
    188.     // 【Direct3D初始化四步曲之一,创接口】:创建Direct3D接口对象, 以便用该Direct3D对象创建Direct3D设备对象  
    189.     //--------------------------------------------------------------------------------------  
    190.     LPDIRECT3D9  pD3D = NULL; //Direct3D接口对象的创建  
    191.     if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) ) //初始化Direct3D接口对象,并进行DirectX版本协商  
    192.             return E_FAIL;  
    193.   
    194.     //--------------------------------------------------------------------------------------  
    195.     // 【Direct3D初始化四步曲之二,取信息】:获取硬件设备信息  
    196.     //--------------------------------------------------------------------------------------  
    197.     D3DCAPS9 caps; int vp = 0;  
    198.     if( FAILED( pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps ) ) )  
    199.         {  
    200.             return E_FAIL;  
    201.         }  
    202.     if( caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )  
    203.         vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //支持硬件顶点运算,我们就采用硬件顶点运算,妥妥的  
    204.     else  
    205.         vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING; //不支持硬件顶点运算,无奈只好采用软件顶点运算  
    206.   
    207.     //--------------------------------------------------------------------------------------  
    208.     // 【Direct3D初始化四步曲之三,填内容】:填充D3DPRESENT_PARAMETERS结构体  
    209.     //--------------------------------------------------------------------------------------  
    210.     D3DPRESENT_PARAMETERS d3dpp;   
    211.     ZeroMemory(&d3dpp, sizeof(d3dpp));  
    212.     d3dpp.BackBufferWidth            = SCREEN_WIDTH;  
    213.     d3dpp.BackBufferHeight           = SCREEN_HEIGHT;  
    214.     d3dpp.BackBufferFormat           = D3DFMT_A8R8G8B8;  
    215.     d3dpp.BackBufferCount            = 2;  
    216.     d3dpp.MultiSampleType            = D3DMULTISAMPLE_NONE;  
    217.     d3dpp.MultiSampleQuality         = 0;  
    218.     d3dpp.SwapEffect                 = D3DSWAPEFFECT_DISCARD;   
    219.     d3dpp.hDeviceWindow              = hwnd;  
    220.     d3dpp.Windowed                   = true;  
    221.     d3dpp.EnableAutoDepthStencil     = true;   
    222.     d3dpp.AutoDepthStencilFormat     = D3DFMT_D24S8;  
    223.     d3dpp.Flags                      = 0;  
    224.     d3dpp.FullScreen_RefreshRateInHz = 0;  
    225.     d3dpp.PresentationInterval       = D3DPRESENT_INTERVAL_IMMEDIATE;  
    226.   
    227.     //--------------------------------------------------------------------------------------  
    228.     // 【Direct3D初始化四步曲之四,创设备】:创建Direct3D设备接口  
    229.     //--------------------------------------------------------------------------------------  
    230.     if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,   
    231.         hwnd, vp, &d3dpp, &g_pd3dDevice)))  
    232.         return E_FAIL;  
    233.   
    234.   
    235.     if(!(S_OK==Objects_Init())) return E_FAIL;  
    236.   
    237.   
    238.   
    239.     SAFE_RELEASE(pD3D) //LPDIRECT3D9接口对象的使命完成,我们将其释放掉  
    240.   
    241.     return S_OK;  
    242. }  
    243.   
    244.   
    245. HRESULT Objects_Init()  
    246. {  
    247.     //创建字体  
    248.     if(FAILED(D3DXCreateFont(g_pd3dDevice, 30, 0, 0, 1, FALSE, DEFAULT_CHARSET,   
    249.         OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, 0, _T("宋体"), &g_pFont)))  
    250.         return E_FAIL;  
    251.   
    252.   
    253.     srand((unsigned)time(NULL));      //初始化时间种子  
    254.   
    255.   
    256.     //--------------------------------------------------------------------------------------  
    257.     // 【顶点缓存、索引缓存绘图四步曲之二】:创建顶点缓存和索引缓存  
    258.     //--------------------------------------------------------------------------------------  
    259.         //创建顶点缓存  
    260.         if( FAILED( g_pd3dDevice->CreateVertexBuffer( 8*sizeof(CUSTOMVERTEX),  
    261.             0, D3DFVF_CUSTOMVERTEX,  
    262.             D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL ) ) )  
    263.         {  
    264.             return E_FAIL;  
    265.         }  
    266.         // 创建索引缓存  
    267.     if( FAILED(     g_pd3dDevice->CreateIndexBuffer(36* sizeof(WORD), 0,   
    268.         D3DFMT_INDEX16, D3DPOOL_DEFAULT, &g_pIndexBuffer, NULL)) )  
    269.         {  
    270.         return E_FAIL;  
    271.   
    272.         }  
    273.         //--------------------------------------------------------------------------------------  
    274.         // 【顶点缓存、索引缓存绘图四步曲之三】:访问顶点缓存和索引缓存  
    275.         //--------------------------------------------------------------------------------------  
    276.         //顶点数据的设置,  
    277.     //顶点数据  
    278.     CUSTOMVERTEX Vertices[] =  
    279.     {  
    280.         { -20.0f, 20.0f, -20.0f,  D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    281.         { -20.0f, 20.0f, 20.0f,  D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },   
    282.         { 20.0f, 20.0f, 20.0f,  D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    283.         { 20.0f, 20.0f, -20.0f,  D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    284.         { -20.0f, -20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    285.         { -20.0f, -20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },   
    286.         { 20.0f, -20.0f, 20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    287.         { 20.0f, -20.0f, -20.0f, D3DCOLOR_XRGB(rand() % 256, rand() % 256, rand() % 256) },  
    288.   
    289.     };  
    290.   
    291.         //填充顶点缓存  
    292.         VOID* pVertices;  
    293.         if( FAILED( g_pVertexBuffer->Lock( 0, sizeof(Vertices), (void**)&pVertices, 0 ) ) )  
    294.             return E_FAIL;  
    295.         memcpy( pVertices, Vertices, sizeof(Vertices) );  
    296.         g_pVertexBuffer->Unlock();  
    297.   
    298.   
    299.   
    300.         // 填充索引数据  
    301.         WORD *pIndices = NULL;  
    302.         g_pIndexBuffer->Lock(0, 0, (void**)&pIndices, 0);  
    303.         // 顶面  
    304.         pIndices[0] = 0, pIndices[1] = 1, pIndices[2] = 2;  
    305.         pIndices[3] = 0, pIndices[4] = 2, pIndices[5] = 3;  
    306.   
    307.         // 正面  
    308.         pIndices[6] = 0, pIndices[7]  = 3, pIndices[8]  = 7;  
    309.         pIndices[9] = 0, pIndices[10] = 7, pIndices[11] = 4;  
    310.   
    311.         // 左侧面  
    312.         pIndices[12] = 0, pIndices[13] = 4, pIndices[14] = 5;  
    313.         pIndices[15] = 0, pIndices[16] = 5, pIndices[17] = 1;  
    314.   
    315.         // 右侧面  
    316.         pIndices[18] = 2, pIndices[19] = 6, pIndices[20] = 7;  
    317.         pIndices[21] = 2, pIndices[22] = 7, pIndices[23] = 3;  
    318.   
    319.         // 背面  
    320.         pIndices[24] = 2, pIndices[25] = 5, pIndices[26] = 6;  
    321.         pIndices[27] = 2, pIndices[28] = 1, pIndices[29] = 5;  
    322.   
    323.         // 底面  
    324.         pIndices[30] = 4, pIndices[31] = 6, pIndices[32] = 5;  
    325.         pIndices[33] = 4, pIndices[34] = 7, pIndices[35] = 6;  
    326.         g_pIndexBuffer->Unlock();  
    327.   
    328.   
    329.     return S_OK;  
    330. }  
    331.   
    332.   
    333. //*****************************************************************************************  
    334. // Name:Matrix_Set()  
    335. // Desc: 设置世界矩阵  
    336. // Point:【Direct3D四大变换】  
    337. //      1.【四大变换之一】:世界变换矩阵的设置  
    338. //      2.【四大变换之二】:取景变换矩阵的设置  
    339. //      3.【四大变换之三】:投影变换矩阵的设置  
    340. //      4.【四大变换之四】:视口变换的设置  
    341. //*****************************************************************************************  
    342. VOID Matrix_Set()  
    343. {  
    344.     //--------------------------------------------------------------------------------------  
    345.     //【四大变换之一】:世界变换矩阵的设置  
    346.     //--------------------------------------------------------------------------------------  
    347.     D3DXMATRIX matWorld, Rx, Ry, Rz;  
    348.     D3DXMatrixIdentity(&matWorld);                  // 单位化世界矩阵  
    349.     D3DXMatrixRotationX(&Rx, D3DX_PI *(::timeGetTime() / 1000.0f));    // 绕X轴旋转  
    350.     D3DXMatrixRotationY(&Ry, D3DX_PI *( ::timeGetTime() / 1000.0f/2));    // 绕Y轴旋转  
    351.     D3DXMatrixRotationZ(&Rz, D3DX_PI *( ::timeGetTime() / 1000.0f/3));   // 绕Z轴旋转  
    352.     matWorld = Rx * Ry * Rz * matWorld;             // 得到最终的组合矩阵  
    353.     g_pd3dDevice->SetTransform(D3DTS_WORLD, &matWorld);  //设置世界变换矩阵  
    354.   
    355.     //--------------------------------------------------------------------------------------  
    356.     //【四大变换之二】:取景变换矩阵的设置  
    357.     //--------------------------------------------------------------------------------------  
    358.     D3DXMATRIX matView; //定义一个矩阵  
    359.     D3DXVECTOR3 vEye(0.0f, 0.0f, -200.0f);  //摄像机的位置  
    360.     D3DXVECTOR3 vAt(0.0f, 0.0f, 0.0f); //观察点的位置  
    361.     D3DXVECTOR3 vUp(0.0f, 1.0f, 0.0f);//向上的向量  
    362.     D3DXMatrixLookAtLH(&matView, &vEye, &vAt, &vUp); //计算出取景变换矩阵  
    363.     g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); //应用取景变换矩阵  
    364.   
    365.     //--------------------------------------------------------------------------------------  
    366.     //【四大变换之三】:投影变换矩阵的设置  
    367.     //--------------------------------------------------------------------------------------  
    368.     D3DXMATRIX matProj; //定义一个矩阵  
    369.     D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI / 4.0f, 1.0f, 1.0f, 1000.0f); //计算投影变换矩阵  
    370.     g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);  //设置投影变换矩阵  
    371.   
    372.     //--------------------------------------------------------------------------------------  
    373.     //【四大变换之四】:视口变换的设置  
    374.     //--------------------------------------------------------------------------------------  
    375.     D3DVIEWPORT9 vp; //实例化一个D3DVIEWPORT9结构体,然后做填空题给各个参数赋值就可以了  
    376.     vp.X      = 0;      //表示视口相对于窗口的X坐标  
    377.     vp.Y      = 0;      //视口相对对窗口的Y坐标  
    378.     vp.Width  = SCREEN_WIDTH;   //视口的宽度  
    379.     vp.Height = SCREEN_HEIGHT; //视口的高度  
    380.     vp.MinZ   = 0.0f; //视口在深度缓存中的最小深度值  
    381.     vp.MaxZ   = 1.0f;   //视口在深度缓存中的最大深度值  
    382.     g_pd3dDevice->SetViewport(&vp); //视口的设置  
    383.   
    384. }  
    385.   
    386.   
    387.   
    388. //*****************************************************************************************  
    389. // Name: Direct3D_Render()  
    390. // Desc: 进行图形的渲染操作  
    391. // Point:【Direct3D渲染五步曲】  
    392. //      1.渲染五步曲之一,清屏操作  
    393. //      2.渲染五步曲之二,开始绘制  
    394. //      3.渲染五步曲之三,正式绘制  
    395. //      4.渲染五步曲之四,结束绘制  
    396. //      5.渲染五步曲之五,翻转显示  
    397. //*****************************************************************************************  
    398.   
    399. void Direct3D_Render(HWND hwnd)  
    400. {  
    401.   
    402.     //--------------------------------------------------------------------------------------  
    403.     // 【Direct3D渲染五步曲之一】:清屏操作  
    404.     //--------------------------------------------------------------------------------------  
    405.     g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);  
    406.   
    407.     //定义一个矩形,用于获取主窗口矩形  
    408.     RECT formatRect;  
    409.     GetClientRect(hwnd, &formatRect);  
    410.   
    411.     //--------------------------------------------------------------------------------------  
    412.     // 【Direct3D渲染五步曲之二】:开始绘制  
    413.     //--------------------------------------------------------------------------------------  
    414.     g_pd3dDevice->BeginScene();                     // 开始绘制  
    415.   
    416.     Matrix_Set();  
    417.   
    418.     // 获取键盘消息并给予设置相应的填充模式  
    419.     if (::GetAsyncKeyState(0x31) & 0x8000f)         // 若数字键1被按下,进行线框填充  
    420.         g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);  
    421.     if (::GetAsyncKeyState(0x32) & 0x8000f)         // 若数字键2被按下,进行实体填充  
    422.         g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);  
    423.   
    424.     //--------------------------------------------------------------------------------------  
    425.     // 【Direct3D渲染五步曲之三】:正式绘制,利用顶点缓存绘制图形  
    426.     //--------------------------------------------------------------------------------------  
    427.   
    428.     //--------------------------------------------------------------------------------------  
    429.     // 【顶点缓存、索引缓存绘图四步曲之四】:绘制图形  
    430.     //--------------------------------------------------------------------------------------  
    431.     // 设置渲染状态  
    432.     g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);  
    433.     g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);   //开启背面消隐  
    434.   
    435.     g_pd3dDevice->SetStreamSource( 0, g_pVertexBuffer, 0, sizeof(CUSTOMVERTEX) );//把包含的几何体信息的顶点缓存和渲染流水线相关联  
    436.     g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );//指定我们使用的灵活顶点格式的宏名称  
    437.     g_pd3dDevice->SetIndices(g_pIndexBuffer);//设置索引缓存  
    438.     g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, 8, 0, 12);//利用索引缓存配合顶点缓存绘制图形  
    439.   
    440.   
    441.     //在窗口右上角处,显示每秒帧数  
    442.     int charCount = swprintf_s(g_strFPS, 20, _T("FPS:%0.3f"), Get_FPS() );  
    443.     g_pFont->DrawText(NULL, g_strFPS, charCount , &formatRect, DT_TOP | DT_RIGHT, D3DCOLOR_XRGB(255,239,136));  
    444.   
    445.     //--------------------------------------------------------------------------------------  
    446.     // 【Direct3D渲染五步曲之四】:结束绘制  
    447.     //--------------------------------------------------------------------------------------  
    448.     g_pd3dDevice->EndScene();                       // 结束绘制  
    449.     //--------------------------------------------------------------------------------------  
    450.     // 【Direct3D渲染五步曲之五】:显示翻转  
    451.     //--------------------------------------------------------------------------------------  
    452.     g_pd3dDevice->Present(NULL, NULL, NULL, NULL);  // 翻转与显示  
    453.        
    454. }  
    455.   
    456.   
    457. //*****************************************************************************************  
    458. // Name:Get_FPS()函数  
    459. // Desc: 用于计算帧速率  
    460. //*****************************************************************************************  
    461. float Get_FPS()  
    462. {  
    463.   
    464.     //定义四个静态变量  
    465.     static float  fps = 0; //我们需要计算的FPS值  
    466.     static int    frameCount = 0;//帧数  
    467.     static float  currentTime =0.0f;//当前时间  
    468.     static float  lastTime = 0.0f;//持续时间  
    469.   
    470.     frameCount++;//每调用一次Get_FPS()函数,帧数自增1  
    471.     currentTime = timeGetTime()*0.001f;//获取系统时间,其中timeGetTime函数返回的是以毫秒为单位的系统时间,所以需要乘以0.001,得到单位为秒的时间  
    472.   
    473.     //如果当前时间减去持续时间大于了1秒钟,就进行一次FPS的计算和持续时间的更新,并将帧数值清零  
    474.     if(currentTime - lastTime > 1.0f) //将时间控制在1秒钟  
    475.     {  
    476.         fps = (float)frameCount /(currentTime - lastTime);//计算这1秒钟的FPS值  
    477.         lastTime = currentTime; //将当前时间currentTime赋给持续时间lastTime,作为下一秒的基准时间  
    478.         frameCount    = 0;//将本次帧数frameCount值清零  
    479.     }  
    480.   
    481.     return fps;  
    482. }  
    483.   
    484.   
    485. //*****************************************************************************************  
    486. // Name: Direct3D_CleanUp()  
    487. // Desc: 对Direct3D的资源进行清理,释放COM接口对象  
    488. //*****************************************************************************************  
    489. void Direct3D_CleanUp()  
    490. {  
    491.     //释放COM接口对象  
    492.     SAFE_RELEASE(g_pIndexBuffer)  
    493.     SAFE_RELEASE(g_pVertexBuffer)  
    494.     SAFE_RELEASE(g_pFont)  
    495.     SAFE_RELEASE(g_pd3dDevice)    
    496. }  


     

     

    多次运行这个demo,我们看到颜色随机的绚烂的立方体在三维空间中以一定的节奏转动着,按下键盘上的1键,我们会看到立方体的线框轮廓,而默认情况下或者按下键盘上的2键,我们会看到立方体的实体轮廓。

    浅墨相信通过之前两篇文章的讲解,理解起这个立方体的创建过程并不是什么难事。

    在demo之中,按键更改填充模式的代码实现如下,也就是在渲染五步曲第三步中加入如下代码就可以了:

    1. // 获取键盘消息并给予设置相应的填充模式  
    2.   
    3.          if (::GetAsyncKeyState(0x31) & 0x8000f)         // 若数字键1被按下,进行线框填充  
    4.   
    5.                    g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_WIREFRAME);  
    6.   
    7.          if (::GetAsyncKeyState(0x32) & 0x8000f)         // 若数字键2被按下,进行实体填充  
    8.   
    9.                    g_pd3dDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);  

     

    其中,GetAsyncKeyState函数用于获取当前键盘上按键的按下状态。关于渲染状态,后面我们会讲解到,这里暂时就给大家一个印象吧。

    最后们我们放出这个demo的运行截图:

     

     

     


    转载出自http://blog.csdn.net/poem_qianmo/article/details/8408723


    展开全文
  • Direct3D(一)

    2019-09-28 22:11:04
    创建顶点/缓存缩索引 HRESULT IDirect3DDevice9::CreateVertexBuffer( UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool IDirect3DVertexBuffer9** ppVertexBuffer, ...HRESULT IDirec...
  • Direct3D

    千次阅读 2018-09-05 17:32:35
    初始化: 创建设备(Device)和上下文(Context) 通过该接口与硬件进行交互。 Device接口用于检测显示适配器功能和分配资源。 Context接口用于设置管线状态、将资源绑定到图形管线和生成渲染命令。...
  • Direct3D基础——Direct3D概述

    千次阅读 2018-11-16 10:38:55
    Direct3D是一套底层图形API,借助该API我们可以利用硬件加速功能来实现3D场景的绘制,Direct3D可以视为应用程序和底层设备的交互。下图展示了Direct3D和硬件之间的交互关系: 图中Direct3D部分是一套已经定义好的...
  • Direct3D(D3D)简介

    千次阅读 2019-05-26 17:36:00
    1.Direct3D(简称:D3D)与DirectX的关系 DirectX,由微软创建的多媒体编程接口,是由很多API组成,按照性质分类,分为:显示部分、声音部分、输入部分和网络部分,其中显示部分担任图像处理关键,其中D3...
  • 深入浅出Direct3D

    千次阅读 2004-09-27 17:14:00
    深入浅出Direct3D 蔡军生 版权所有·第 1 章 3D基础知识·1.1 什么是3D?·1.2 3D相关概念·1.3 DirectX介绍·1.4 Direct3D中3D基元·1.5 Direct3D的表面和顶点法向矢量·第 2 章 Direct3D系统架构·2.1 Direct3D
  • Direct3D 9 到 Direct3D 11 的重要更改

    千次阅读 2015-06-08 18:52:02
    从根本上说,Direct3D 11 与 Direct3D 9 是同类型的 API - 一种到图形硬件的低级别虚拟化接口。它还允许你在各种硬件实现上执行图形绘制操作。自 Direct3D 9 起,图形 API 的布局已更改,设备上下文的概念已经扩展,...
  • DirectX、Direct3D和OpenGL的区别

    千次阅读 2012-03-22 21:42:44
    DirectX,(Direct eXtension,简称DX)是由微软公司创建的多媒体编程接口。...     ... 显示部分担任图形处理的关键,分为DirectDraw(DDraw)和Direct3D(D3D),前者主要负责2D图像加速。它包
  • [D3D] 如何查看Direct3D的版本

    千次阅读 2013-07-12 10:07:11
    “开始” --> “运行” --> 输入dxdiag可以DirectX诊断工具。
  • Direct2D教程(一)

    万次阅读 2012-11-04 18:20:16
    什么是Direct2D 一言以蔽之,就是Windows 7平台上的一个2D图形API,可以提供高性能,高质量的2D渲染。大多数人对Direct2D可能都比较陌生,以至于我之前在论坛上提到这个词的时候,有人...可能大家比较熟悉的是Direct3D
  • 附录A 1. Direct3D对象

    千次阅读 2004-09-27 17:30:00
    Direct3D对象 (蔡军生 版权所有)在Direct3D应用程序里,最先创建的对象是Direct3D对象,最后删除的对象也是Direct3D对象。那么Direct3D对象是用来作什么的呢?是怎么样创建的呢?由于Direct3D对象是用COM实现,...
  • Direct3D好,还是OpenGL好呢?

    千次阅读 2004-09-28 10:05:00
    Q: Direct3D好,还是OpenGL好呢?A: 当你去OPENGL网站问时,肯定说是OPENGL好,如果去D3D网站问时,肯定是说D3D好。其实上,这两个API都是做同样的事情,都是基于同样的硬件尽可能加速,没有谁好谁坏,只要你选择一...
  • Windows 2008 / 2008 R2 开启显卡硬件加速

    万次阅读 2011-12-06 11:35:06
    通过修改注册表项实现: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Video\{187826E1-5903-40A9-BA63-0ED7D45282A8}\0000 查看右邊的鍵值類型為 REG_DWORD 的 Acceleration.Level 如果其值為 0 , ...
  • 这篇文章主要讲解如何初始化Direct3DDirect3D初始化成功后,我们将的到一个代表显卡的C++对象(类型:IDrect3DDevice9)。初始化流程图:完整代码: //全局变量 IDirect3DDevice9 *device = 0 ; // Step 1: ...
  • WPF与Win32图形系统

    千次阅读 2007-01-12 20:24:00
    说明:这里的Win32特指Vista操作系统之前的所有图形系统:GDI、GDI+、Direct3D。 GDI是当今应用程序的主流图形库,GDI图形系统已经形成了很多年。它提供了2D图形和文本功能,以及受限的图像处理功能。虽然在一些...
  • 最简单的视音频播放示例1:总述

    万次阅读 多人点赞 2015-01-13 16:07:32
    在Windows平台下的视频播放技术主要有以下三种:GDI,Direct3D和OpenGL;音频播放技术主要是DirectSound。这些技术属于比较底层的技术,因此使用起来相对来说还是比较复杂的。我在学习的过程中也发现这一领域一直...
  •  文章链接: http://blog.csdn.net/zhmxy555/article/details/8197792 作者:毛星云(浅墨) 邮箱: happylifemxy@163.com 这篇文章里,我们将迈出精通DirectX的第一步,先了解典型Direct3D程序的书写流程,...
1 2 3 4 5 ... 20
收藏数 32,191
精华内容 12,876
关键字:

direct3d