ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DirectX 9.0c示例包解析:从清屏到旋转立方体的图形编程入门

DirectX 9.0c示例包解析:从清屏到旋转立方体的图形编程入门 简介这是一份面向DirectX 9游戏开发初学者的入门示例工程基于DirectX 9c与Visual Studio 2012构建通过一个可编译运行的游戏样本演示图形渲染、资源加载与主循环等核心流程帮助读者理解DirectX 9 API在Windows平台上的基本用法。压缩包共8个文件约5KB以cpp源码、vcxproj工程文件与filters筛选器为主配合md说明文档、txt资源与标签记录以及gitattributes、gitignore等版本控制配置结构精简便于快速导入IDE并逐步调试。目前已有49人学习关注。读者可借此掌握DirectX 9初始化步骤、工程目录组织方式与Visual Studio下的编译调试思路适合作为游戏编程入门练手与代码阅读的参考素材。1. DirectX 9.0c 示例包一个被低估的图形入门切口如果你手头有一份directX9c 的一个示例 .zip解压后大概率会看到一堆.cpp、.h和.fx文件外加几个.sln或.vcproj工程。别急着双击运行——这套东西不是拿来即用的成品 Demo而是微软 DirectX SDK 里那批经典示例的打包版本覆盖了顶点缓冲、纹理映射、光照、着色器、粒子系统等基础模块。它解决的核心问题是让你在一个能编译、能跑、能改的最小工程里亲眼看到 D3D9 的渲染管线是怎么转起来的。适合谁适合刚接触图形编程、想绕过 OpenGL 环境配置玄学、直接摸 Windows 原生 3D 接口的人也适合老项目维护者需要翻出 D3D9 固定管线写法做兼容或移植参考。热搜里directX9c和.zip绑在一起出现说明很多人是在找“能直接下载、解压就能看”的示例集合而不是去啃 SDK 文档。这份资源正好卡在这个需求上。2. 把示例跑起来环境、工程与第一个三角形2.1 为什么 D3D9 示例在今天还值得跑一遍DirectX 9.0c 是固定管线和可编程管线并存的分水岭。它的 API 设计比 D3D11/12 直白得多创建设备、设置渲染状态、画图三步走。现代图形接口把资源绑定、命令队列、同步这些概念拆得很细新手一上来容易陷在“为什么没画面”的黑匣子里。D3D9 示例的好处是你改一个SetRenderState就能立刻看到颜色变化改一个SetTexture就能看到贴图切换反馈链路短。常见做法是拿Tutorial01或SimpleSample这类最小工程当起点先确认设备能创建、窗口能清屏再逐步加顶点、加索引、加矩阵变换。这套流程走一遍比看十篇管线概述都管用。2.2 编译环境怎么搭VS 版本与 SDK 路径这批示例原始工程多是 VS2005/2008 格式用新版 Visual Studio 打开会触发工程升级。我一般会先装一个 DirectX SDKJune 2010 版是最后一个独立包然后把包含目录和库目录指过去。注意Windows SDK 里已经带了d3d9.h和d3d9.lib但示例里用到的d3dx9.h、d3dx9.lib以及各种D3DX辅助函数必须靠独立 SDK 补上。如果你只装 Windows SDK编译时会报一堆D3DX未定义。# 典型目录结构解压后 directX9c示例/ ├── Tutorials/ │ ├── Tutorial01/ │ │ ├── Tutorial01.cpp │ │ ├── Tutorial01.vcproj │ │ └── dxstdafx.h │ ├── Tutorial02/ │ └── ... ├── Samples/ │ ├── SimpleSample/ │ └── ... └── Include/ (部分包会带)上面是解压后常见的目录布局。Tutorials按序号递进Samples是完整度更高的场景。先别碰Samples从Tutorial01开始它的代码量最少逻辑最干净。2.3 从清屏到画三角形关键代码段拆解以Tutorial01为例核心就三块InitD3D、Render、Cleanup。下面这段是设备创建和清屏的骨架我按自己的注释习惯标了参数含义。// 创建 D3D9 设备 LPDIRECT3D9 g_pD3D NULL; LPDIRECT3DDEVICE9 g_pd3dDevice NULL; HRESULT InitD3D(HWND hWnd) { // 创建 D3D 接口对象 if (NULL (g_pD3D Direct3DCreate9(D3D_SDK_VERSION))) return E_FAIL; D3DPRESENT_PARAMETERS d3dpp; ZeroMemory(d3dpp, sizeof(d3dpp)); d3dpp.Windowed TRUE; // 窗口模式调试方便 d3dpp.SwapEffect D3DSWAPEFFECT_DISCARD; // 后台缓冲丢弃最常用 d3dpp.BackBufferFormat D3DFMT_UNKNOWN; // 窗口模式用桌面格式 d3dpp.EnableAutoDepthStencil TRUE; // 开启深度模板 d3dpp.AutoDepthStencilFormat D3DFMT_D16; // 16 位深度 // 创建设备HAL 是硬件抽象层软件顶点处理兼容性更好 if (FAILED(g_pD3D-CreateDevice( D3DADAPTER_DEFAULT, // 默认显示适配器 D3DDEVTYPE_HAL, // 硬件加速 hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, // 软件顶点处理 d3dpp, g_pd3dDevice))) return E_FAIL; return S_OK; }D3DADAPTER_DEFAULT表示主显卡多屏环境可以枚举适配器换索引。D3DDEVTYPE_HAL走硬件如果虚拟机或老机器跑不起来可以临时改成D3DDEVTYPE_REF走参考光栅器慢但能出图。D3DCREATE_SOFTWARE_VERTEXPROCESSING是血泪经验很多集成显卡或远程桌面环境下硬件顶点处理会直接黑屏软件处理虽然费 CPU但兼容性稳得多。清屏和绘制在Render里VOID Render() { // 清空后台缓冲和深度缓冲 g_pd3dDevice-Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 255), 1.0f, 0); if (SUCCEEDED(g_pd3dDevice-BeginScene())) { // 这里放绘制调用Tutorial01 没有几何体只清屏 g_pd3dDevice-EndScene(); } // 翻转后台缓冲到前台 g_pd3dDevice-Present(NULL, NULL, NULL, NULL); }Clear的第一个参数是矩形区域数量传 0 和 NULL 表示全屏清。D3DCOLOR_XRGB(0,0,255)是蓝色改这三个值就能换背景色。Present负责把后台缓冲翻到屏幕如果这里返回D3DERR_DEVICELOST说明设备丢了常见于 AltTab 切换或屏保触发需要重建资源。2.4 工程升级后常见的链接错误与修复用 VS2019/2022 打开旧工程升级向导走完后链接阶段大概率报LNK2019: 无法解析的外部符号 _D3DXCreateTextureFromFileA12之类。原因是d3dx9.lib没链上。右键工程 → 属性 → 链接器 → 输入 → 附加依赖项加上d3dx9.lib;d3d9.lib;dxguid.lib。如果还报dxerr9.lib缺失那是旧版错误处理库新 SDK 里已经并入dxerr.lib把代码里的#include dxerr9.h改成#include dxerr.h即可。另一个高频坑是字符集旧工程默认多字节新 VS 默认 UnicodeD3DXCreateTextureFromFile会变成...FromFileW如果代码里写死了A版本就会链接失败。统一在工程属性里把字符集改成“使用多字节字符集”最省事。3. 顶点缓冲、纹理与矩阵把示例改成自己的试验田3.1 顶点格式与 FVF固定管线的核心约定D3D9 固定管线靠 FVFFlexible Vertex Format告诉设备“这个顶点里有什么”。比如D3DFVF_XYZ | D3DFVF_DIFFUSE表示顶点包含位置和漫反射颜色。示例里常见的是D3DFVF_XYZRHW那是屏幕坐标跳过矩阵变换直接画 2D。如果你想画 3D 三角形得用D3DFVF_XYZ然后自己设世界、视图、投影矩阵。我一般会先定义一个结构体再写一个SetFVF调用struct CUSTOMVERTEX { FLOAT x, y, z; // 位置 DWORD color; // 颜色 FLOAT u, v; // 纹理坐标 }; #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1) // 创建顶点缓冲 g_pd3dDevice-CreateVertexBuffer( 3 * sizeof(CUSTOMVERTEX), // 三个顶点 0, // 用途标志静态几何传 0 D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, // 默认池显存 g_pVB, NULL);D3DPOOL_DEFAULT放显存速度快但设备丢失时需要重建。D3DPOOL_MANAGED由 D3D 托管设备丢失后自动恢复代价是每帧可能有一次拷贝。新手建议先用MANAGED省去设备丢失处理的麻烦。填充顶点数据时用Lock和UnlockVOID* pVertices; g_pVB-Lock(0, 0, (void**)pVertices, 0); memcpy(pVertices, vertices, sizeof(vertices)); g_pVB-Unlock();Lock的第一个 0 表示从头锁第二个 0 表示锁全部。如果只改部分顶点可以传偏移和大小减少锁开销。3.2 纹理加载D3DX 辅助函数的参数与格式坑纹理加载用D3DXCreateTextureFromFile最省事但它对文件格式和尺寸有要求。常见翻车点加载 24 位 BMP 没问题加载带 Alpha 的 PNG 时如果没指定D3DX_DEFAULT以外的参数Alpha 通道可能被忽略。更稳的做法是用D3DXCreateTextureFromFileEx显式指定格式和 Mipmap 级别D3DXCreateTextureFromFileEx( g_pd3dDevice, Ltexture.png, D3DX_DEFAULT, // 宽度默认取文件宽度 D3DX_DEFAULT, // 高度 D3DX_DEFAULT, // Mipmap 级别默认全链 0, // 用途 D3DFMT_A8R8G8B8, // 显式指定 32 位带 Alpha D3DPOOL_MANAGED, D3DX_FILTER_NONE, // 过滤方式 D3DX_FILTER_NONE, 0, // 颜色键0 表示不透明 NULL, NULL, g_pTexture);D3DFMT_A8R8G8B8是兼容性最好的 32 位格式几乎所有显卡都支持。如果纹理尺寸不是 2 的幂老设备可能直接创建失败常见做法是在加载前把图片缩到 256x256 或 512x512。D3DX_FILTER_NONE表示不缩放如果尺寸不匹配改成D3DX_FILTER_LINEAR让 D3DX 帮你缩。3.3 矩阵设置世界、视图、投影的顺序与单位固定管线里顶点变换顺序是世界矩阵 → 视图矩阵 → 投影矩阵。设置时用SetTransform注意D3DTS_VIEW和D3DTS_PROJECTION要在BeginScene之后、绘制之前设好。视图矩阵用D3DXMatrixLookAtLH最直观D3DXMATRIX matView; D3DXVECTOR3 eye(0.0f, 0.0f, -5.0f); // 相机位置 D3DXVECTOR3 at(0.0f, 0.0f, 0.0f); // 看向原点 D3DXVECTOR3 up(0.0f, 1.0f, 0.0f); // 上方向 D3DXMatrixLookAtLH(matView, eye, at, up); g_pd3dDevice-SetTransform(D3DTS_VIEW, matView); D3DXMATRIX matProj; D3DXMatrixPerspectiveFovLH( matProj, D3DX_PI / 4.0f, // 45 度视野 800.0f / 600.0f, // 宽高比要和窗口一致 0.1f, // 近裁剪面 100.0f); // 远裁剪面 g_pd3dDevice-SetTransform(D3DTS_PROJECTION, matProj);D3DX_PI / 4.0f是 45 度改大视野更广但边缘畸变明显。宽高比如果和实际窗口不一致画面会被拉伸。近裁剪面别设 0否则深度精度会崩。世界矩阵用D3DXMatrixRotationY之类做旋转每帧更新就能看到三角形转起来。3.4 把示例改成自己的场景加一个旋转立方体在Tutorial01基础上加立方体步骤是定义 8 个顶点、12 个三角形索引、创建索引缓冲、每帧更新世界矩阵。索引缓冲用CreateIndexBuffer格式选D3DFMT_INDEX16顶点数少于 65536 时够用。绘制时用DrawIndexedPrimitiveg_pd3dDevice-SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); g_pd3dDevice-SetFVF(D3DFVF_CUSTOMVERTEX); g_pd3dDevice-SetIndices(g_pIB); g_pd3dDevice-DrawIndexedPrimitive( D3DPT_TRIANGLELIST, // 图元类型 0, // 基顶点索引 0, // 最小顶点索引 8, // 顶点数 0, // 起始索引 12); // 三角形数D3DPT_TRIANGLELIST表示每三个索引一个三角形。如果画出来缺面检查索引顺序是不是顺时针——D3D 默认剔除逆时针面SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW)可以改剔除方向。旋转用D3DXMatrixRotationY(matWorld, timeGetTime() / 1000.0f)每帧传新矩阵立方体就转起来了。4. 避坑与排查设备丢失、黑屏与编译报错4.1 现象窗口出来了但全黑没有任何几何体原因通常有三个相机在物体背面、投影矩阵宽高比错误、或者顶点被剔除了。先检查视图矩阵的eye和at如果eye和at重合LookAt会返回 NaN整个画面消失。再确认投影矩阵的宽高比和窗口客户区一致用GetClientRect拿实际尺寸。最后看剔除模式临时设D3DRS_CULLMODE为D3DCULL_NONE如果几何体出现说明索引绕序反了。4.2 现象AltTab 切换后画面卡死或报 D3DERR_DEVICELOST这是 D3D9 的经典设备丢失。Present返回D3DERR_DEVICELOST时不能继续渲染必须等TestCooperativeLevel返回D3DERR_DEVICENOTRESET然后释放所有D3DPOOL_DEFAULT资源调用Reset重建再重新创建资源。常见做法是把设备丢失处理写成一个状态机在消息循环里每帧检查。用D3DPOOL_MANAGED的资源不用手动重建但D3DPOOL_DEFAULT的顶点缓冲、纹理必须重建。4.3 现象编译报错 “无法打开包括文件: d3dx9.h”原因只装了 Windows SDK没装 DirectX SDK。解决下载 DirectX SDK June 2010安装后在工程属性 → VC 目录 → 包含目录里加上$(DXSDK_DIR)Include库目录加上$(DXSDK_DIR)Lib\x86。如果安装后DXSDK_DIR环境变量没生效重启 VS 或手动指定绝对路径。注意 64 位工程要用Lib\x64但 D3D9 示例基本都是 32 位别混。4.4 现象纹理加载失败返回 D3DXERR_INVALIDDATA原因文件路径含中文、图片格式不支持、或尺寸不是 2 的幂。解决先把图片放到英文路径下用画图或 PS 另存为 24 位 BMP 测试。如果 BMP 能加载而 PNG 不行检查是否缺d3dx9.lib的 PNG 支持——某些精简版 SDK 不带。尺寸问题用D3DXCreateTextureFromFileEx加D3DX_FILTER_LINEAR让 D3DX 自动缩放或者提前把图片改成 256x256。4.5 现象运行时报 “找不到 d3dx9_43.dll”原因目标机器没装 DirectX 运行时。解决在开发机上把d3dx9_43.dll和可执行文件放一起或者让用户装 DirectX End-User Runtime。注意d3dx9_43.dll对应的是 June 2010 SDK不同版本号不能混用。如果发布绿色版把 DLL 放同目录是最省事的做法但要注意 32/64 位匹配。5. 进阶技巧用 Effect 框架和调试层把示例吃透5.1 从固定管线切到 Effect 文件示例里有一部分用了.fx文件那是 Effect 框架把着色器、渲染状态、纹理绑定打包在一个文件里。相比散落的SetRenderStateEffect 的好处是状态集中管理改光照模型不用翻遍代码。加载 Effect 用D3DXCreateEffectFromFile然后Begin/BeginPass/CommitChanges/EndPass/End一套流程。我一般会先跑通固定管线版本再把顶点着色器和像素着色器抽到.fx里对比两者输出是否一致。如果 Effect 版本画面偏暗检查CommitChanges是否在BeginPass之后调用以及着色器里的矩阵是否转置——D3D9 默认行主序D3DXMatrixTranspose别忘了。5.2 打开调试层看详细报错DirectX SDK 自带调试运行时在控制面板 → DirectX 属性页里把“调试输出级别”拉到最大运行示例时 VS 输出窗口会打印资源泄漏、状态冲突、格式不支持等警告。常见的一条是“Texture created with D3DPOOL_DEFAULT but not released”提示你设备丢失时会漏资源。另一条是“DrawPrimitive called without vertex shader”说明固定管线和着色器混用了。这些信息比HRESULT返回值具体得多排查效率翻倍。5.3 用 PIX 抓帧看渲染流程PIX for Windows 是 SDK 里的抓帧工具能录一帧的所有 D3D 调用然后逐条回放。用法在 PIX 里启动示例 exe按抓帧快捷键然后看事件列表。重点看DrawIndexedPrimitive前后的状态设置确认纹理绑没绑、矩阵传没传、深度测试开没开。我遇到过画出来全白的情况PIX 里一看是SetTexture传了 NULL但代码里明明加载了纹理——最后发现是SetTexture的采样阶段索引写成了 1而像素着色器用的是阶段 0。这种问题看代码很难发现抓帧一眼就定位。5.4 一个具体技巧用宏切换调试与发布配置示例工程里经常有#ifdef DEBUG包起来的调试代码我习惯再加一个#define D3D_DEBUG_INFO放在d3d9.h包含之前这样创建资源时 D3D 会记录额外信息调试层能报出更详细的资源名。配合D3DXCreateEffectFromFile的D3DXSHADER_DEBUG标志着色器编译错误会带行号。发布时把这两个宏去掉性能不受影响。从那以后我每次改示例代码都强制走一遍“调试层 PIX 抓帧”再提交省得后面花时间猜黑屏原因。希望帮到你。本文还有配套的精品资源点击获取
返回列表