基于《重庆大学出版社 – 基于DirectX 11的3D图形程序设计案例教程》这本书。书中使用的是Visual Studio 2012(好老的东西),现在Visual Studio 2022都已经用的很普遍了,所以难免会遇到一些问题。
安装Dx11 SDK
现在Windows系统和Visual Studio的Windows SDK中好像默认都不自带Dx11的头文件和Lib库文件了,所以要自己去安装一个。有两种方案:
方案一:使用书上的方案 DX SDK(June 2010)
直接打开安装程序按照书上的安装就好啦。
方案二:使用NuGet工具(这个好用多了)
打开一个项目后(要是还没有项目的话,先看下面的教程创建一个项目,然后再跳转回来这里看这个方案二),选择菜单栏里的“工具” - “NuGet包管理器” - “程序包管理器控制台”。

然后会打开一个控制台窗口

在这个 PM>提示符后面输入以下命令,然后回车:
NuGet\Install-Package Microsoft.DXSDK.D3DX -Version 9.29.952.8

然后等待它安装完,应该是会长这样。

创建项目
和Dx12一样,创建空项目。

然后自己起一个名字,点击创建项目。

接下来进行项目的配置
在项目中点击右键 – 属性

按顺序点击“链接器” -> “系统” -> “子系统” -> 选择”窗口” -> 按”确定”

(好了现在可以跳回去看安装DxSDK的部分了,一二三跳!)
配置项目
再次来到项目的属性窗口,这次要编辑附加库目录。

新建一个

填入以下内容:
$(SolutionDir)packages\Microsoft.DXSDK.D3DX.9.29.952.8\build\native\release\lib\x64
填完点确定,然后点确定。

