三角

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#ifndef UNICODE
#define UNICODE
#endif

#include <windows.h>
#include <d3d11.h>
#include <d3dcompiler.h>
#include <iostream>
#include <wrl/client.h>
#include <cmath>

#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3dcompiler.lib")

using Microsoft::WRL::ComPtr;

// 1. 全局资源指针(为了在回调函数中重绘)
ComPtr<ID3D11Device> g_device;
ComPtr<ID3D11DeviceContext> g_context;
ComPtr<IDXGISwapChain> g_swapChain;
ComPtr<ID3D11RenderTargetView> g_rtv;
ComPtr<ID3D11Buffer> g_vertexBuffer;
ComPtr<ID3D11Buffer> g_constantBuffer;
ComPtr<ID3D11VertexShader> g_vertexShader;
ComPtr<ID3D11PixelShader> g_pixelShader;
ComPtr<ID3D11InputLayout> g_inputLayout;

struct Vertex {
float x, y, z;
float r, g, b, a;
};

struct ConstantBuffer {
float matrix[4][4];
};

const char* shaderSource = R"(
cbuffer SceneData : register(b0) { matrix transform; };
struct VOut { float4 pos : SV_POSITION; float4 col : COLOR; };
VOut VShader(float4 pos : POSITION, float4 col : COLOR) {
VOut output;
output.pos = mul(pos, transform);
output.col = col;
return output;
}
float4 PShader(float4 pos : SV_POSITION, float4 col : COLOR) : SV_TARGET {
return col;
}
)";
void ShowFPS() {
static int frames = 0;
static double lastTime = GetTickCount64();
frames++;
double currentTime = GetTickCount64();
if (currentTime - lastTime >= 1000.0) {
std::cout << "\r当前渲染帧率 (FPS): " << frames << " " << std::flush;
frames = 0;
lastTime = currentTime;
}
}

// 2. 独立的渲染函数
void Render() {
if (!g_context || !g_rtv) return;

static float time = 0.0f;
time += 0.0005f;

// 更新旋转矩阵
ConstantBuffer cbData = {};
float s = sin(time), c = cos(time);
cbData.matrix[0][0] = c; cbData.matrix[0][1] = -s;
cbData.matrix[1][0] = s; cbData.matrix[1][1] = c;
cbData.matrix[2][2] = 1.0f; cbData.matrix[3][3] = 1.0f;

D3D11_MAPPED_SUBRESOURCE mapped;
g_context->Map(g_constantBuffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped);
memcpy(mapped.pData, &cbData, sizeof(cbData));
g_context->Unmap(g_constantBuffer.Get(), 0);

float bgColor[] = { 0.1f, 0.2f, 0.3f, 1.0f };
g_context->ClearRenderTargetView(g_rtv.Get(), bgColor);

g_context->OMSetRenderTargets(1, g_rtv.GetAddressOf(), nullptr);
UINT stride = sizeof(Vertex), offset = 0;
g_context->IASetVertexBuffers(0, 1, g_vertexBuffer.GetAddressOf(), &stride, &offset);
g_context->IASetInputLayout(g_inputLayout.Get());
g_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
g_context->VSSetShader(g_vertexShader.Get(), nullptr, 0);
g_context->VSSetConstantBuffers(0, 1, g_constantBuffer.GetAddressOf());
g_context->PSSetShader(g_pixelShader.Get(), nullptr, 0);

g_context->Draw(3, 0);
g_swapChain->Present(1, 0);
ShowFPS();
}

// 增加一个重置资源的函数
void ResizeD3D(int width, int height) {
if (!g_swapChain) return;

// 1. 必须先释放掉引用了后台缓冲的资源
g_rtv.Reset();

// 2. 调整缓冲区大小 (0 表示保持原有格式)
g_swapChain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);

// 3. 重新获取后台缓冲并创建 RTV
ComPtr<ID3D11Texture2D> pBackBuffer;
g_swapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_device->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &g_rtv);

// 4. 更新视口,否则画面会裁剪
D3D11_VIEWPORT vp = { 0, 0, (float)width, (float)height, 0.0f, 1.0f };
g_context->RSSetViewports(1, &vp);
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_SIZE:
// 当窗口大小改变时,wParam != SIZE_MINIMIZED 确保不是最小化
if (g_device && wParam != SIZE_MINIMIZED) {
int width = LOWORD(lParam);
int height = HIWORD(lParam);
ResizeD3D(width, height);
Render(); // 立即画一帧,防止拉伸白屏
}
return 0;

case WM_PAINT:
Render();
ValidateRect(hWnd, NULL);
return 0;

