从一个绘制茶壶的例子开始入门DirectX编程
1 新建一个DirectX项目
在Visual Studio 2017中新建一个DirectX项目
解决方案处右键–》添加–》项目,打开新建项目向导
选择
Visual C++
/Windows桌面
下的Windows桌面向导在弹出窗口中选择
桌面应用程序
,并勾选空项目
设置项目属性
- 项目处右键–》属性
- VC++目录–》包含目录–》添加 D:\DirectX\Include
- VC++目录–》库目录–》添加 D:\DirectX\Lib\x86
- 链接器–》输入–》附加依赖项–》添加d3d9.lib d3dx9.lib winmm.lib(每行显示一个)
2 思路分析
初始化Direct3D
需要四个步骤:
1. 创建Direct3D对象,获取IDirect3D9接口指针
1 | // LPDIRECT3D9 是 Direct3D9* 的宏定义 |
2. 检查设备性能
- 通过GetDeviceCaps方法填充一个D3DCAP9结构
- 通过检查D3DCAP9结构的各个成员,可以判断当前硬件是否支持某项功能
使用方式
1 | D3DCAPS9 caps; |
GetDeviceCaps函数原型
1 | HRESULT GetDeviceCaps( |
返回值取值:
- 成功:D3D_OK
- 失败:D3DERR_INVALIDCALL|D3DERR_INVALIDDEVICE|D3DERR_OUTOFVIDEOMEMORY|D3DERR_NOTAVAILABLE
D3DDEVTYPE原型:
1 | typedef enum D3DDEVTYPE |
顶点运行方法
D3DCREATE_SOFTWARE_VERTEXPROCESSING
:硬件顶点处理(处理能力可变)D3DCREATE_HARDWARE_VERTEXPROCESSING
:软件顶点处理(处理能力不可变)D3DCREATE_MIXED_VERTEXPROCESSING
:软硬件混合顶点处理
3. 填充D3DPRESENT_PARAMETERS结构
1 | D3DPRESENT_PARAMETERS d3dpp; |
D3DPRESENT_PARAMETERS结构原型
1 | typedef struct D3DPRESENT_PARAMETERS { |
D3DFORMAT
是一个枚举类,表示表面格式,包含的格式种类有:
常用取值举例
- D3DFMT_UNKNOWN:未知像素格式
- D3DFMT_R8G8B8:每个像素由8位R、G、B组成
- D3DFMT_X8R8G8B8:每个像素由8位R、G、B和8位保留位组成
- D3DFMT_A8R8G8B8:每个像素由8位R、G、B、A(Alpha)组成
- D3DFMT_R5G6B5:每个像素由5位R、6位G、5位B组成
- D3DFMT_D32:32位z缓冲区的位深度
- D3DFMT_D16:16位z缓冲区的位深度
交换链与表现翻转
大多数三维图形程序拥有2个或更多颜色缓存。用于当前屏幕刷新的颜色缓存称为前台缓存,用于图形绘制的其他颜色缓存称为后台缓存。
当在后台缓存绘制完图形后,就需要将后台缓存移动到前台缓存进行显示,这个过程称为表面翻转。
交换链是按顺序逐个提交到前台显示的多个后台缓存的集合。在Direct3D中创建的每个渲染设备至少有一个交换链。
D3DSWAPEFFECT
1 | typedef enum D3DSWAPEFFECT { |
D3DSWAPEFFECT_FLIP
:交换链上包含多个缓存。用一个循环队列来实现,队头指针指向的是前台缓存,这种方式通过队头指针移动来更新队头缓存,以此来实现翻转。D3DSWAPEFFECT_COPY
:交换链上只包含两个缓存。通过将后台缓存的数据复制到前台缓存的方式来进行翻转。D3DSWAPEFFECT_DISCARD
:交换链上包含多个缓存。是D3DSWAPEFFECT_FLIP
的改进版,可以避免以上两种在不合适情况下造成的大量开销。D3DSWAPEFFECT_OVERLAY
:使用可覆盖在主表面上的显存专用区域。当显示覆盖时,不执行复制。覆盖操作在硬件中执行,而不修改主表面中的数据。D3DSWAPEFFECT_FORCE_DWORD
:强制将此枚举编译为32位的大小。如果没有此值,一些编译器将允许此枚举编译到32位以外的大小。不使用此值。
4. 创建Direct3D设备对象,获取IDirect3DDevice9接口指针
1 | g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, vp, &d3dpp, &g_pd3dDevice) |
CreateDevice函数原型
1 | HRESULT CreateDevice( |
返回值:
- 如果该方法成功,则返回值为D3D_OK
- 如果方法失败,则返回值可以是以下值之一:D3DERR_DEVICELOST,D3DERR_INVALIDCALL,D3DERR_NOTAVAILABLE,D3DERR_OUTOFVIDEOMEMORY
额外功能
创建字体对象
1 | D3DXCreateFont(g_pd3dDevice, 0, 0, 0, 0, 0, 0, 0, 0, 0, L"Arial", &g_pFont) |
D3DXCreateFont函数原型
1 | HRESULT D3DXCreateFont( |
返回值:
- 如果函数成功,则返回值为S_OK
- 如果该函数失败,则返回值可以是以下之一:D3DERR_INVALIDCALL,D3DXERR_INVALIDDATA,E_OUTOFMEMORY
获取窗口客户区
1 | GetClientRect(hWnd, &g_ClientRect); |
GetClientRect函数原型
1 | BOOL GetClientRect( |
返回值:
- 如果函数成功,则返回值为非零
- 如果函数失败,则返回值为零
设置观察和投影矩阵
观察矩阵设置
观察变换使世界坐标系的坐标转换成观察坐标系的坐标
1 | D3DXVECTOR3 vEyePt(0.0f, 0.0f, -5.0f); |
D3DXMATRIXA16类
一个 4x4 , 16位对齐的矩阵。
DirectX常用坐标系
D3DXMatrixLookAtLH函数原型
1 | D3DXMATRIX* D3DXMatrixLookAtLH( |
D3DXMatrixLookAtLH函数原理说明
齐次坐标表示法就是用n+1维向量表示一个n维向量
应用齐次坐标可以有效地用矩阵运算把二维、三维甚至更高维空间中点集从一个坐标系转换到另一个坐标系中
1 | zaxis = normal(At - Eye) |
由此可知,在观察坐标系里,z轴方向代表实现方向,y轴代表上方向。
变换设置
1 | g_pd3dDevice->SetTransform(D3DTS_VIEW, &matView); |
SetTransform函数原型
1 | HRESULT SetTransform( |
D3DTRANSFORMSTATETYPE结构
1 | typedef enum D3DTRANSFORMSTATETYPE { |
投影矩阵设置
投影变换将三维坐标变换成二维坐标
1 | D3DXMATRIXA16 matProj; |
D3DXMatrixPerspectiveFovLH函数原型
1 | D3DXMATRIX* D3DXMatrixPerspectiveFovLH( |
创建场景图形
1 | HRESULT InitGeometry() |
D3DXCreateTeapot函数原型
1 | HRESULT D3DXCreateTeapot( |
当方法返回时,ppAdjacency参数将由每个面三个DWORD的数组填充,这些数组为网格中的每个面指定三个邻居。可以指定NULL
返回值:
- 如果函数成功,则返回值为D3D_OK
- 如果函数失败,则返回值可以是以下之一:D3DERR_INVALIDCALL,D3DXERR_INVALIDDATA,E_OUTOFMEMORY
图形渲染
渲染步骤
- 先通过IDirect3DDevice9接口进行相关的渲染状态设置
- 然后渲染图形【所有渲染操作必须在BeginScene()和EndScene()之间进行】
1. 清空后台缓存
1 | g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(45, 50, 170), 1.0f, 0); |
Clear函数原型
1 | HRESULT Clear( |
返回值:
- 成功,返回D3D_OK
- 失败,返回D3DERR_INVALIDCALL
Flags取值:
D3DCLEAR_STENCIL
:清除模板缓冲区D3DCLEAR_TARGET
:清除渲染目标D3DCLEAR_ZBUFFER
:清除深度缓冲区
2. 绘制图形
1 | g_pd3dDevice->BeginScene() // 开始在后台缓存绘制图形 |
设置灯光
方向光
1 | D3DXVECTOR3 vecDir; |
结构体D3DLIGHT9原型
1 | typedef struct D3DLIGHT9 { |
聚光灯模型
D3DLIGHTTYPE类定义
1 | typedef enum D3DLIGHTTYPE { |
光源比较
光源 | 包含属性 | 特性 |
---|---|---|
点光源 | 颜色、位置 | 发出的光的强度会虽随着距离物体的远近而衰减 |
定向光 | 颜色、方向 | 所有光线平行地沿着某个特定方向传播,不会衰减 |
聚光灯 | 颜色、位置、方向 | 聚光灯发出的光线由一个明亮的内锥体和大一点的外锥体组成且光线强度从内锥体到外锥体逐渐衰减 |
- 定向光要比点光源快一点
- 但点光看起来要好一些
- 聚光灯提供有趣的视觉效果,但计算耗时
全局环境光
1 | g_pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0xff505050); |
SetRenderState函数原型
设置单个设备的渲染状态参数
1 | HRESULT SetRenderState( |
D3DRENDERSTATETYPE类
MSDN官方定义
设置材质
1 | D3DMATERIAL9 mtrl; |
D3DMATERIAL9类结构
1 | typedef struct D3DMATERIAL9 { |
设置世界矩阵
1 | D3DXMATRIXA16 matWorld; |
绘制茶壶
1 | g_pTeapotMesh->DrawSubset(0); |
DrawSubset函数原型
1 | HRESULT DrawSubset( |
帧速率
1 | static float fps = 0; |
timeGetTime函数
- 检索系统时间,以毫秒为单位
- 系统时间是自Windows启动以来经过的时间
将在后台缓存绘制的图形提交到前台缓存显示
1 | g_pd3dDevice->Present(NULL, NULL, NULL, NULL); |
3 完整代码
头文件UtilMacro.h
1 | // UtilMacro.h |
源文件Main.cpp
1 | // Main.cpp |