写代码
在源文件中创建main.cpp。
main.cpp(点击展开)
// 包含基本的Windows头文件和Direct3D头文件
#include <windows.h>
#include <windowsx.h>
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx10.h>
// 包含Direct3D库文件(链接时需要)
#pragma comment (lib, "d3d11.lib")
#pragma comment (lib, "d3dx11.lib")
#pragma comment (lib, "d3dx10.lib")
// 定义屏幕分辨率
#define SCREEN_WIDTH 800 // 屏幕宽度
#define SCREEN_HEIGHT 600 // 屏幕高度
// 全局变量声明
IDXGISwapChain* swapchain; // 指向交换链接口的指针(负责前后缓冲区交换)
ID3D11Device* dev; // 指向Direct3D设备接口的指针(负责创建资源)
ID3D11DeviceContext* devcon; // 指向Direct3D设备上下文的指针(负责绘制命令)
ID3D11RenderTargetView* backbuffer; // 指向后台缓冲区的渲染目标视图(用于渲染输出)
ID3D11InputLayout* pLayout; // 指向输入布局的指针(定义顶点数据格式)
ID3D11VertexShader* pVS; // 指向顶点着色器的指针(处理顶点数据)
ID3D11PixelShader* pPS; // 指向像素着色器的指针(处理像素颜色)
ID3D11Buffer* pVBuffer; // 指向顶点缓冲区的指针(存储顶点数据)
// 背景颜色
const float ChztClearColor[] = { 0.5f, 0.2f, 0.6f, 1.0f };
// 定义单个顶点的结构体
// 包含位置信息(X,Y,Z)和颜色信息(RGBA)
struct VERTEX { FLOAT X, Y, Z; D3DXCOLOR Color; };
// 函数原型声明
void InitD3D(HWND hWnd); // 设置并初始化Direct3D
void RenderFrame(void); // 渲染单帧图像
void CleanD3D(void); // 关闭Direct3D并释放内存资源
void InitGraphics(void); // 创建要渲染的图形(顶点数据)
void InitPipeline(void); // 加载并准备着色器程序
// 窗口消息处理函数原型
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// Windows程序的入口点
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd; // 窗口句柄
WNDCLASSEX wc; // 窗口类结构体
// 初始化窗口类结构体为0
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// 填充窗口类信息
wc.cbSize = sizeof(WNDCLASSEX); // 结构体大小
wc.style = CS_HREDRAW | CS_VREDRAW; // 窗口样式(水平/垂直重绘)
wc.lpfnWndProc = WindowProc; // 消息处理函数
wc.hInstance = hInstance; // 应用实例句柄
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 鼠标光标样式
wc.lpszClassName = L"WindowClass"; // 窗口类名
// 注册窗口类
RegisterClassEx(&wc);
// 计算窗口客户区大小(考虑窗口边框)
RECT wr = { 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT }; // 初始客户区矩形
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // 调整为包含边框的窗口大小
// 创建窗口
hWnd = CreateWindowEx(NULL,
L"WindowClass", // 窗口类名
L"ChztDx11Demo", // 窗口标题
WS_OVERLAPPEDWINDOW, // 窗口样式(带边框、标题栏等)
300, // 窗口左上角X坐标
300, // 窗口左上角Y坐标
wr.right - wr.left, // 窗口宽度(包含边框)
wr.bottom - wr.top, // 窗口高度(包含边框)
NULL, // 父窗口句柄
NULL, // 菜单句柄
hInstance, // 应用实例句柄
NULL); // 附加数据
// 显示窗口
ShowWindow(hWnd, nCmdShow);
// 初始化Direct3D
InitD3D(hWnd);
// 进入主消息循环
MSG msg;
while (TRUE)
{
// 处理窗口消息(非阻塞方式)
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg); // 转换键盘消息(如WM_KEYDOWN到WM_CHAR)
DispatchMessage(&msg); // 将消息分发给WindowProc处理
// 如果收到退出消息,退出循环
if (msg.message == WM_QUIT)
break;
}
// 渲染一帧图像
RenderFrame();
}
// 清理DirectX和COM资源
CleanD3D();
return msg.wParam;
}
// 程序的主要消息处理函数
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY: // 窗口销毁消息
{
PostQuitMessage(0); // 发送退出消息,触发主循环退出
return 0;
} break;
}
// 处理其他未捕获的消息
return DefWindowProc(hWnd, message, wParam, lParam);
}
// 初始化并准备Direct3D以供使用
void InitD3D(HWND hWnd)
{
// 创建交换链描述结构体(用于配置交换链)
DXGI_SWAP_CHAIN_DESC scd;
// 清空结构体,避免垃圾数据
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
// 填充交换链描述信息
scd.BufferCount = 1; // 后台缓冲区数量(1个)
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 像素格式(32位颜色:红8位+绿8位+蓝8位+透明8位)
scd.BufferDesc.Width = SCREEN_WIDTH; // 后台缓冲区宽度(与屏幕宽度一致)
scd.BufferDesc.Height = SCREEN_HEIGHT; // 后台缓冲区高度(与屏幕高度一致)
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途(作为渲染目标输出)
scd.OutputWindow = hWnd; // 输出窗口句柄
scd.SampleDesc.Count = 4; // 多重采样数量(抗锯齿)
scd.Windowed = TRUE; // 窗口模式(TRUE为窗口,FALSE为全屏)
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // 允许全屏/窗口模式切换
// 创建D3D设备、设备上下文和交换链
D3D11CreateDeviceAndSwapChain(NULL,
D3D_DRIVER_TYPE_HARDWARE, // 使用硬件加速
NULL, // 软件驱动(硬件加速时为NULL)
NULL, // 设备创建标志(默认)
NULL, NULL, // 特性等级(默认)
D3D11_SDK_VERSION, // SDK版本
&scd, // 交换链描述
&swapchain, // 输出:交换链指针
&dev, // 输出:设备指针
NULL, // 输出:实际特性等级(默认忽略)
&devcon); // 输出:设备上下文指针
// 获取后台缓冲区的地址
ID3D11Texture2D* pBackBuffer;
swapchain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer);
// 使用后台缓冲区地址创建渲染目标视图
dev->CreateRenderTargetView(pBackBuffer, NULL, &backbuffer);
pBackBuffer->Release(); // 释放后台缓冲区纹理(已通过渲染目标视图引用)
// 将渲染目标设置为后台缓冲区
devcon->OMSetRenderTargets(1, &backbuffer, NULL);
// 设置视口(渲染区域)
D3D11_VIEWPORT viewport;
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); // 清空视口结构体
viewport.TopLeftX = 0; // 视口左上角X坐标
viewport.TopLeftY = 0; // 视口左上角Y坐标
viewport.Width = SCREEN_WIDTH; // 视口宽度
viewport.Height = SCREEN_HEIGHT; // 视口高度
// 应用视口设置
devcon->RSSetViewports(1, &viewport);
// 初始化渲染管线(着色器等)
InitPipeline();
// 初始化图形数据(顶点等)
InitGraphics();
}
// 用于渲染单帧图像的函数
void RenderFrame(void)
{
// 清空后台缓冲区为浅紫色(RGBA:0.5, 0.2, 0.6, 1.0)
devcon->ClearRenderTargetView(backbuffer, D3DXCOLOR(ChztClearColor[0], ChztClearColor[1], ChztClearColor[2], ChztClearColor[3]));
// 设置要绘制的顶点缓冲区
UINT stride = sizeof(VERTEX); // 每个顶点的字节大小
UINT offset = 0; // 顶点数据起始偏移量
devcon->IASetVertexBuffers(0, 1, &pVBuffer, &stride, &offset);
// 设置图元类型(这里使用三角形列表:每3个顶点组成一个三角形)
devcon->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 绘制顶点缓冲区中的顶点(3个顶点,从索引0开始)
devcon->Draw(3, 0);
// 交换前后缓冲区(将后台缓冲区的渲染结果显示到屏幕)
swapchain->Present(0, 0);
}
// 清理Direct3D和COM对象的函数
void CleanD3D(void)
{
swapchain->SetFullscreenState(FALSE, NULL); // 切换到窗口模式(退出全屏)
// 释放所有COM对象(必须按顺序释放,避免资源泄漏)
pLayout->Release(); // 释放输入布局
pVS->Release(); // 释放顶点着色器
pPS->Release(); // 释放像素着色器
pVBuffer->Release(); // 释放顶点缓冲区
swapchain->Release(); // 释放交换链
backbuffer->Release(); // 释放后台缓冲区渲染目标
dev->Release(); // 释放D3D设备
devcon->Release(); // 释放设备上下文
}
// 创建要渲染的图形(顶点数据)的函数
void InitGraphics()
{
// 使用VERTEX结构体定义一个三角形的3个顶点
// 每个顶点包含位置(X,Y,Z)和颜色(RGBA)
VERTEX OurVertices[] =
{
{0.0f, 0.5f, 0.0f, D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f)}, // 顶部顶点(红色)
{0.45f, -0.5, 0.0f, D3DXCOLOR(0.0f, 1.0f, 0.0f, 1.0f)}, // 右下角顶点(绿色)
{-0.45f, -0.5f, 0.0f, D3DXCOLOR(0.0f, 0.0f, 1.0f, 1.0f)} // 左下角顶点(蓝色)
};
// 创建顶点缓冲区描述结构体
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd)); // 清空结构体
bd.Usage = D3D11_USAGE_DYNAMIC; // 缓冲区用途(CPU和GPU均可写入)
bd.ByteWidth = sizeof(VERTEX) * 3; // 缓冲区总大小(3个顶点)
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER; // 绑定类型(顶点缓冲区)
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; // CPU访问权限(允许写入)
// 创建顶点缓冲区
dev->CreateBuffer(&bd, NULL, &pVBuffer); // 根据描述创建缓冲区
// 将顶点数据复制到缓冲区
D3D11_MAPPED_SUBRESOURCE ms; // 用于映射缓冲区的结构体
// 映射缓冲区到CPU可访问的内存
devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
memcpy(ms.pData, OurVertices, sizeof(OurVertices)); // 复制顶点数据到缓冲区
devcon->Unmap(pVBuffer, NULL); // 取消映射(恢复GPU访问)
}
// 加载并准备着色器的函数
void InitPipeline()
{
// 加载并编译顶点着色器和像素着色器
ID3D10Blob* VS, * PS; // 用于存储编译后的着色器二进制数据
// 编译顶点着色器(从文件加载,入口函数为VShader,目标版本vs_4_0)
D3DX11CompileFromFile(L"shaders.hlsl", 0, 0, "VShader", "vs_4_0", 0, 0, 0, &VS, 0, 0);
// 编译像素着色器(从文件加载,入口函数为PShader,目标版本ps_4_0)
D3DX11CompileFromFile(L"shaders.hlsl", 0, 0, "PShader", "ps_4_0", 0, 0, 0, &PS, 0, 0);
// 将编译后的着色器封装为着色器对象
dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS);
dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);
// 设置当前使用的着色器
devcon->VSSetShader(pVS, 0, 0); // 设置顶点着色器
devcon->PSSetShader(pPS, 0, 0); // 设置像素着色器
// 创建输入布局(定义顶点数据如何传递给顶点着色器)
D3D11_INPUT_ELEMENT_DESC ied[] =
{
// 位置属性:语义名为POSITION,格式为32位浮点(X,Y,Z),从顶点数据起始位置开始
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
// 颜色属性:语义名为COLOR,格式为32位浮点(R,G,B,A),从顶点数据第12字节开始(3个float=12字节)
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
// 根据顶点着色器的输入签名创建输入布局
dev->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
// 设置当前使用的输入布局
devcon->IASetInputLayout(pLayout);
}
然后可以创建一个筛选器专门用来放我们的Shader。

