截止6.11,统计前三周的工作内容:
大部分的UI工作都是为了后续整合与调整做铺垫,做了很多实验性的工作。
- ImGui类的统筹管理
常规ImGui语句全部放在main函数中过于复杂,我们打算设计一个ImGui类来管理一些通用的UI绘制并整理一些ImGui语句,进一步去探索如何高效地连接openGL的绘制物体和ImGui部分
MyImGui::MyImGui(int*width,int*height,GLFWwindow* glWindow) {
//do init
Width = width;
Height = height;
Init(glWindow);
}
void MyImGui::Init(GLFWwindow* glWindow) {
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
(void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io.Fonts->AddFontFromFileTTF("C:\\\\Windows\\\\Fonts\\\\msyh.ttc", 30.f, NULL, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForOpenGL(glWindow, true);
ImGui_ImplOpenGL3_Init("#version 460");
}
初始化操作的整合,并为后面按钮等回调逻辑的书写铺垫
一些功能函数一览
class MyImGui {
public:
MyImGui(int*width,int*height, GLFWwindow* glWindow);
~MyImGui();
void Init(GLFWwindow* glWindow);
void RenderImGui();
void DrawPanels();
void TestPanel(const char* panelName,Material* mat,Light* _light);
void InitMatPanel(const char* panelName, Material* mat, Light* _light);
void SetButton(const char* label, std::function<void(const char*, Material*, Light*)> callback, const char* para, Material* mat, Light* light);
void SetObjectPosition(Object* obj,const char* name);
void SetObjectRotation(Object* obj);
void SetMaterial(Material* grassMaterial);
bool MyImGui::CustomizedButton(const char* label);
void SetObjectMaterial(Material* mat,Mesh* obj);
public:
std::vector<Panel*> panels;
std::unordered_map<std::string,Panel*> panelMap;
int* Width;
int* Height;
};
利用ImGui类,在原脚本中复杂的UI绘制语句就可以被大大缩减。
例如如下语句,对所有物体设置UI操控项就可以这样书写。
for (auto obj : renderMap) {
my_img->SetObjectPosition(obj.second, obj.first.c_str());
}
实现的效果:
这样就很有一些软件的UI感觉,能够高效管理其中内容
- 事件回调与按钮/面板的打开与关闭
先前推文中实现了回调传入,利用这一特性我们就可以为只有true或false的按钮控件绑定触发函数
并让其对应具体面板,利用其打开/关闭面板以做到UI驱动UI.
my_img->SetButton("Mat Panel", std::bind(&MyImGui::InitMatPanel, my_img, "Mat Panel", ((Mesh*)renderMap["sphere"])->mMaterial, dirLight),
"pbr", sphereMat, dirLight);
void MyImGui::SetButton(const char* label,std::function<void(const char*,Material*,Light*)> callback,const char* para,Material* mat,Light* light) {
if (CustomizedButton(label))
callback(para,mat,light);
}
然后利用Panel管理面板内容,根据传入参数不同做分化,以打开不同的面板。
class Panel {
public:
Panel();
Panel(const char* panelName);
~Panel();
void draw();
void toggle();
virtual void InitContent();
public:
//用于触发Panel显示
bool isActive;
const char* name;
Material* material;
Light* light;
};
- 与物体渲染结合,实现材质切换
利用按钮更新物体材质选项,同时对面板的呈现内容进行更新操作。实现打开材质面板的信息显示的是物体材质的实时信息。这些内容大部分都是为了后续渲染时可以灵活地调整渲染所需要的材质等信息
//ImGui渲染部分
if (ImGui::CollapsingHeader("Material Choices")) {
if (my_img->CustomizedButton("Phong")) {
((Mesh*)renderMap["sphere"])->mMaterial = groundMat;
}
if (my_img->CustomizedButton("boxMat")) {
((Mesh*)renderMap["sphere"])->mMaterial = boxMat;
}
}
ChoicePanel::ChoicePanel(Material* mat,DirectionalLight* p_light,const char* p_name) {
material = mat;
name = p_name;
light = p_light;
}
void ChoicePanel::InitContent() {
GrassInstanceMaterial* grassMaterial = nullptr;
PBRSurfaceMaterial* pbrMat = nullptr;
switch (material->mType) {
case(MaterialType::GrassInstanceMaterial):
case(MaterialType::BlinnPhongMaterial):
break;
case(MaterialType::CubeMaterial):
ImGui::Button("CubeMaterial!");
break;
case(MaterialType::PhongParallaxShadowMaterial):
ImGui::Button("Phong!");
//参数设置
break;
case(MaterialType::PBRSurfaceMaterial):
break;
default:
break;
}
}
杂项:内存占用监控
利用MemoryMonitor类进行绘制与更新程序的内存占用
#pragma once
#include <vector>
#include <chrono>
#include "../core.h"
// 包含之前提到的内存检测方法,例如:
#include <windows.h> // Windows
#include <psapi.h>
#include "../../imgui/imgui.h"
#include "../../imgui/imgui_impl_glfw.h"
#include "../../imgui/imgui_impl_opengl3.h"
class MemoryMonitor {
private:
std::vector<float> memoryHistory; // 存储历史内存数据
int maxHistorySize = 200; // 最大存储的数据点数
float maxMemoryMB = 0; // 用于自动缩放图表
public:
void Update() {
// 获取当前内存使用量(使用前面提到的方法)
size_t currentMemory = GetCurrentMemoryUsage();
float memoryMB = currentMemory / (1024.0f * 1024.0f);
// 更新历史数据
memoryHistory.push_back(memoryMB);
if (memoryHistory.size() > maxHistorySize) {
memoryHistory.erase(memoryHistory.begin());
}
// 更新最大内存用于自动缩放
if (memoryMB > maxMemoryMB) {
maxMemoryMB = memoryMB * 1.1f; // 留10%余量
}
}
void DrawGUI() {
if (memoryHistory.empty()) return;
ImGui::Begin("Memory Usage");
ImGui::Text("Frame Rate: %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
if (ImGui::CollapsingHeader("Memory Usage")) {
// 显示当前内存值
ImGui::Text("Current: %.2f MB", memoryHistory.back());
// 绘制折线图
ImGui::PlotLines("Memory (MB)",
memoryHistory.data(),
static_cast<int>(memoryHistory.size()),
0, nullptr,
0.0f, maxMemoryMB,
ImVec2(0, 80.0f));
// 显示统计信息
float avg = 0;
for (float v : memoryHistory) avg += v;
avg /= memoryHistory.size();
ImGui::Text("Avg: %.2f MB | Max: %.2f MB", avg, maxMemoryMB);
// 控制选项
ImGui::SliderInt("History Size", &maxHistorySize, 10, 1000);
if (ImGui::Button("Reset Max")) {
maxMemoryMB = 0;
}
}
ImGui::End();
}
private:
size_t GetCurrentMemoryUsage() {
PROCESS_MEMORY_COUNTERS pmc;
if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) {
return pmc.WorkingSetSize;
}
return 0;
}
};