在计算机的3D世界中,所有的物体模型都是通过多边形网格来逼近表示的,这些多边形可以是三角形,也可以是四边形。多边形网格是构成物体模型的基本单元。从存储的角度出发,三维模型本质是通过顶点来实现的。在Direct3D中,顶点缓冲区(Vertex Buffer)用来保存顶点数据的。顶点数据的存储位置可以在内存中,也可以在显卡的显存中。为了讲解清楚顶点缓冲区的使用方法,我们来看一个使用顶点缓冲区构建一个三角形的例子。首先我们使用VS2019创建一个名为“D3D_03_Triangle”的项目,然后创建“main.cpp”源码文件。同时,不要忘了在“链接器”下的“系统”项目中,将“子系统”的值由“控制台”改为“窗口”,还要添加DirectX的支持,也就是添加“包含目录”和“库目录”。虽然比较繁琐,但是这些修改都是必须的。首先,我们还是需要引入头文件,代码如下:

// 引入头文件
#include <windows.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <string>// 引入依赖的库文件
#pragma comment(lib,"d3d9.lib")
#pragma comment(lib,"d3dx9.lib")#define WINDOW_LEFT       200             // 窗口位置
#define WINDOW_TOP      100             // 窗口位置
#define WINDOW_WIDTH    800             // 窗口宽度
#define WINDOW_HEIGHT   600             // 窗口高度
#define WINDOW_TITLE    L"D3D游戏开发"    // 窗口标题
#define CLASS_NAME      L"D3D游戏开发"    // 窗口类名// Direct3D设备指针对象
LPDIRECT3DDEVICE9 D3DDevice = NULL;// 鼠标位置
int mx = 0, my = 0;

使用顶点缓冲区(Vertex Buffer)绘制三角形之前,我们需要先定义顶点的结构体,我们这里只定义顶点的坐标和颜色即可,然后我们在声明一个顶点缓存对象,代码如下:

// 定义FVF灵活顶点格式结构体
struct D3D_DATA_VERTEX { FLOAT x, y, z, rhw; DWORD color; };// D3DFVF_XYZRHW 像素坐标(窗体左上角为坐标系原点,坐标单位也是像素),不需要投影变换
#define D3D_FVF_VERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE)// 顶点缓冲区对象
LPDIRECT3DVERTEXBUFFER9 D3DVertexBuffer = NULL;

我们定义了一个结构体对象和一个顶点格式宏。这两个东西是对应关系。结构体中的x,y,z,rhw就对应了宏里面的D3DFVF_XYZRHW,而结构体中的color就对应了宏里面的D3DFVF_DIFFUSE。DIFFUSE就是漫反射颜色,也就是物体的颜色。这里需要注意的是,我们定义的顶点类型为D3DFVF_XYZRHW,它的意思是说我们的顶点坐标就是窗体的2维坐标系。D3DFVF_XYZRHW是(x, y, z, w)形式的齐次坐标,它的坐标值(x, y)是基于窗体坐标系而言的,坐标系的原点(0,0)位于左上角,且x向右为正,y向下为正。而其中z是象素深度,取值范围:0.0-1.0,离观察者最近的地方为0.0,观察范围内最远可见的地方为1.0。w一般情况下只用1.0即可。

我们之前讲过,3D模型绘制在2D屏幕上,需要投影变换。而2D图形绘制在2D屏幕上的时候,因为维度相同,因此可以直接绘制,不需要投影变换。相对于D3DFVF_XYZRHW类型的顶点,另一种D3DFVF_XYZ的类型,它则是绘制3D图形,它就需要进行投影变换了。D3DFVF_XYZRHW通常用于做UI(用户界面),并且D3DFVF_XYZRHW是高洛德光照,而D3DFVF_XYZ默认的为无光照的。

上述顶点格式宏中我们只是简单的构建了顶点的坐标数据和颜色数据,其实我们还可以定义更多,比如法线向量和纹理坐标等等。需要注意的是,我们在书写灵活顶点格式的宏定义的时候需要遵守一个顺序原则,顺序就是优先级需要这样来分:

