课程1:开始Direct3D

鸣谢:

翻译来自:http://www.directxtutorial.com/Lesson.aspx?lessonid=9-4-1

如有不对之处请见谅。微笑

概述:

首先,我正式欢迎你使用Direct3D,我很高兴能分享一些关于3D编程的基础知识和高级技术。无论以后你是搭建自己的3D引擎还是借用修改别人的3D引擎,甚至只是购买使用,理解3D引擎背后的工作原理都是很重要的。

和3D游戏引擎背后工作原理同样重要的是我们即将使用的3D库(DirectX),这也就是我为什么开展这个blog系列。这次的课程会涉及到Direct3D的基本概念以及开始第一个DirectX编程的相关内容,最后我会在window窗口中实现一个DirectX例程。

COM

什么是COM接口?

COM接口是指Component Object Model接口,是微软定义的标准接口。它是开发软件组件的一种方法。

在COM构架下,人们可以开发出各种各样的功能专一的组件,然后将它们按照需要组合起来,构成复杂的应用系统。由此带来的好处是多方面的:可以将系统中的组件用新的替换掉,以便随时进行系统的升级和定制;可以在多个应用系统中重复利用同一个组件;可以方便的将应用系统扩展到网络环境下;COM与语言,平台无关的特性使所有的程序员均可充分发挥自己的才智与专长编写组件模块;等等。【百度百科】吐舌笑脸

为什么我会讨论COM呢?

没错,DirectX就是一系类的COM组件,其中一个就是Direct3D(我们会使用到)。Direct3D时一个相当强大的类库包含了所有你用来做2D/3D图形编程的软件、硬件和各种件~眨眼

举个例子:你会看到这样的调用

d3d->CreateDevice()
d3d->Release()

我们用Direct3D的接口类间接地调用了函数CreateDevice() 和 Release(), 你会在接下来的例程中看到更多接口的使用。

交换链和页面“翻转”(The Swap Chain and Page Swapping)

任何一个显卡的内存里包含一个指针,指向显示在当前窗口的图像缓存。但我们需要渲染一些物体像3D模型或者纹理时,显卡会更新缓存(this array)同时传送这些信息给显示器,显示器重绘屏幕,从顶部到底部逐渐加入被渲染的部分。

然后,这里有一点点问题惊讶,显示器的显示更新频率不能达到实时渲染的速度。大部分的显频(Display refresh rate)为60Hz到100Hz不等(一些3D显示设备可以达到120Hz以上)。设想这样的情况:一个新的模型已经被成功渲染到显卡的buffer里了(所有buffer里的数据是新模型的数据),而这个时候显示器还在更新上一帧的数据,等显示器更新完完整一帧时,就会出现屏幕上的图像被分割成两半的现象,上面的一半是老一点的图像,下面的一半是新一点的图像。这种现象叫做tearing。主要就是由于渲染速度比现实速度快导致,渲染数据非法更新显示数据。

为了避免这样的现象,DirectX设计了一种叫做swapping的技术。

“翻转”(Swapping)

区别于直接把渲染后的数据送给显示器进行显示,Direct3D将渲染的结果先放入到第二个像素缓存中,也就是后项缓存(back buffer)。而显示器正在绘制的缓存叫做前项缓存(front buffer)。你可以先把所有的图形绘制到back buffer里,等待显示器已经完成一次绘制后(这也就是为什么需要设置divice的显示频率,也就是告诉DirectX等待多久在进行swap操作),Direct3D会用back buffer更新front buffer,同时扔掉之前显示的图像。

你一定以为这样就make sense了,其实不然,现在是解决了GPU速度高于显示器速度的问题。如果只做这部分工作仍然会导致tearing,因为在在显示器进行更新新的一帧的时候可能还有部分数据在传输(CPU的速度比显示器的显示速度快狠多)。

为了彻底避免tearing现象,DirectX使用了指针指向前项和后项的buffer,当需要重绘的时候直接交换指针的值,back buffer就可以成为front buffer,这样确保了显示器更新的时候所有的数据都是重新渲染后的数据。

1

当然我们可以通过使用更多的back buffer提升游戏的性能

2

为什么说增加多的back buffer可以提升游戏的性能呢。因为当显卡更新好一帧图像放到back buffer里后,显示器还没有完成前一帧的显示。如果此时进行swapping操作,必然会导致tearing。所以所能做的就是程序停下来等到屏幕完成了绘制(这其实很影响GPU的效率)。如果可以,当然是让GPU利用这段等待时间绘制接下来的图像依次放入好几个back buffer中, 这样GPU遇到非常复杂耗时的绘制时也不会使得显示效果出现卡滞。

这个过程就叫做swap链,每次在屏幕上显示完一帧后交换buffer的位置。(这里我理解的是屏显控制buffer交换的速率,然后原文说的是swapping positions each time a new frame is rendered,在新的一帧完成渲染后就进行wasp

基本的Direct3D编程

我们不会以一个“Hello World”开始Direct3D的学习(学习一门新的语言惯用的方式),因为Direct3D其实不是一门新的语言(只是COM类库)。我们会把用Direct3D把一个窗口填充为蓝色作为我们的第一个项目,分为接下来最基本的几步:

1. 创建全局变量和函数声明(或者在类中作为成员变量和方法);

2. 创建函数完成Direct3D的初始化和Direct3D设备的创建;

3. 创建渲染函数完成对一帧的渲染;

4. 创建销毁函数完成对资源的释放和Direct3D的关闭。


1. 创建全局变量和函数声明

对于Direct3D的使用,需要调用相关的引用以及库文件,如下所示:

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
// include the Direct3D Library file
#pragma comment (lib, "d3d9.lib")
// global declarations
LPDIRECT3D9 d3d; // the pointer to our Direct3D interface
LPDIRECT3DDEVICE9 d3ddev; // the pointer to the device class
// function prototypes
void initD3D(HWND hWnd); // sets up and initializes Direct3D
void render_frame(void); // renders a single frame
void cleanD3D(void); // closes Direct3D and releases memory
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

#pragma comment (lib, "d3d9.lib")

包含Direct3D 9 的库文件,#pragma comment命令用来通知编译目标object file应该包含的文件信息,对于其中的第一个参数 lib 用来指示我们想要加入一个library文件,路径"d3d9.lib",默认是在当前目录下,可以添加路径指引到需要的地方。

LPDIRECT3D9 d3d;

这个变量是一个长指针指向Direct3D,我们需要创建一个iDirect3D9的类。

LPDIRECT3DDEVICE9 d3ddev;

Direct3D Device是另一个接口,用来管理所有与graphics driver, video card,图形渲染的信息。

2. 创建函数完成Direct3D的初始化和Direct3D设备的创建;

// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION); // create the Direct3D interface
    
    D3DPRESENT_PARAMETERS d3dpp; // create a struct to hold various device information
    
    ZeroMemory(&d3dpp, sizeof(d3dpp)); // clear out the struct for use
    d3dpp.Windowed = TRUE; // program windowed, not fullscreen
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; // discard old frames
    d3dpp.hDeviceWindow = hWnd; // set the window to be used by Direct3D
    
    // create a device class using this information and information from the d3dpp stuct
    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                        D3DDEVTYPE_HAL,
                        hWnd,
                        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                        &d3dpp,
                        &d3ddev);
}
原文地址:https://www.cnblogs.com/yxy8023ustc/p/2779154.html