创建完如下

现在在这里面创建一个shaders.hlsl

文件内容如下:
shaders.hlsl(点击展开)
struct VOut
{
float4 position : SV_POSITION;
float4 color : COLOR;
};
VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
VOut output;
output.position = position;
output.color = color;
return output;
}
float4 PShader(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
{
return color;
}
然后还要修改一下它的属性

把项类型改成“不参与生成”

运行!
不出意外应该会有一个窗口出现

这就说明DxSDK配置没有问题。
使用完整的Effect框架
现在要新建一个项目,按照上面的教程安装DxSDK,然后配置项目,但是不用写上面的代码!只要照着下面的教程把Effect框架也装好配置好就可以(抄)写cube的代码了
配置Effect框架
同样提供两种方案:
方案一:书上写的
请参考书本第1.4节和第6.2节的内容。
方案二:使用NuGet工具(这个真是太好用啦)
参考以上,打开NuGet的命令行窗口。输入以下命令
NuGet\Install-Package fx11_desktop_2019 -Version 2022.5.24.5
等待安装完毕

安装完毕后,同样要到项目配置的附加库目录中,添加Effect框架的库文件。

添加如下代码
$(SolutionDir)packages\fx11_desktop_2019.2022.5.24.5\native\lib\x64\Release

然后确定,然后确定
写代码
接下来和书上写的一样,创建头文件d3dUtility.h;源文件d3dUtility.cpp、d3dCube.cpp;着色器源代码文件SimpleShader.fx。然后悲伤的发现搞了好久也没有画出Cube呜呜呜呜呜~
所以只能不按书上的来了。
我们创建源文件cube.cpp;着色器源代码文件cube.fx
注意,着色器的源代码文件都要设置成“不参与生成”!
文件内容如下:
cube.cpp(点击展开)
#include <Windows.h>
// 数学库相关头文件,【Chzt注】XNA已过时,改为使用DirectXMath
#include <d3dcompiler.h>
#include <DirectXMath.h>
using namespace DirectX;
// DirectX11相关头文件
#include <d3d11.h>
#include <d3dx11.h>
#include <d3dx11effect.h> // 新增Effect框架头文件
// DirectX11相关库
#pragma comment(lib,"Effects11.lib") // 新增Effect框架库文件
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib,"d3dx11.lib")
#pragma comment(lib,"d3dcompiler.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib,"winmm.lib")
// ------------------------------
// 工具宏定义
// ------------------------------
// HR宏:用于简化HRESULT错误处理
// 参数:x - 要执行的HRESULT函数调用
// 功能:执行函数调用,检查返回值,若失败则显示详细错误信息
#define HR(x) { HRESULT hr = (x); if (FAILED(hr)) { \
char errMsg[256]; \
MessageBoxA(g_hWnd, errMsg, "DirectX错误", MB_OK | MB_ICONERROR); } }
// ReleaseCOM宏:用于安全释放COM对象
// 参数:x - 指向COM对象的指针
// 功能:检查指针是否为空,不为空则调用Release()方法并将指针置为nullptr
#define ReleaseCOM(x) { if (x) { x->Release(); x = nullptr; } }
// ------------------------------
// 颜色定义命名空间
// ------------------------------
// 功能:提供常用预定义颜色,用于简化代码书写
// 说明:所有颜色均为RGBA格式,范围[0, 1]
namespace Colors
{
const XMFLOAT4 White = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);
const XMFLOAT4 Black = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
const XMFLOAT4 Red = XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f);
const XMFLOAT4 Green = XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f);
const XMFLOAT4 Blue = XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f);
const XMFLOAT4 Yellow = XMFLOAT4(1.0f, 1.0f, 0.0f, 1.0f);
const XMFLOAT4 Cyan = XMFLOAT4(0.0f, 1.0f, 1.0f, 1.0f);
const XMFLOAT4 Magenta = XMFLOAT4(1.0f, 0.0f, 1.0f, 1.0f);
const XMFLOAT4 LightSteelBlue = XMFLOAT4(0.69f, 0.77f, 0.87f, 1.0f);
}
// ------------------------------
// 顶点结构体定义
// ------------------------------
// 功能:定义立方体顶点的数据结构
// 说明:与着色器文件(Cube.fx)中的VertexIn结构严格对应,确保数据格式匹配
struct Vertex
{
XMFLOAT3 Pos; // 空间位置(DirectXMath兼容类型)
XMFLOAT4 Color; // 顶点颜色
};
// ------------------------------
// 全局变量声明
// ------------------------------
// DirectX 11核心设备和资源
ID3D11Device* g_pD3DDevice = nullptr; // D3D11设备指针,用于创建各种DirectX资源
ID3D11DeviceContext* g_pD3DContext = nullptr; // D3D11设备上下文指针,用于执行渲染命令
IDXGISwapChain* g_pSwapChain = nullptr; // 交换链指针,负责将渲染结果显示到屏幕
ID3D11RenderTargetView* g_pRenderTargetView = nullptr; // 渲染目标视图,用于颜色缓冲
ID3D11DepthStencilView* g_pDepthStencilView = nullptr; // 深度模板视图,用于深度测试
// 几何体资源
ID3D11Buffer* g_pVertexBuffer = nullptr; // 顶点缓冲,存储立方体的顶点数据
ID3D11Buffer* g_pIndexBuffer = nullptr; // 索引缓冲,存储立方体的三角形索引
// Effect框架资源
ID3DX11Effect* g_pFX = nullptr; // Effect对象,管理着色器资源和常量缓冲区
ID3DX11EffectTechnique* g_pTech = nullptr; // Technique对象,定义完整的渲染技术
ID3DX11EffectMatrixVariable* g_pFXWorldViewProj = nullptr; // 世界-视图-投影矩阵变量
ID3D11InputLayout* g_pInputLayout = nullptr; // 输入布局,定义顶点数据如何传递给着色器
// 背景色(淡紫色,想想看,这是为什么?hhh)
const float ChztClearColor[] = { 0.5f, 0.2f, 0.6f, 1.0f };
// 矩阵和相机参数
XMFLOAT4X4 g_World; // 世界变换矩阵,用于定位和旋转模型
XMFLOAT4X4 g_View; // 视图变换矩阵,模拟相机位置和方向
XMFLOAT4X4 g_Proj; // 投影变换矩阵,将3D场景投影到2D屏幕
float g_Radius = 5.0f; // 相机环绕半径,控制相机与模型的距离
float g_Phi = XM_PIDIV4; // 相机俯仰角,控制相机的上下观察角度
float g_Theta = 0.0f; // 相机方位角,控制相机的水平观察角度
// 窗口和程序状态
HWND g_hWnd = nullptr; // 主窗口句柄,用于消息处理和渲染输出
bool g_Running = true; // 程序运行状态标志,控制主循环
// ------------------------------
// 函数声明
// ------------------------------
HRESULT InitD3D(); // 初始化DirectX 11设备和资源
void CreateVertexBuffer(); // 创建立方体的顶点缓冲
void CreateIndexBuffer(); // 创建立方体的索引缓冲
void CompileShader(); // 编译着色器文件
void CreateInputLayout(); // 创建顶点输入布局
void UpdateScene(float dt); // 更新场景状态(相机位置、模型旋转等)
void DrawScene(); // 绘制场景
void CleanupD3D(); // 清理DirectX资源
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); // 窗口消息处理函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd); // 程序入口点
// ------------------------------
// 窗口消息处理函数
// ------------------------------
// 功能:处理所有窗口消息,包括关闭窗口、键盘输入等
// 参数:
// hWnd - 窗口句柄
// msg - 消息类型
// wParam - 消息参数1
// lParam - 消息参数2
// 返回值:
// 消息处理结果,对于未处理的消息返回DefWindowProc的结果
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY: // 窗口销毁消息
PostQuitMessage(0); // 发送退出消息
g_Running = false; // 设置运行标志为false,退出主循环
break;
case WM_KEYDOWN: // 键盘按键按下消息
if (wParam == VK_ESCAPE) // 如果按下ESC键
DestroyWindow(hWnd); // 销毁窗口
break;
default: // 其他未处理的消息
return DefWindowProc(hWnd, msg, wParam, lParam); // 调用默认消息处理函数
}
return 0;
}
// ------------------------------
// 初始化DirectX 11设备和资源
// ------------------------------
// 功能:初始化DirectX 11设备、交换链、渲染目标视图和深度模板视图
// 并创建立方体所需的几何体资源和着色器资源
// 返回值:
// S_OK - 初始化成功
// 其他HRESULT值 - 初始化失败,包含具体错误代码
HRESULT InitD3D()
{
// 计算调整后的窗口尺寸
RECT rc = { 0, 0, 800, 600 }; // 期望的客户区尺寸
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE); // 调整为窗口尺寸(包含边框)
UINT width = rc.right - rc.left; // 调整后的窗口宽度
UINT height = rc.bottom - rc.top; // 调整后的窗口高度
// 配置交换链描述结构体
DXGI_SWAP_CHAIN_DESC scd = {}; // 初始化为全零
scd.BufferCount = 1; // 缓冲区数量,使用1个缓冲区
scd.BufferDesc.Width = width; // 缓冲区宽度
scd.BufferDesc.Height = height; // 缓冲区高度
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 缓冲区格式:RGBA 8位无符号整数
scd.BufferDesc.RefreshRate.Numerator = 60; // 刷新率分子:60
scd.BufferDesc.RefreshRate.Denominator = 1; // 刷新率分母:1(即60Hz)
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 缓冲区用途:渲染目标输出
scd.OutputWindow = g_hWnd; // 输出窗口句柄
scd.SampleDesc.Count = 1; // 多重采样计数:1(无抗锯齿)
scd.Windowed = TRUE; // 窗口模式:TRUE
scd.Flags = DXGI_SWAP_EFFECT_DISCARD; // 交换效果:丢弃旧缓冲区
// 创建设备和交换链
D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0 }; // 只请求DirectX 11.0特性级别
HR(D3D11CreateDeviceAndSwapChain(
nullptr, // 默认适配器
D3D_DRIVER_TYPE_HARDWARE, // 使用硬件加速
nullptr, // 软件驱动程序句柄
0, // 创建设备标志
featureLevels, // 支持的特性级别列表
1, // 特性级别数量
D3D11_SDK_VERSION, // SDK版本
&scd, // 交换链描述
&g_pSwapChain, // 返回的交换链指针
&g_pD3DDevice, // 返回的设备指针
nullptr, // 返回的实际特性级别(未使用)
&g_pD3DContext // 返回的设备上下文指针
));
// 创建渲染目标视图
ID3D11Texture2D* pBackBuffer = nullptr; // 后缓冲区纹理指针
HR(g_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBuffer)); // 获取后缓冲区
HR(g_pD3DDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_pRenderTargetView)); // 创建渲染目标视图
ReleaseCOM(pBackBuffer); // 释放后缓冲区纹理指针
// 创建深度缓冲
D3D11_TEXTURE2D_DESC dsd = {};
dsd.Width = width;
dsd.Height = height;
dsd.MipLevels = 1;
dsd.ArraySize = 1;
dsd.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
dsd.SampleDesc.Count = 1;
dsd.Usage = D3D11_USAGE_DEFAULT;
dsd.BindFlags = D3D11_BIND_DEPTH_STENCIL;
ID3D11Texture2D* pDepthBuffer = nullptr;
HR(g_pD3DDevice->CreateTexture2D(&dsd, nullptr, &pDepthBuffer));
HR(g_pD3DDevice->CreateDepthStencilView(pDepthBuffer, nullptr, &g_pDepthStencilView));
ReleaseCOM(pDepthBuffer);
// 绑定渲染目标和深度缓冲
g_pD3DContext->OMSetRenderTargets(1, &g_pRenderTargetView, g_pDepthStencilView);
// 设置视口
D3D11_VIEWPORT vp = {};
vp.Width = (float)width;
vp.Height = (float)height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
g_pD3DContext->RSSetViewports(1, &vp);
// 使用默认的光栅化状态,不创建线框模式
// 初始化矩阵(DirectXMath原生函数)
XMStoreFloat4x4(&g_World, XMMatrixIdentity());
XMStoreFloat4x4(&g_Proj, XMMatrixPerspectiveFovLH(XM_PIDIV4, (float)width / (float)height, 0.1f, 100.0f));
// 初始化核心资源
CreateVertexBuffer();
CreateIndexBuffer();
CompileShader();
CreateInputLayout();
return S_OK;
}
// 创建顶点缓冲(立方体8个顶点)
void CreateVertexBuffer()
{
Vertex vertices[] =
{
{ XMFLOAT3(-1.0f, -1.0f, -1.0f), Colors::White },
{ XMFLOAT3(-1.0f, 1.0f, -1.0f), Colors::Black },
{ XMFLOAT3(1.0f, 1.0f, -1.0f), Colors::Red },
{ XMFLOAT3(1.0f, -1.0f, -1.0f), Colors::Green },
{ XMFLOAT3(-1.0f, -1.0f, 1.0f), Colors::Blue },
{ XMFLOAT3(-1.0f, 1.0f, 1.0f), Colors::Yellow },
{ XMFLOAT3(1.0f, 1.0f, 1.0f), Colors::Cyan },
{ XMFLOAT3(1.0f, -1.0f, 1.0f), Colors::Magenta }
};
D3D11_BUFFER_DESC vbd = {};
vbd.Usage = D3D11_USAGE_IMMUTABLE;
vbd.ByteWidth = sizeof(Vertex) * 8;
vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
D3D11_SUBRESOURCE_DATA vinitData = {};
vinitData.pSysMem = vertices;
HR(g_pD3DDevice->CreateBuffer(&vbd, &vinitData, &g_pVertexBuffer));
}
// 创建索引缓冲(36个索引,三角形列表)
void CreateIndexBuffer()
{
UINT indices[] = {
0,1,2, 0,2,3, // 前表面
4,6,5, 4,7,6, // 后表面
4,5,1, 4,1,0, // 左表面
3,2,6, 3,6,7, // 右表面
1,5,6, 1,6,2, // 上表面
4,0,3, 4,3,7 // 下表面
};
D3D11_BUFFER_DESC ibd = {};
ibd.Usage = D3D11_USAGE_IMMUTABLE;
ibd.ByteWidth = sizeof(UINT) * 36;
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
D3D11_SUBRESOURCE_DATA iinitData = {};
iinitData.pSysMem = indices;
HR(g_pD3DDevice->CreateBuffer(&ibd, &iinitData, &g_pIndexBuffer));
}
// 编译着色器(依赖d3dcompiler.lib和Effects11.lib)
void CompileShader()
{
DWORD shaderFlags = 0;
#if defined(DEBUG) || defined(_DEBUG)
shaderFlags |= D3D10_SHADER_DEBUG | D3D10_SHADER_SKIP_OPTIMIZATION;
#endif
ID3D10Blob* pCompiledShader = nullptr;
ID3D10Blob* pErrorMsgs = nullptr;
// 编译ColorEffect.fx着色器文件
HR(D3DX11CompileFromFile(
L"cube.fx", nullptr, nullptr, nullptr, "fx_5_0",
shaderFlags, 0, nullptr, &pCompiledShader, &pErrorMsgs, nullptr
));
// 输出编译错误信息
if (pErrorMsgs)
{
MessageBoxA(g_hWnd, (char*)pErrorMsgs->GetBufferPointer(), "Shader Compile Error", MB_OK | MB_ICONERROR);
ReleaseCOM(pErrorMsgs);
}
// 创建Effect对象(依赖d3dx11effect.h和Effects11.lib)
HR(D3DX11CreateEffectFromMemory(
pCompiledShader->GetBufferPointer(), pCompiledShader->GetBufferSize(),
0, g_pD3DDevice, &g_pFX
));
ReleaseCOM(pCompiledShader);
// 获取着色器中的Technique和矩阵变量
g_pTech = g_pFX->GetTechniqueByName("ColorTech");
g_pFXWorldViewProj = g_pFX->GetVariableByName("gWorldViewProj")->AsMatrix();
}
// 创建输入布局(匹配顶点结构体与着色器输入)
void CreateInputLayout()
{
D3D11_INPUT_ELEMENT_DESC vertexDesc[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
D3DX11_PASS_DESC passDesc;
g_pTech->GetPassByIndex(0)->GetDesc(&passDesc);
HR(g_pD3DDevice->CreateInputLayout(
vertexDesc, 2, passDesc.pIAInputSignature,
passDesc.IAInputSignatureSize, &g_pInputLayout
));
}
// 更新场景(DirectXMath矩阵运算,无额外依赖)
void UpdateScene(float dt)
{
// 摄像机球面运动(三角函数使用标准C++数学库)
g_Theta += 1.0f * dt;
// 限制俯仰角在π/6到5π/6之间(使用标准clamp实现)
const float piDiv6 = XM_PI / 6.0f;
const float fivePiDiv6 = 5.0f * XM_PI / 6.0f;
g_Phi = max(piDiv6, min(fivePiDiv6, g_Phi));
float x = g_Radius * sinf(g_Phi) * cosf(g_Theta);
float z = g_Radius * sinf(g_Phi) * sinf(g_Theta);
float y = g_Radius * cosf(g_Phi);
// 计算视图矩阵
// 创建临时XMFLOAT3变量,然后再加载到XMVECTOR
XMFLOAT3 posVec = XMFLOAT3(x, y, z);
XMFLOAT3 upVec = XMFLOAT3(0.0f, 1.0f, 0.0f);
XMVECTOR pos = XMLoadFloat3(&posVec);
XMVECTOR target = XMVectorZero();
XMVECTOR up = XMLoadFloat3(&upVec);
XMStoreFloat4x4(&g_View, XMMatrixLookAtLH(pos, target, up));
// 计算世界*视图*投影矩阵
XMMATRIX world = XMLoadFloat4x4(&g_World);
XMMATRIX view = XMLoadFloat4x4(&g_View);
XMMATRIX proj = XMLoadFloat4x4(&g_Proj);
XMMATRIX wvp = world * view * proj;
// 更新着色器矩阵变量
// 将XMMATRIX转换为XMFLOAT4X4,然后传递地址
XMFLOAT4X4 wvpMatrix;
XMStoreFloat4x4(&wvpMatrix, wvp);
g_pFXWorldViewProj->SetMatrix(reinterpret_cast<float*>(&wvpMatrix));
}
// 绘制场景(DirectX11核心渲染流程)
void DrawScene()
{
// 清屏(渲染目标+深度缓冲)
// g_pD3DContext->ClearRenderTargetView(g_pRenderTargetView, &Colors::LightSteelBlue.x);
g_pD3DContext->ClearRenderTargetView(g_pRenderTargetView, ChztClearColor);
g_pD3DContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
// 设置渲染状态
g_pD3DContext->IASetInputLayout(g_pInputLayout);
g_pD3DContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// 使用默认的填充模式,不设置线框模式
g_pD3DContext->RSSetState(nullptr);
// 绑定顶点缓冲
UINT stride = sizeof(Vertex);
UINT offset = 0;
g_pD3DContext->IASetVertexBuffers(0, 1, &g_pVertexBuffer, &stride, &offset);
// 绑定索引缓冲
g_pD3DContext->IASetIndexBuffer(g_pIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
// 执行渲染(遍历所有Pass)
D3DX11_TECHNIQUE_DESC techDesc;
g_pTech->GetDesc(&techDesc);
for (UINT p = 0; p < techDesc.Passes; p++)
{
g_pTech->GetPassByIndex(p)->Apply(0, g_pD3DContext);
g_pD3DContext->DrawIndexed(36, 0, 0);
}
// 交换缓冲区,显示渲染结果
g_pSwapChain->Present(0, 0);
}
// 清理所有D3D资源(避免内存泄漏)
void CleanupD3D()
{
ReleaseCOM(g_pInputLayout);
ReleaseCOM(g_pFX);
ReleaseCOM(g_pIndexBuffer);
ReleaseCOM(g_pVertexBuffer);
ReleaseCOM(g_pDepthStencilView);
ReleaseCOM(g_pRenderTargetView);
ReleaseCOM(g_pSwapChain);
ReleaseCOM(g_pD3DContext);
ReleaseCOM(g_pD3DDevice);
}
// 主函数(程序入口)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// 注册窗口类
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"ChztDx11EffectCubeDemo";
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
RegisterClassEx(&wc);
// 创建窗口
g_hWnd = CreateWindowEx(
0, L"ChztDx11EffectCubeDemo", L"ChztDx11EffectCubeDemo",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
800, 600, nullptr, nullptr, hInstance, nullptr
);
ShowWindow(g_hWnd, nShowCmd);
UpdateWindow(g_hWnd);
// 初始化D3D,失败则弹窗提示
if (FAILED(InitD3D()))
{
MessageBox(g_hWnd, L"D3D Initialization Failed!", L"Error", MB_OK | MB_ICONERROR);
return 1;
}
// 消息循环+渲染循环
MSG msg = {};
LARGE_INTEGER freq, prev, curr;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&prev);
while (g_Running)
{
// 处理窗口消息
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
// 计算帧间隔时间(deltaTime)
QueryPerformanceCounter(&curr);
float dt = (curr.QuadPart - prev.QuadPart) / (float)freq.QuadPart;
prev = curr;
// 更新场景+绘制场景
UpdateScene(dt);
DrawScene();
}
}
// 清理资源+注销窗口类
CleanupD3D();
UnregisterClass(L"D3D11CubeDemo", hInstance);
return 0;
}
cube.fx(点击展开)
// 常量缓冲区(与C++矩阵变量对应)
cbuffer cbPerObject
{
float4x4 gWorldViewProj;
};
// 顶点输入结构体(匹配C++ Vertex)
struct VertexIn
{
float3 PosL : POSITION; // 局部空间位置
float4 Color : COLOR; // 顶点颜色
};
// 顶点输出结构体(传递给像素着色器)
struct VertexOut
{
float4 PosH : SV_POSITION; // 齐次剪裁空间位置
float4 Color : COLOR; // 插值后的颜色
};
// 顶点着色器(DirectXMath兼容的坐标变换)
VertexOut VS(VertexIn vin)
{
VertexOut vout;
vout.PosH = mul(float4(vin.PosL, 1.0f), gWorldViewProj);
vout.Color = vin.Color;
return vout;
}
// 像素着色器(返回插值颜色)
float4 PS(VertexOut pin) : SV_Target
{
return pin.Color;
}
// 渲染技术(单个渲染通道)
technique11 ColorTech
{
pass P0
{
SetVertexShader(CompileShader(vs_5_0, VS()));
SetPixelShader(CompileShader(ps_5_0, PS()));
}
}
再运行!
好的,不出意外的话应该能看到这个五颜六色的丑丑的cube在转转了。

注意!这个cube的代码和三角形的代码是两个独立的!
也就是说如果要在第一个项目里运行这个cube就直接把新代码覆盖到旧的就好。但是要注意main.cpp里的着色器编译的文件名。