第 22 章 与 ImGui 集成
本章定位:调试面板、实时调参离不开 UI。
vsgImGui是独立组件库,将 Dear ImGui 叠加到 VSG 渲染之上。本章讲集成模式(具体类名随版本可能微调,模式稳定)。
摘要:本章介绍如何在 VulkanSceneGraph (VSG) 中集成 Dear ImGui 调试界面。通过vsgImGui外部组件库,创建 ImGui 叠加渲染节点,实现每帧更新 UI 控件。关键步骤包括:1) 安装并链接vsgImGui库;2) 继承RenderImGui类重写update方法绘制控件;3) 使用CollectEvents转发鼠标键盘事件;4) 将 UI 控件绑定到 uniform 值实现实时调参。最终构建带调试面板的查看器,支持曝光等参数动态调整。
22.1 本章目标
- 了解
vsgImGui组件库的作用; - 掌握「ImGui 叠加节点 + 每帧
update绘制控件」的集成模式; - 将 UI 控件绑定到场景参数(呼应第 10 章 uniform)。
22.2 前置准备
- 第 8 章(状态命令)与第 10 章(uniform/描述符)已读;
- 已安装
vsgImGui组件库(独立仓库,非核心)。
22.3 组件库与链接
vsgImGui是外部组件库(与vsgXchange同级),需单独安装并链接:
find_package(vsgImGui REQUIRED) target_link_libraries(my_app PRIVATE vsg::vsg vsgImGui::vsgImGui)若你的版本把 ImGui 整合进核心,则无需
find_package;但按当前仓库现状,vsgImGui是独立组件库,请以你安装的版本为准。
22.4 集成模式:ImGui 叠加节点
标准做法:创建一个渲染 ImGui 的节点(通常是vsgImGui::RenderImGui的子类),把它加入场景图。它在自己的渲染通道里把 Dear ImGui 的绘制指令输出到屏幕,叠加在主场景之上。
// 1) 自定义一个 ImGui 节点,重写每帧更新classMyGui:publicvsgImGui::RenderImGui{public:MyGui(ref_ptr<vsg::Camera>cam,ref_ptr<vsg::floatValue>exposure):camera(cam),exposure(exposure){}voiddoUpdate(bool&updated)override// 每帧调用,绘制控件{ImGui::Begin("调试面板");floate=exposure->value;if(ImGui::SliderFloat("曝光",&e,0.0f,3.0f))exposure->value=e;// 修改 uniform(见第 10 章)ImGui::Text("FPS: %.1f",ImGui::GetIO().Framerate);ImGui::End();updated=true;}ref_ptr<vsg::Camera>camera;ref_ptr<vsg::floatValue>exposure;};方法名(
doUpdate/update)与基类依据vsgImGui版本而定;核心是「每帧回调里调用 ImGui 控件 API」。请对照你所用版本的RenderImGui头文件确认重写点。
22.5 事件转发
要让鼠标/键盘驱动 ImGui,需要把 VSG 的PointerEvent/KeyEvent转发给 ImGui。组件库通常提供事件收集器(如vsgImGui::CollectEvents),将其添加到Viewer的事件处理器:
viewer->addEventHandlers({vsgImGui::CollectEvents::create(guiNode)});这样 ImGui 能正确响应悬停、点击、输入。
22.6 绑定到场景参数(实时调参)
UI 控件直接修改第 10 章的Value类型即可实时生效:
autoexposure=vsg::floatValue::create(1.0f);// 作为 DescriptorBuffer 绑到管线(第 10 章)autoubo=vsg::DescriptorBuffer::create(exposure,0);// GUI 里改 exposure->value,帧循环里:exposure->value=e;ubo->copyDataListToBuffers();// CPU → GPU于是拖动滑块,画面曝光立即变化——典型的「调试面板实时参数调整」。
22.7 完整示例:带调试面板的查看器
autoexposure=vsg::floatValue::create(1.0f);autogui=MyGui::create(camera,exposure);// 你的 RenderImGui 子类autoroot=vsg::Group::create();root->addChild(model);root->addChild(gui);// 将 ImGui 叠加到场景图autocommandGraph=vsg::createCommandGraphForView(window,camera,root);viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph});viewer->addEventHandlers({vsgImGui::CollectEvents::create(gui)});22.8 常见问题
| 现象 | 原因 | 解决 | 代码/配置示例 |
|---|---|---|---|
编译找不到vsgImGui | 没安装/链接组件库 | find_package(vsgImGui REQUIRED)+ 链接 | 错误 CMakeLists.txt:cmake<br>find_package(vsg REQUIRED)<br>target_link_libraries(my_app PRIVATE vsg::vsg)<br>正确 CMakeLists.txt: cmake<br>find_package(vsg REQUIRED)<br>find_package(vsgImGui REQUIRED) # ← 必须添加<br>target_link_libraries(my_app PRIVATE vsg::vsg vsgImGui::vsgImGui)<br> |
| ImGui 不响应输入 | 没转发事件 | 将CollectEvents(或等价事件收集器)添加到 Viewer | 错误写法(缺少事件收集器):cpp<br>auto gui = MyGui::create(camera, exposure);<br>root->addChild(gui);<br>// 忘记添加事件收集器,ImGui 无法接收鼠标/键盘事件<br>正确写法: cpp<br>auto gui = MyGui::create(camera, exposure);<br>root->addChild(gui);<br>viewer->addEventHandlers({ vsgImGui::CollectEvents::create(gui) }); // ← 关键<br> |
| 控件改了无效果 | 没把值同步到 uniform | 修改value后调用copyDataListToBuffers() | 错误写法(只改值不同步):cpp<br>void doUpdate(bool& updated) override {<br> ImGui::SliderFloat("曝光", &exposure->value, 0.0f, 3.0f);<br> // 缺少 copyDataListToBuffers(),GPU 未更新<br> updated = true;<br>}<br>正确写法: cpp<br>void doUpdate(bool& updated) override {<br> float e = exposure->value;<br> if (ImGui::SliderFloat("曝光", &e, 0.0f, 3.0f)) {<br> exposure->value = e;<br> ubo->copyDataListToBuffers(); // ← 同步到 GPU<br> }<br> updated = true;<br>}<br> |
| 面板挡住主场景 | 叠加顺序/通道问题 | 确保 ImGui 在最后渲染(叠加通道) | 错误场景图结构:cpp<br>auto root = vsg::Group::create();<br>root->addChild(gui); // ImGui 节点在前<br>root->addChild(model); // 模型节点在后,会被 ImGui 遮挡<br>正确场景图结构: cpp<br>auto root = vsg::Group::create();<br>root->addChild(model); // 先渲染模型<br>root->addChild(gui); // 最后渲染 ImGui(叠加在最上层)<br>// 或者使用专门的叠加通道:<br>// auto overlay = vsg::OverlayNode::create(gui);<br>// root->addChild(overlay);<br> |
22.9 小结
vsgImGui是外部组件库,提供 ImGui 叠加渲染节点;- 模式:子类
RenderImGui→ 每帧update绘制控件 → 加入场景图;事件通过收集器转发给 ImGui; - UI 控件直接修改
Value/uniform,配合copyDataListToBuffers()实时生效。
22.10 延伸阅读与下一章预告
- 第 10 章《描述符与资源绑定》:UI 修改的就是这些 uniform;
- 第 24 章《模型查看器》:把 ImGui 面板做成完整的查看器控制;
- 第 26 章《调试技巧》:验证层 + RenderDoc 配合参数调整。