// 优化:处理窗口正在调整大小时的平滑度
case WM_ENTERSIZEMOVE:
// 可以在此处暂停某些逻辑
return 0;
case WM_EXITSIZEMOVE:
// 调整结束
return 0;
case WM_ERASEBKGND:
return 1; // 告诉系统:背景已处理,别乱擦我的屏
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

int main() {
system("chcp 65001");

// 注册 Unicode 窗口类
WNDCLASSW wc = { 0 };
wc.lpfnWndProc = WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = L"D3D11_Modern_Class";
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
RegisterClassW(&wc);

// 创建窗口(使用 L 前缀确保标题不被截断)
HWND hWnd = CreateWindowExW(0, L"D3D11_Modern_Class", L"Direct3D 11 旋转三角形 - Unicode 版",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr, nullptr, wc.hInstance, nullptr);

ShowWindow(hWnd, SW_SHOW);

// --- D3D11 初始化 ---
DXGI_SWAP_CHAIN_DESC sd = {};
sd.BufferCount = 1;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = hWnd;
sd.SampleDesc.Count = 1;
sd.Windowed = TRUE;

D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, 0, nullptr, 0,
D3D11_SDK_VERSION, &sd, &g_swapChain, &g_device, nullptr, &g_context);

ComPtr<ID3D11Texture2D> pBackBuffer;
g_swapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
g_device->CreateRenderTargetView(pBackBuffer.Get(), nullptr, &g_rtv);

// 编译 Shader
ComPtr<ID3DBlob> vsBlob, psBlob;
D3DCompile(shaderSource, strlen(shaderSource), nullptr, nullptr, nullptr, "VShader", "vs_4_0", 0, 0, &vsBlob, nullptr);
D3DCompile(shaderSource, strlen(shaderSource), nullptr, nullptr, nullptr, "PShader", "ps_4_0", 0, 0, &psBlob, nullptr);
g_device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &g_vertexShader);
g_device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &g_pixelShader);

// 输入布局
D3D11_INPUT_ELEMENT_DESC ied[] = {
{"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}
};
g_device->CreateInputLayout(ied, 2, vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &g_inputLayout);

// 顶点与常量缓冲
Vertex verts[] = { {0, 0.5f, 0, 1, 0, 0, 1}, {0.5f, -0.5f, 0, 0, 1, 0, 1}, {-0.5f, -0.5f, 0, 0, 0, 1, 1} };
D3D11_BUFFER_DESC vbd = { sizeof(verts), D3D11_USAGE_DEFAULT, D3D11_BIND_VERTEX_BUFFER, 0, 0, 0 };
D3D11_SUBRESOURCE_DATA vsd = { verts, 0, 0 };
g_device->CreateBuffer(&vbd, &vsd, &g_vertexBuffer);

D3D11_BUFFER_DESC cbd = { sizeof(ConstantBuffer), D3D11_USAGE_DYNAMIC, D3D11_BIND_CONSTANT_BUFFER, D3D11_CPU_ACCESS_WRITE, 0, 0 };
g_device->CreateBuffer(&cbd, nullptr, &g_constantBuffer);

D3D11_VIEWPORT vp = { 0, 0, 800, 600, 0.0f, 1.0f };
g_context->RSSetViewports(1, &vp);

// 消息循环
MSG msg = { 0 };
while (msg.message != WM_QUIT) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else {
Render(); // 正常渲染
}
}
return 0;
}

CMakeLists.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cmake_minimum_required(VERSION 3.20)

# 项目名称
project(ConsoleD3D11 LANGUAGES CXX)
# 关键:开启全局 Unicode 支持,防止标题截断
add_definitions(-DUNICODE -D_UNICODE)

if(MSVC)
add_compile_options("/utf-8")
endif()
# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 1. 寻找 Windows SDK 中的核心库
# d3d11.lib: 核心 API
# dxgi.lib: 交换链和硬件管理
# d3dcompiler.lib: HLSL 着色器编译
find_library(D3D11_LIB d3d11 REQUIRED)
find_library(DXGI_LIB dxgi REQUIRED)
find_library(D3DCOMPILER_LIB d3dcompiler REQUIRED)

# 2. 扫描源文件 (假设你的文件名是 main.cpp)
add_executable(${PROJECT_NAME} main.cpp)

# 3. 链接库到可执行文件
target_link_libraries(${PROJECT_NAME} PRIVATE
${D3D11_LIB}
${DXGI_LIB}
${D3DCOMPILER_LIB}
)
# 输出到项目下的 bin 文件夹
set_target_properties(${PROJECT_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin")
# 4. (可选) 设置控制台子系统
# 虽然 CMake 默认就是控制台,但这样可以确保链接器行为正确
set_target_properties(${PROJECT_NAME} PROPERTIES
WIN32_EXECUTABLE FALSE
)