You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Dear ImGui(DX11后端)时如何处理窗口缩放变形问题

问题:ImGui窗口缩放时元素拉伸变形的解决方法

我正在用Visual C++开发一个基于Dear ImGui的简易Win32应用,采用DX11后端。发现调整宿主窗口大小时,ImGui窗口及控件(如绘制的图形)会出现拉伸变形:例如从(0,0)到(200,200)绘制的45度线,缩放后角度发生变化。我确定这不是D3D11的问题,此前使用D3D11处理窗口缩放无需额外代码。应用实际是跟踪notepad.exe窗口,通过MoveWindow调整大小,以下是渲染代码,请问如何正确处理ImGui窗口缩放?

std::optional<int> Window::beginRender() noexcept
{
    if (const auto code = Window::processMessages())
        return *code;

    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    return {};
}

void Window::render(const char* windowTitle) noexcept
{
    ImGui::SetNextWindowPos({ 0, 0 });
    ImGui::SetNextWindowSize({ static_cast<float>(m_width), static_cast<float>(m_height) });

    ImVec2 test{ ImGui::GetWindowSize() };
    ImVec2 test2{ ImGui::GetContentRegionAvail() };

    ImGui::Begin(windowTitle, nullptr, /*ImGuiWindowFlags_NoResize |*/ ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar);

    ImGui::ShowDemoWindow();
    ImGui::GetBackgroundDrawList()->AddLine(ImVec2{ 0.0f, 0.0f }, ImVec2{ 200.0f, 200.0f }, 0xFF0000FF);

    ImGui::End();
}

void Window::endRender() noexcept
{
    ImGui::EndFrame();

    ImGui::Render();

    constexpr float color[]{ 0.0f, 0.0f, 0.0f, 0.0f };

    m_pContext->OMSetRenderTargets(1, &m_pRTV, nullptr);
    m_pContext->ClearRenderTargetView(m_pRTV, color);

    updateTargetData();
    MoveWindow(m_hWnd, m_position.x, m_position.y, m_width, m_height, false);

    //ImGuiIO& io{ ::ImGui::GetIO() };
    //io.DisplaySize.x = m_width;
    //io.DisplaySize.y = m_height;

    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

    m_pSwapChain->Present(1, 0);
}

解决方案

问题核心是ImGui的DisplaySize未与窗口实际尺寸同步,且DX11视口未适配新窗口大小,导致渲染投影矩阵错误,引发元素拉伸。以下是具体修复步骤:

1. 正确更新ImGuiIO的DisplaySize

必须在ImGui::NewFrame()之前更新DisplaySize,因为ImGui依赖该参数计算布局和投影矩阵,晚更新会导致当前帧使用旧尺寸渲染。

修改beginRender():

std::optional<int> Window::beginRender() noexcept
{
    if (const auto code = Window::processMessages())
        return *code;

    // 关键:在NewFrame前同步窗口尺寸到ImGui
    ImGuiIO& io = ImGui::GetIO();
    io.DisplaySize = ImVec2(static_cast<float>(m_width), static_cast<float>(m_height));

    ImGui_ImplDX11_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();

    return {};
}

2. 同步DX11视口(Viewport)

窗口尺寸变化时,需更新D3D11视口,确保渲染内容适配当前分辨率:

修改endRender():

void Window::endRender() noexcept
{
    ImGui::EndFrame();
    ImGui::Render();

    constexpr float color[]{ 0.0f, 0.0f, 0.0f, 0.0f };
    m_pContext->OMSetRenderTargets(1, &m_pRTV, nullptr);
    m_pContext->ClearRenderTargetView(m_pRTV, color);

    // 更新DX11视口,匹配当前窗口尺寸
    D3D11_VIEWPORT vp{};
    vp.Width = static_cast<float>(m_width);
    vp.Height = static_cast<float>(m_height);
    vp.MinDepth = 0.0f;
    vp.MaxDepth = 1.0f;
    vp.TopLeftX = 0.0f;
    vp.TopLeftY = 0.0f;
    m_pContext->RSSetViewports(1, &vp);

    updateTargetData();
    // 建议将MoveWindow移到WM_SIZE消息处理中,避免每帧重复调用
    // MoveWindow(m_hWnd, m_position.x, m_position.y, m_width, m_height, false);

    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
    m_pSwapChain->Present(1, 0);
}

3. 调整窗口尺寸更新时机

在WM_SIZE消息处理中更新窗口尺寸,并确保重建DX11渲染目标:

// 窗口消息处理函数中添加WM_SIZE分支
case WM_SIZE:
{
    m_width = LOWORD(lParam);
    m_height = HIWORD(lParam);
    updateTargetData(); // 确保此处重建了RTV和交换链缓冲区
    return 0;
}

4. 移除冲突的窗口标志

去掉ImGuiWindowFlags_AlwaysAutoResize,该标志会覆盖你手动设置的窗口大小,导致尺寸不匹配:

ImGui::Begin(windowTitle, nullptr, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoTitleBar);

内容的提问来源于stack exchange,提问作者ITF_ Ph3r0X

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 03:40:43