坐标位置>RHW值>混合权重值>法线向量>漫反射颜色值>镜面反射颜色值>纹理坐标

接下来,我们继续声明几个函数,用于完成不同的功能,代码如下:

// 声明游戏开始函数(初始化DirectX)
bool startGame(HWND hwnd, HINSTANCE hInstance);// 声明游戏结束函数(释放对象)
void endGame();// 声明游戏循环中处理用户输入函数
void update(int type, WPARAM wParam);// 声明游戏循环中游戏界面渲染函数
void render(HWND hwnd);// 声明窗口过程函数
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

以上的函数,我们上一章节都使用过了,这里不在详细介绍。我们将上一章节中的wWinMain函数,WndProc函数,startGame函数全部复制过来。这里我们要使用DirectX绘制三角形了,不需要打印文字了,所以就不需要D3DFont对象了,大家可以删除或注释掉。本案例中,我们不需要对鼠标和键盘事件进行处理,因此update方法留空即可。

// 定义游戏循环中处理用户输入函数
void update(int type, WPARAM wParam) {};

接下来,我们就行复制render函数过来,因为我们不绘制文字了,因此绘制文字的代码删除或者注释掉即可,代码如下:

// 定义游戏循环中游戏界面渲染函数
void render(HWND hwnd) {// 第一步:清屏操作D3DDevice->Clear(0,NULL,D3DCLEAR_STENCIL | D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,D3DCOLOR_XRGB(255, 255, 255),1.0f,0);// 第二步:开始绘制D3DDevice->BeginScene();// 第三步:绘制三角形// 第四步:结束绘制D3DDevice->EndScene();// 第五步:显示翻转D3DDevice->Present(NULL,NULL,NULL,NULL);
}

最后我们在给出endGame函数代码,如下:

// 定义游戏结束函数(释放对象)
void endGame() {D3DVertexBuffer->Release();D3DVertexBuffer = NULL;D3DDevice->Release();D3DDevice = NULL;
}

此时,一个干干净净的DirectX程序就准备好了,大家可以运行一下,保证代码复制和改动都是正确的。因为很多代码都是重复固定的,为了不破坏这些固定的代码,我们重新定义两个新函数,专门用来初始化游戏数据和绘制游戏画面,如下:

// 声明初始化场景函数
void initScene(HWND hwnd, HINSTANCE hInstance);// 声明渲染场景函数
void renderScene(HWND hwnd);

第一个函数initScene肯定是在DirectX初始化完毕后调用了,也就是之前我们实例化D3DFont对象的位置,就是在startGame函数的末尾,代码如下:

// 第五步:释放Direct3D接口对象
D3D9->Release();
D3D9 = NULL;// 初始化场景
initScene(hwnd, hInstance);
return true;

第二个函数renderScene就是放在render函数的第三步,代码如下:

// 第二步:开始绘制
D3DDevice->BeginScene();// 第三步:绘制场景
renderScene(hwnd);// 第四步:结束绘制
D3DDevice->EndScene();

两个函数的声明和调用都完成了,接下来我们开始定义第一个函数initScene来绘制三角形。我们知道一个三角形由三个顶点构成,因此我们需要提供三个顶点的数据。至于这三个顶点的位置,我们就根据窗体坐标系来定义了。我们还定义了三个顶点的颜色为红色。也就是说,绘制出来的三角形应该是红色的。代码如下:

