The smooth brass case back of the pocket watch reflects large bright sky regions of the Venice dawn environment at HDR values well above 1. With the old threshold of 1.0 the entire metal surface passed the bloom extract, and the blurred additive result washed it out to flat white. Only genuinely hot highlights (HDR >= 3, near-white after ACES) should bloom. - Default bloomThreshold 1.0 -> 3.0; Stats slider range extended to 8 with a logarithmic scale - Texture mip generation: transition the whole mip chain to TRANSFER_DST up front; blitting into never-transitioned destination mips triggered one validation warning per level
215 lines
7.4 KiB
C++
215 lines
7.4 KiB
C++
//
|
||
// StatsPanel.cpp:遥测与全局设置实现(原 main.cpp Scene Debug 搬迁)。
|
||
// 面板按 View / Scene / Rendering / Physics / Performance 分组——
|
||
// 环境贴图/环境光已实体化(Skybox/SkyLight 组件,Details 配置),
|
||
// 此处只留场景级 ClearColor。
|
||
//
|
||
|
||
#include "StatsPanel.h"
|
||
#include "Engine/ECS/Components.h"
|
||
#include "Engine/Physics/PhysicsWorld.h"
|
||
#include "Engine/Core/Time.h"
|
||
|
||
#include <imgui.h>
|
||
#include <glm/gtc/type_ptr.hpp>
|
||
|
||
using namespace Engine; // f32(Engine/Core/Types.h)
|
||
|
||
void StatsPanel::Draw(const EditorContext& ctx)
|
||
{
|
||
ImGui::Begin("Stats");
|
||
|
||
auto& renderer = *ctx.renderer;
|
||
auto& app = *ctx.app;
|
||
|
||
// ---- View:看哪里(渲染目标)+ 相机位姿(跟随渲染源分派)----
|
||
if (ImGui::CollapsingHeader("View", ImGuiTreeNodeFlags_DefaultOpen))
|
||
{
|
||
// 渲染源分派与 SceneRenderer::BuildFrame 同规则:PIE 内嵌播放
|
||
// 或 kDisplayDirect 全屏预览 = primary 相机,否则编辑器相机
|
||
const bool playing = ctx.playState && *ctx.playState;
|
||
static const char* displayTarget[] = {
|
||
"Fullscreen",
|
||
"Viewport",
|
||
};
|
||
int mode = renderer.GetDisplayMode();
|
||
if (ImGui::Combo("Display Target", &mode, displayTarget,
|
||
IM_ARRAYSIZE(displayTarget)))
|
||
{
|
||
renderer.SetDisplayMode(mode);
|
||
}
|
||
if ((playing || renderer.GetDisplayMode() == kDisplayDirect))
|
||
{
|
||
Engine::ECS::TransformComponent* camT = nullptr;
|
||
if (ctx.scene->GetPrimaryCamera(&camT) != nullptr && camT != nullptr)
|
||
{
|
||
// world 位姿(相机实体可能挂父层级下)
|
||
const glm::vec3& pos = camT->GetWorldPosition();
|
||
ImGui::Text("Game Camera Pos");
|
||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||
}
|
||
else
|
||
{
|
||
ImGui::TextDisabled("(no primary camera)");
|
||
}
|
||
}
|
||
else if (ctx.editorCamera)
|
||
{
|
||
const glm::vec3& pos = ctx.editorCamera->GetTransform().GetPosition();
|
||
ImGui::Text("Editor Camera Pos");
|
||
ImGui::Text("(%.3f, %.3f, %.3f)", pos.x, pos.y, pos.z);
|
||
}
|
||
else
|
||
{
|
||
ImGui::TextDisabled("(no camera)");
|
||
}
|
||
}
|
||
|
||
// ---- Scene:模拟控制 + 场景级设置。PIE 暂停在主工具栏(UE 语义)----
|
||
if (ImGui::CollapsingHeader("Scene", ImGuiTreeNodeFlags_DefaultOpen))
|
||
{
|
||
ImGui::ColorEdit4("Clear Color",
|
||
glm::value_ptr(ctx.scene->GetSettings().ClearColor));
|
||
}
|
||
|
||
// ---- Rendering:渲染管线参数与开关 ----
|
||
if (ImGui::CollapsingHeader("Rendering", ImGuiTreeNodeFlags_DefaultOpen))
|
||
{
|
||
f32 exposure = renderer.GetExposure();
|
||
if (ImGui::SliderFloat("Exposure", &exposure, 0.1f, 4.0f))
|
||
{
|
||
renderer.SetExposure(exposure);
|
||
}
|
||
bool aces = renderer.GetUseACES();
|
||
if (ImGui::Checkbox("ACES", &aces))
|
||
{
|
||
renderer.SetUseACES(aces);
|
||
}
|
||
|
||
// Bloom:开关即显隐——勾上才显示参数
|
||
bool bloom = renderer.GetBloomEnabled();
|
||
if (ImGui::Checkbox("Bloom", &bloom))
|
||
{
|
||
renderer.SetBloomEnabled(bloom);
|
||
}
|
||
if (bloom)
|
||
{
|
||
f32 threshold = renderer.GetBloomThreshold();
|
||
if (ImGui::SliderFloat("Threshold", &threshold, 0.0f, 8.0f,
|
||
"%.1f", ImGuiSliderFlags_Logarithmic))
|
||
{
|
||
renderer.SetBloomThreshold(threshold);
|
||
}
|
||
f32 strength = renderer.GetBloomStrength();
|
||
if (ImGui::SliderFloat("Strength", &strength, 0.0f, 2.0f))
|
||
{
|
||
renderer.SetBloomStrength(strength);
|
||
}
|
||
}
|
||
|
||
// 无参数的纯开关:横排平铺
|
||
bool shadows = renderer.GetShadowsEnabled();
|
||
if (ImGui::Checkbox("Shadows", &shadows))
|
||
{
|
||
renderer.SetShadowsEnabled(shadows);
|
||
}
|
||
ImGui::SameLine();
|
||
bool taa = renderer.GetTAAEnabled();
|
||
if (ImGui::Checkbox("TAA", &taa))
|
||
{
|
||
renderer.SetTAAEnabled(taa);
|
||
}
|
||
ImGui::SameLine();
|
||
bool lod = renderer.GetLodEnabled();
|
||
if (ImGui::Checkbox("LOD", &lod))
|
||
{
|
||
renderer.SetLodEnabled(lod); // 关 = 恒绘原网格
|
||
}
|
||
|
||
// 调试视图:单看某个 pass 的中间输出(类 UE pass debug)
|
||
const auto& viewNames = renderer.GetDebugViewNames();
|
||
int view = renderer.GetDebugView();
|
||
if (ImGui::Combo("Debug View", &view, viewNames.data(),
|
||
static_cast<int>(viewNames.size())))
|
||
{
|
||
renderer.SetDebugView(view);
|
||
}
|
||
}
|
||
|
||
// ---- Physics:调试可视化(collider/约束/重力/速度/加速度)----
|
||
if (ImGui::CollapsingHeader("Physics"))
|
||
{
|
||
auto& physics = app.GetPhysics();
|
||
u32 bits = physics.GetDebugDrawBits();
|
||
bool master = bits != 0;
|
||
if (ImGui::Checkbox("Debug Draw##Phys", &master))
|
||
{
|
||
bits = master ? kPhyDbgDefault : 0;
|
||
}
|
||
if (master)
|
||
{
|
||
ImGui::Indent();
|
||
auto toggle = [&bits](const char* label, u32 bit) {
|
||
bool on = (bits & bit) != 0;
|
||
if (ImGui::Checkbox(label, &on))
|
||
{
|
||
bits = on ? (bits | bit) : (bits & ~bit);
|
||
}
|
||
};
|
||
toggle("Colliders", kPhyDbgColliders);
|
||
toggle("Constraints", kPhyDbgConstraints);
|
||
toggle("Gravity", kPhyDbgGravity);
|
||
toggle("Velocity", kPhyDbgVelocity);
|
||
toggle("Acceleration", kPhyDbgAccel);
|
||
ImGui::Unindent();
|
||
}
|
||
physics.SetDebugDrawBits(bits);
|
||
// 碰撞事件计数(最近一次步进;掉落物触地时应 > 0)
|
||
ImGui::Text("contacts (frame): %zu", physics.GetEvents().size());
|
||
}
|
||
|
||
// ---- Performance:帧率控制 + GPU 遥测(默认折叠,不占面板高度)----
|
||
if (ImGui::CollapsingHeader("Performance"))
|
||
{
|
||
bool vsync = app.GetVSync();
|
||
if (ImGui::Checkbox("VSync", &vsync))
|
||
{
|
||
app.SetVSync(vsync);
|
||
if (!vsync)
|
||
{
|
||
const f32 target = app.GetTargetFps();
|
||
app.SetTargetFps(target);
|
||
}
|
||
}
|
||
if (!vsync)
|
||
{
|
||
f32 target = app.GetTargetFps();
|
||
if (ImGui::SliderFloat("Target FPS", &target, 24.0f, 360.0f, "%.0f"))
|
||
{
|
||
app.SetTargetFps(target);
|
||
}
|
||
}
|
||
ImGui::Text("fps: %.1f frame: %llu",
|
||
1.0f / Time::Current().UnscaledDt(),
|
||
static_cast<unsigned long long>(app.GetFrameCount()));
|
||
|
||
ImGui::Separator();
|
||
ImGui::Text("instances: %u main / %u shadow",
|
||
renderer.GetMainInstanceCount(),
|
||
renderer.GetShadowInstanceCount());
|
||
if (renderer.GetInstancesTruncated())
|
||
{
|
||
ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f),
|
||
"TRUNCATED: instance buffer full!");
|
||
}
|
||
const auto& names = renderer.GetPassNames();
|
||
const auto& times = renderer.GetPassGpuMs();
|
||
for (size_t i = 0; i < names.size() && i < times.size(); ++i)
|
||
{
|
||
ImGui::Text(" %-12s %.3f ms", names[i].c_str(), times[i]);
|
||
}
|
||
}
|
||
|
||
ImGui::End();
|
||
}
|