2.ImGui-搭建一个外部绘制的窗口环境(使用ImGui绘制一个空白窗口)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!
本次游戏没法给
内容参考于:微尘网络安全
上一个内容:1.ImGui-环境安装
本章的步骤会有很多,不需要记住,会抄会用就行了别的真的不需要知道,知识很多不要把时间浪费在记这种无用知识,不要把时间浪费在去了解这种知识上,只需要记住在这里可以得到答案就行,本文章不会下线,忘了就回来看,知识很多千万不要浪废时间
本次使用Visual Studio 2022上一个内容中有Visual Studio 2017的安装,2022和2017安装的步骤都一样,安装包在置顶文章的百度网盘里也有。
首先打开 Visual Studio 2022,然后·点击下图红框的创建新项目,本章的代码说明,放到了最后,这之间的过程只需要抄就行(不抄会找不到文件),不需要关心代码有什么用,代码的用处说明全部都放到了最后
选择下图红框的空项目,然后点击下一步
然后输入一个项目名,然后点击创建,以后绘制的代码都写在它里面
然后再看ImGui给的Directx11的实例,它加载了下图红框的文件,所以我们也要加载
然后再回到我们刚创建的ImGuiOutTest项目里,如下图右击源文件,选择新建项
创建一个main.cpp文件,然后点击添加
添加好main.cpp后先给它里面添加下图红框的内容
int main() {return 0;
}
然后鼠标右击选择打开文件夹
然后就能来到我们项目的文件目录里,如下图,下图是后补的所以有很多文件,跟着下方的步骤来就可以
然后把ImGui的文件复制到我们的(ImGuiOutTest)项目里,把下图红框文件复制过去
然后再把文件拖到源文件里
拖完之后
然后再把下图红框的4个文件复制到我们的项目里,复制之前为了好区分先创建一个ImGui的文件夹
如下图创建ImGui文件夹
然后把那4个文件夹都放进去
复制完后来到vs2022里,如下图新建一个筛选器
名字叫ImGui,如下图红框
然后把这4个文件拖进去
拖动完之后,就可以了
然后开始复制代码,首先把下图红框的代码复制过去
复制过来后,它就会报错,这是因为没有引入需要的文件
然后再创建一个文件,如下图右击头文件,然后选择新建项
然后如下图添加一个main.h文件
创建完后
然后在main.cpp添加下图红框的代码 #include "main.h"
然后在main.h文件里添加 #include "windows.h"
再回到main.cpp文件里,可以看到有些代码就不报错了
然后鼠标左键单击下图红框位置,然后按F1
就可以打开微软对于 WNDCLASSEXW 的说明,其它的说明比如 CreateWindowW 也是这样的看法,这说明看了也没用,能看懂的不用看,看不懂的不用看,跟着我的代码抄和描述来学就行了,不用管它,说明不是给新手看的
然后点击下图红框
可以查看第二个参数的说明和能写的值有哪些,如下图红框,看不懂就去问ai大模型
然后还缺下图红框的代码
然后来到ImGui的代码,按着CTRL键鼠标左键单机下图红框
就可以跳转到WndProc函数里,把它们复制过去
然后它就不会报错了,如下图红框,注意WndProc函数,要在main函数的上方,如果放到了main函数的下方,main函数里面它还是会找不到WndProc
然后再把下图红框的三行代码复制一下
复制到main.h里,可以看到它会报错,找不到文件
因为它俩在ImGui筛选器里,如下图红框
然后可以看到main函数里已经不报错了,但是WindProc函数里还有报错,所以接下来处理WindProc函数中的报错
再来到ImGui的源码中,如下图红框位置创建了 ImGui_ImplWin32_WndProcHandler,所以要把它复制过去
然后复制完就可以了,但还是有两个位置报错 g_ResizeWidth 和 g_ResizeHeight接下来找它们
然后按着CTRL鼠标左键单机下图红框位置
然后把下图红框位置的代码复制过去
然后就不报错了,然后就可以注册一个窗口类,然后创建一个窗口了
点击下图红框尝试运行
然后它会报错,这里点击否
错误说明是找不到imgui.h文件,然后双击下图红框任意一个
然后它出错的文件是 imgui_impl_dx11.cpp,然后它在ImGui筛选器里,然后imgui.h在源文件筛选器里,解决这个文件就要设置包含目录
如下图鼠标右击ImGuiOutTest,然后选择属性
然后根据下图进行点击
单击下图红框
然后再单击下图红框
然后找到imgui.h文件的目录,找到后点击选择文件夹
如下图,然后点击确定
然后点击应用,再点击确定,也可以直接点击确定(确定包含的应用)
然后就不会报错了
然后再次运行
然后可以运行了,但是没有窗口,这样原因是,没有执行显示窗口,所以接下来继续复制代码
然后创建D3D
复制之后又有一大堆错误,这是因为缺少d3d的引入
引入d3d,把下图红框的代码复制过去
复制后
然后还剩下图红框位置在报错,然后继续找它们
然后把下图红框的代码复制过去
然后下图红框位置就不报错了
然后再把下图红框的代码复制过去
然后下图黄框位置就不报错了
然后再把下图红框位置复制过去
然后还剩下图红框位置报错,接着找它们
把下图红框的代码复制过去
复制完就不报错了
再次运行,它还会错误
错误说明是无法解析的符号,双击也没有反应,这个错误是,我们只引用了d3d的声明(声明就是告诉我们有这样一个东西,但是东西在哪它不告诉我们),没有引入d3d的库(这个库是告诉我们东西在什么位置,声明和库是分开的,它俩配合起来才能正常运行)D3D的库需要我们自己手动引入
鼠标右击选择属性
然后找到链接器里的输入,然后在附加依赖项里写d3d11.lib;,然后点击确定
为什么写d3d11.lib;,如下图来到ImGui的源码,然后右击 example_win32_directx11,然后选择属性
然后如下图红框,它的链接器里的输入就写的d3d11.lib;,所以我们也写d3d11.lib;
然后就可以运行了
然后继续抄,直到抄到可以画出一个东西,然后该复制下图红框的代码了,它是来显示窗口了
复制过来没有报错,如果复制后它是白色的,可以尝试把文件关闭重新打开,这是vs的问题,不用管
然后继续抄
它也没有报错
然后继续
它也没有报错
然后继续
然后也没有报错
继续抄
然后也没有报错
继续抄,一个循环
然后复制之后如下图红框
然后再运行,可以看到一个卡死的白色的窗口了
这个窗口是卡死的没法用然后继续抄,这是WIndows系统的消息循环,消息循环就是我们鼠标的移动,键盘的按下,都是一个消息循环中的消息,通过消息可以得到鼠标移动的位置,键盘按下的键等
然后也没有报错
继续复制
然后这有报错了
然后复制下图红框的代码
复制完就不会报错了
然后复制下图红框的内容
复制之后
再次运行,如下图可以得到一个蓝色背景的窗口了
然后把下图红框的代码复制过去,这是运行完(关闭窗口)清理内存的代码
到这就框架就复制完了,这时可以把这代码备份一下,后面想使用的时候直接拿过来,就不需要重新跟着上方的步骤来复制了
main.cpp的完整代码
// 引入头文件:包含程序所需的基础定义(相当于画画前准备好颜料、画笔的说明书)
#include "main.h"// ==============================================
// 【全局静态变量 → 跨函数共享的“画画工具/状态”】
// 小白类比:这些变量就像画室里的“画架尺寸”“画笔”“画布”,所有人都能用但不外借
// ==============================================
// 窗口需要调整的宽高:记录“画架要改成多大”(用户拖动窗口后,先记下来再调整)
static UINT g_ResizeWidth = 0, g_ResizeHeight = 0;
// Direct3D核心“画画工具”(显卡提供的功能):
static ID3D11Device* g_pd3dDevice = nullptr; // 1. 颜料工厂:负责生产“能挂在画布上的颜料”(创建显卡资源)
static ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; // 2. 画师的手:负责用颜料在画布上画画(执行渲染命令)
static IDXGISwapChain* g_pSwapChain = nullptr; // 3. 双画布切换器:后台画布画完→快速切换到前台展示(避免用户看到半成品)
static ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; // 4. 带颜料层的画布(核心!):专门用来画画,颜料能附着在上面
static bool g_SwapChainOccluded = false; // 画布是否被挡住:记录“观众看不到幕布”(窗口被遮挡)→暂停画画// 【ImGUI窗口消息处理器 → 画师的助理:先处理简单需求】
// 小白类比:用户说“要画个圆”,助理先记下来,处理不了再找画师
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);// ==============================================
// 【自定义窗口消息处理函数 → 画师的“需求接收台”】
// 小白类比:用户的所有要求(拖动画架、关闭画室)都先传到这,再安排处理
// ==============================================
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{// 第一步:助理先处理需求(比如用户点击画布上的按钮)if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))return true;switch (msg){// 需求1:用户拖动画架边缘→画架变大/变小case WM_SIZE:// 若画架被收起来(窗口最小化)→不用调整画布if (wParam == SIZE_MINIMIZED)return 0;// 记录新的画架尺寸(宽=LOWORD(lParam),高=HIWORD(lParam)→Windows规定的记法)g_ResizeWidth = (UINT)LOWORD(lParam); g_ResizeHeight = (UINT)HIWORD(lParam);return 0;// 需求2:用户按ALT+空格→想调出画架菜单(禁用,避免干扰画画)case WM_SYSCOMMAND:if ((wParam & 0xfff0) == SC_KEYMENU) return 0;break;// 需求3:用户要关门(关闭窗口)→通知画师停止工作case WM_DESTROY:::PostQuitMessage(0); // 发“下班”通知→主循环收到后停止return 0;}// 处理不了的需求→交给“画室管理员”(Windows系统)默认处理return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}// ==============================================
// 【创建渲染目标视图 → 制作“能挂住颜料的画布”】
// 小白重点!!!
// 核心概念类比:
// - 渲染目标(RenderTarget)= 带“颜料吸附层”的画布:普通白纸(缓冲区)不能直接画画,要涂一层特殊涂层(渲染目标视图),颜料才能粘住
// - 交换链(SwapChain)= 双画布切换器:有两块画布,一块在后台画画(后台缓冲区),一块在前台展示(前台缓冲区),画完后快速切换,用户看不到半成品
// 这一步就是:给后台画布涂“颜料吸附层”,让画师能在上面画画
// ==============================================
void CreateRenderTarget()
{// 1. 从“双画布切换器”(交换链)中拿出“后台画布”(空白白纸,类型是ID3D11Texture2D)ID3D11Texture2D* pBackBuffer; // 临时变量:存空白白纸// GetBuffer(0, ...):拿第0块画布(交换链默认两块:0=后台画画,1=前台展示)// IID_PPV_ARGS:安全拿画布→避免拿错成其他东西(比如画笔)g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));// 2. 给“后台画布”(空白白纸)涂“颜料吸附层”→生成“渲染目标视图”(带涂层的画布)// 第一个参数:要涂涂层的空白画布(pBackBuffer)// 第二个参数:nullptr→用默认涂层配方(和画布材质匹配,颜料不会掉)// 第三个参数:&g_mainRenderTargetView→把涂好层的画布存到全局变量,后续画画用g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);// 3. 把“空白白纸”(pBackBuffer)收起来→因为已经有“带涂层的画布”了,白纸没用了// 为什么要收?:画室空间有限,不用的东西要放回原位(避免内存泄漏)pBackBuffer->Release();
}// ==============================================
// 【清理渲染目标视图 → 把“带涂层的画布”擦干净】
// 小白重点!!!
// 作用:换画布/关门时,把“带涂层的画布”上的颜料擦掉,避免下次画画有残留
// ==============================================
void CleanupRenderTarget()
{// 若“带涂层的画布”(g_mainRenderTargetView)存在→先擦干净(Release),再记为空(避免下次拿错)if (g_mainRenderTargetView) { g_mainRenderTargetView->Release(); // 擦干净涂层和颜料g_mainRenderTargetView = nullptr; // 标记“画布已清空”}
}// ==============================================
// 【创建Direct3D设备和交换链 → 搭建“画室全套工具”】
// 小白重点!!!
// 核心概念类比:
// - Direct3D设备(g_pd3dDevice)= 颜料工厂:生产各种颜料(资源),比如红色、蓝色,还能制作画布涂层
// - Direct3D设备上下文(g_pd3dDeviceContext)= 画师的手:拿着颜料(设备生产的资源),在画布(渲染目标)上画画
// - 交换链(g_pSwapChain)= 双画布切换器+前台幕布:不仅有两块画布,还能把前台画布的内容显示到“画室窗户”(窗口hwnd)上,用户能看到
// 这一步就是:把颜料工厂、画师的手、双画布切换器都准备好,画室可以开工了
// ==============================================
bool CreateDeviceD3D(HWND hWnd)
{// 1. 给“双画布切换器”(交换链)写“配置单”→告诉工厂要做什么样的切换器DXGI_SWAP_CHAIN_DESC sd; // 配置单ZeroMemory(&sd, sizeof(sd)); // 先把配置单擦干净→避免之前的字迹干扰sd.BufferCount = 2; // 要2块画布(双缓冲:1块后台画,1块前台显)sd.BufferDesc.Width = 0; // 画布宽度:0=自动匹配“画室窗户”(窗口)大小,不用手动设sd.BufferDesc.Height = 0; // 画布高度:同上,自动匹配窗口sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // 画布材质:能显示红(R)、绿(G)、蓝(B)、透明(A)四种颜色,和电脑屏幕匹配sd.BufferDesc.RefreshRate.Numerator = 60; // 切换画布的速度:每秒60次(避免画面闪)sd.BufferDesc.RefreshRate.Denominator = 1; sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // 允许切换“显示模式”(比如窗口模式/全屏模式)sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // 画布用途:专门用来画画(不是用来写字)sd.OutputWindow = hWnd; // 把前台画布的内容显示到“哪个画室窗户”(窗口hwnd)sd.SampleDesc.Count = 1; // 颜料细腻度:1=普通画质(够用,画得快),数字越大越细腻但越慢sd.SampleDesc.Quality = 0; // 配合细腻度的参数,1对应0即可sd.Windowed = TRUE; // 画布显示模式:TRUE=窗口模式(画布在窗户里),FALSE=全屏(画布占满整个屏幕)sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; // 切换画布后:后台画布的旧颜料扔掉(不用保存,下次重新画)// 2. 给“颜料工厂”(Direct3D设备)设“生产模式”UINT createDeviceFlags = 0;// createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG; // 调试模式(开发时用,能提示哪里画错了,正式用要关掉)// 3. 告诉工厂“能生产哪些材质的颜料”(向下兼容旧电脑)D3D_FEATURE_LEVEL featureLevel; // 记录工厂实际能生产的最高级材质const D3D_FEATURE_LEVEL featureLevelArray[2] = { D3D_FEATURE_LEVEL_11_0, // 优先生产高级颜料(D3D11,新电脑支持)D3D_FEATURE_LEVEL_10_0 // 兼容旧电脑(D3D10,旧显卡也能画)};// 4. 核心步骤:按配置单“搭建全套工具”// D3D11CreateDeviceAndSwapChain:一站式创建4个东西:// - 颜料工厂(g_pd3dDevice)// - 画师的手(g_pd3dDeviceContext)// - 双画布切换器(g_pSwapChain)// - 记录工厂能生产的颜料材质(featureLevel)HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags,featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain,&g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);// 5. 兼容处理:若新电脑颜料工厂坏了(比如显卡不支持D3D11)→用“备用手工颜料”(WARP软件驱动,纯CPU画画,慢但能画)if (res == DXGI_ERROR_UNSUPPORTED) res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags,featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain,&g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);// 6. 若工具没搭好(res != S_OK)→画室开不了门,返回失败if (res != S_OK)return false;// 7. 最后一步:制作“能挂住颜料的画布”(调用前面的函数)→工具齐了,能画画了CreateRenderTarget();return true;
}// ==============================================
// 【清理Direct3D资源 → 画室关门,收拾工具】
// 小白重点!!!
// 收拾顺序:先擦画布→再收切换器→再收手和工厂→避免工具损坏(资源泄漏)
// ==============================================
void CleanupDeviceD3D()
{CleanupRenderTarget(); // 1. 先擦干净“带涂层的画布”(避免颜料干在上面)if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = nullptr; } // 2. 收“双画布切换器”if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = nullptr; } // 3. 收“画师的手”if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = nullptr; } // 4. 关“颜料工厂”
}// ==============================================
// 【程序入口 → 画室从开门到关门的全流程】
// 小白重点!!!关注“画画(渲染)”的完整步骤
// ==============================================
int main() {// -------------------------- 1. 调整画架大小 → 避免画布太小/模糊ImGui_ImplWin32_EnableDpiAwareness(); // 告诉画师“画室窗户(屏幕)的清晰度”→避免画的图模糊float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(::MonitorFromPoint(POINT{ 0, 0 }, MONITOR_DEFAULTTOPRIMARY)); // 算“缩放比例”→比如窗户是4K屏,比例=2.0,画架要放大2倍才清楚// -------------------------- 2. 租“画室窗户”(创建窗口)→ 用户能看到画布的地方WNDCLASSEXW wc = { sizeof(wc), CS_CLASSDC, WndProc, 0L, 0L, GetModuleHandle(nullptr), nullptr, nullptr, nullptr, nullptr, L"ImGui Example", nullptr };::RegisterClassExW(&wc); // 告诉“画室管理员”要租窗户HWND hwnd = ::CreateWindowW(wc.lpszClassName, L"Dear ImGui DirectX11 Example", WS_OVERLAPPEDWINDOW, 100, 100, (int)(1280 * main_scale), (int)(800 * main_scale), nullptr, nullptr, wc.hInstance, nullptr); // 实际租到窗户// -------------------------- 3. 搭建画室工具 → 颜料工厂、手、切换器、画布if (!CreateDeviceD3D(hwnd)) // 若工具没搭好→关窗走人{CleanupDeviceD3D(); // 收拾已有的工具::UnregisterClassW(wc.lpszClassName, wc.hInstance); // 退租窗户return 1;}// -------------------------- 4. 打开窗户+请助理 → 准备接待用户::ShowWindow(hwnd, SW_SHOWDEFAULT); // 打开窗户→用户能看到里面::UpdateWindow(hwnd); // 擦干净窗户→避免有灰尘(窗口空白)// 请“ImGUI助理”→负责画草图(UI)、记录用户需求IMGUI_CHECKVERSION(); // 检查助理资质→避免新手助理出错ImGui::CreateContext(); // 给助理安排工作间(上下文)ImGuiIO& io = ImGui::GetIO(); (void)io; // 给助理配“需求记录本”(IO对象)io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // 助理要会看键盘需求(比如用户按Tab)io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // 助理要会看手柄需求// 给助理说“画架缩放比例”→助理画的草图要和画架一样大ImGuiStyle& style = ImGui::GetStyle();style.ScaleAllSizes(main_scale); // 草图里的按钮、文字→放大main_scale倍style.FontScaleDpi = main_scale; // 草图里的字→也要放大,避免看不清// 助理和画师/工具对接→助理画的草图,画师能看懂ImGui_ImplWin32_Init(hwnd); // 助理和窗户对接→知道草图要画在哪个窗户里ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext); // 助理和画师/工厂对接→知道用什么颜料画// -------------------------- 5. 准备画画的“素材”→ 要画的内容和背景色bool show_demo_window = true; // 要不要画“示例草图”(比如按钮、滑块)bool show_another_window = false;// 要不要画“另一张草图”ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); // 画布背景色→蓝灰色(R=0.45, G=0.55, B=0.60, 透明=1.0)// -------------------------- 6. 画室日常运营(主循环)→ 持续画画、展示bool done = false; // 关门标志→false=开门,true=关门while (!done){// 6.1 处理用户需求→助理记录,画师调整MSG msg;while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) // 看有没有新需求{::TranslateMessage(&msg); // 助理翻译需求(比如用户按键盘→翻译成“要移动按钮”)::DispatchMessage(&msg); // 把需求传给画师(WndProc)if (msg.message == WM_QUIT) // 收到“关门”需求→标记要关门done = true;}if (done)break;// 6.2 检查用户是否在看→若窗户被挡住(用户看不到)→暂停画画if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED){::Sleep(10); // 休息10毫秒→节省颜料continue;}g_SwapChainOccluded = false;// 6.3 调整画架大小→用户之前说要改画架,现在动手if (g_ResizeWidth != 0 && g_ResizeHeight != 0){CleanupRenderTarget(); // 先擦干净旧画布(避免颜料残留)// 调整“双画布切换器”的画布大小→和新画架匹配g_pSwapChain->ResizeBuffers(0, g_ResizeWidth, g_ResizeHeight, DXGI_FORMAT_UNKNOWN, 0);g_ResizeWidth = g_ResizeHeight = 0; // 标记“已调整完”CreateRenderTarget(); // 重新做“带涂层的新画布”}// 6.4 助理准备画草图→告诉画师“今天要画什么”ImGui_ImplDX11_NewFrame(); // 助理告诉画师“颜料工厂准备好了”ImGui_ImplWin32_NewFrame(); // 助理告诉画师“窗户位置确认好了”ImGui::NewFrame(); // 助理开始画草图→比如画一个按钮、一段文字// (可选)助理画具体草图→比如画示例窗口、自定义按钮// if (show_demo_window)// ImGui::ShowDemoWindow(&show_demo_window); // 画示例草图// ==============================================// 【渲染步骤 → 画师按草图在画布上画画!】// 小白重点!!!完整画画流程(渲染=按草图上颜料)// 1. 助理把草图交给画师(ImGui::Render())→ 确定要画的内容和位置// 2. 画师调背景色→先把画布涂成背景色(避免有旧画)// 3. 画师把“带涂层的画布”固定在画架上→告诉自己“要画在这上面”// 4. 画师按草图上颜料→把按钮、文字画到画布上// 5. 切换画布→把后台画好的画布换到前台,用户看到新画// ==============================================// 1. 助理把草图(UI布局)整理好交给画师→生成“画画指令”(比如“在(100,100)画红色按钮”)ImGui::Render();// 2. 画师调背景色→把“蓝灰色”颜料按比例调好(乘透明度,避免太浓)const float clear_color_with_alpha[4] = { clear_color.x * clear_color.w, // 红色分量(乘透明度)clear_color.y * clear_color.w, // 绿色分量clear_color.z * clear_color.w, // 蓝色分量clear_color.w // 透明度};// 3. 画师把“带涂层的画布”(g_mainRenderTargetView)固定在“画架”(设备上下文)上// OMSetRenderTargets:告诉画师“接下来要画在这块画布上,别画错了”// 第一个参数:1→只固定1块画布// 第二个参数:&g_mainRenderTargetView→要固定的画布(带涂层的后台画布)// 第三个参数:nullptr→不用画景深(简单场景不用)g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);// 4. 画师先给画布涂背景色→用调好的蓝灰色把画布整个涂一遍,覆盖旧画g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);// 5. 画师按“画画指令”(ImGui::GetDrawData())在画布上画具体内容→比如按钮、文字ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());// 6. 切换画布→把“后台画好的画布”(带新画)和“前台展示的画布”快速交换// Present(1, 0):1→每秒只换60次(和屏幕刷新同步,避免画面闪),0→无额外设置HRESULT hr = g_pSwapChain->Present(1, 0); // 检查窗户是否被挡住→更新标志,下次挡住就暂停画画g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);}// -------------------------- 7. 画室关门→收拾所有东西// 先让助理下班→清理助理的工作间和记录本ImGui_ImplDX11_Shutdown();ImGui_ImplWin32_Shutdown();ImGui::DestroyContext();// 再收拾画师工具→擦画布、收切换器、关工厂CleanupDeviceD3D();// 最后退租窗户→把窗户还给管理员::DestroyWindow(hwnd);::UnregisterClassW(wc.lpszClassName, wc.hInstance);return 0; // 画室成功关门,返回0表示一切正常
}