// 三角形顶点数组,窗体左上角是坐标原点,顶点从左下方顺时针开始
D3D_DATA_VERTEX vertexArray[] =
{{ 300.0f, 400.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(255, 0, 0) },{ 400.0f, 200.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(255, 0, 0) },{ 500.0f, 400.0f, 0.0f, 1.0f, D3DCOLOR_XRGB(255, 0, 0) },
};// 创建顶点缓冲区对象
D3DDevice->CreateVertexBuffer(sizeof(vertexArray),   // 表示顶点数据的字节大小0,                        // 表示使用缓存的额外信息,默认0即可。D3D_FVF_VERTEX,         // 表示灵活顶点格式D3DPOOL_DEFAULT,     // 表示顶点缓存的存储位置,D3DPOOL_DEFAULT表示显卡的显存中&D3DVertexBuffer,      // 表示顶点缓存指针对象NULL);                 // 表示保留参数,在此设置为NULL// 将三角形顶点数组写入顶点缓冲区对象
void* ptr;
D3DVertexBuffer->Lock(0,                     // 表示从什么位置开始加锁(存储区偏移量)sizeof(vertexArray),    // 表示要锁定的字节数,也就是加锁区域的大小(void**)&ptr,         // 表示被锁定的存储区的指针0);                      // 表示锁定方式,这里我们设置0即可
// 赋值顶点数据到缓冲区
memcpy(ptr, vertexArray, sizeof(vertexArray));
D3DVertexBuffer->Unlock();

这里需要注意的是,向顶点缓冲区对象中写入顶点数据的时候,需要加锁和解锁。这些DirectX API的参数基本上在注视中给到大家了,理解就可以了,不需要深入研究。我们这需要知道,使用三个顶点来绘制一个三角形,将顶点数据放入到顶点缓存对象中。接下来我们就来完成renderScene函数,绘制一个三角形了。代码如下:

D3DDevice->SetStreamSource(0,                             // 表示顶点缓冲区连接的数据流,默认0即可D3DVertexBuffer,               // 表示顶点缓冲区数据0,                              // 表示数据流中偏移量,默认0即可sizeof(D3D_DATA_VERTEX));      // 表示每个顶点结构的大小D3DDevice->SetFVF(D3D_FVF_VERTEX); // 设置灵活顶点格式D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST,          // 表示要绘制的图元类型,D3DPT_TRIANGLELIST表示三角形列表0,                            // 指定顶点缓存读取顶点数据的起始索引1);                         // 绘制三角形图元数量

绘制的过程主要由三个函数完成,SetStreamSource就是设置绘制的数据来源于我们的顶点缓存对象。SetFVF函数就是设置顶点格式。DrawPrimitive函数就是绘制三角形了。运行代码效果如下:

为什么要绘制一个三角形。因为在游戏引擎中,2D图像和3D模型都是由三角形组成的。

本课程的所有代码案例下载地址:

workspace.zip

备注:这是我们游戏开发系列教程的第二个课程,这个课程主要使用C++语言和DirectX来讲解游戏开发中的一些基础理论知识。学习目标主要依理解理论知识为主,附带的C++代码能够看懂且运行成功即可,不要求我们使用DirectX来开发游戏。课程中如果有一些错误的地方,请大家留言指正,感激不尽!

第三章 DirectX 图形绘制(上)相关推荐

  1. 第三章 DirectX 图形绘制(下)

    一个复杂的3D模型可能由成千上万个三角形组成,而2D图像基本都是由两个三角形组成的四边形组成.如何绘制一个四边形呢: 在图中,我们用四个顶点组成了一个正方形,这四个顶点分别是v0,v1,v2,v3.为 ...

  2. matlab在绘图时分数,第三章_Matlab图形绘制试卷.ppt

    2.griddata函数,用来产生经插值后均匀间隔数据作图.常用的调用方法是:[XI,YI,ZI] = griddata(x,y,z,XI,YI,'method'),其中x,y,z来自关系式z=f(x ...

  3. 第三章 使用 matplotlib 绘制直方图

    系列文章目录 第一章 使用 matplotlib 绘制折线图 第二章 使用 matplotlib 绘制条形图 第三章 使用 matplotlib 绘制直方图 第四章 使用 matplotlib 绘制散 ...

  4. matlab图形绘制经典案例,MATLAB经典教程第四章_图形绘制.ppt

    <MATLAB经典教程第四章_图形绘制.ppt>由会员分享,可在线阅读,更多相关<MATLAB经典教程第四章_图形绘制.ppt(32页珍藏版)>请在人人文库网上搜索. 1.Ma ...

  5. windows 程序设计 第三章读书笔记(上)

    娘的,今天晚上在阳台做饭把水管一脚踢爆了,水流到下面的住户的阳台,让个老娘们把我骂了一顿,本着做错事的原则,我装的很绅士还说了个对不起,擦,真是条纯汉子,能屈能伸. 大爷,别看我年轮小,我都给总结了, ...

  6. 第九章、图形绘制——图形控件和图形方法的应用

    1.图形控件 1.Shape控件 (1)Shape属性 2-Oval椭圆 4-Round Rectangle圆角矩形 5-Round Square圆角正方形 (2)FillStyle(填充类型)和Fi ...

  7. OpenGL(三)三维图形绘制

    #include "stdafx.h" #include<GL/freeglut.h> #include<GLFW/glfw3.h>// 绘图棱锥 void ...

  8. 第三章网页图形图像设计

    3.1网页图形图像在网页中的应用 3.1.1图形图像在网页中的应用 1.标志(LOGO) 2.背景框图 3.主图 4.超链接 3.1.2构成要素--点线面 1.点 圆点.方点.三角点.锯齿点.泥点.雨 ...

  9. 计算机图形学绘制多边形代码_《GPU编程与CG语言之阳春白雪下里巴人》- 第二章(GPU 图形绘制管线)...

    第二章 GPU 图形绘制管线 万事开头难,每门科学都是如此. ------ 马克思 图形绘制管线描述 GPU 渲染流程,即"给定视点.三维物体.光源.照明模式,和纹理等元素,如何绘制一幅二维 ...

  10. TI培训——电子电路基础知识讲座(第三章上)

    第三章 晶体管电路设计(上) 3.1 二极管电路 3.1.1 二极管的一般性质 3.1.2 二极管的伏安特性 3.1.3 二极管的动态特性 3.1.4 二极管的分类 3.2 三极管基本特性与三极管恒流 ...

最新文章

  1. CUDA8.0+VS2015+Win10开发环境搭建教程
  2. android短信功能裁剪,Android短信发送功能实现技巧分享
  3. python入门需要多久-怎么自学python,大概要多久?
  4. Proteus仿真STM32F103R6微控制器的GPIO(按键控制LED开关)
  5. Java多线程:线程属性
  6. mysql5.7.25my.ini_mysql5.7 没有my.ini 的解决办法
  7. 如何修改远程桌面连接3389端口
  8. Redis如何支持高并发的访问
  9. jquery进度条组件
  10. Linux服务器上修改深度学习代码
  11. Matlab生成.exe可执行程序
  12. 养老保险怎么缴最划算?应该少交还是多交?
  13. Linux内核如何私闯进程地址空间并修改进程内存
  14. renren-generator:运行报错java: 找不到符号 符号: 类 Longblob
  15. cetus权限连接主从mysql_网易开源中间件 -Cetus监控模块
  16. 计算机命令通配符,Windows的命令行怎么支持通配符
  17. 关于加强公司内部员工之间的沟通与交流的一点思考1
  18. openEuler安装GPU、CUDA、cudnn
  19. 干货盘点!推荐程序员日常使用的5款工具软件
  20. 万字泣血解析割韭菜内幕,程序员别老想着做副业

热门文章

  1. Ping命令返回错误信息说明
  2. python小波去噪实验
  3. Python题目:判断101-200之间有多少个素数,并输出所有素数,简单方法
  4. win10动态桌面_需要2020考研倒计时的动态桌面源的亲们在此留言
  5. sentaurus器件仿真(sdevice部分)(二)
  6. OriginPro 2021 设置成中文(软件自带)
  7. 100以内的奇数和是多少,问一下100以内的奇数和偶数分别相加起来和是多少?
  8. matlab版本下载地址
  9. MATLAB 添加已下载的工具箱步骤
  10. 《originpro8》怎么拟合曲线