VS2022配置Dx11开发环境

基于《重庆大学出版社 – 基于DirectX 11的3D图形程序设计案例教程》这本书。书中使用的是Visual Studio 2012(好老的东西),现在Visual Studio 2022都已经用的很普遍了,所以难免会遇到一些问题。

安装Dx11 SDK

现在Windows系统和Visual StudioWindows SDK中好像默认都不自带Dx11的头文件和Lib库文件了,所以要自己去安装一个。有两种方案:

方案一:使用书上的方案 DX SDK(June 2010)

点击这里下载(http://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458E31/DXSDK_Jun10.exe)

直接打开安装程序按照书上的安装就好啦。

方案二:使用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.cppd3dCube.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里的着色器编译的文件名。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