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

Win32 C++程序中Unity与ImGui基于DX11同窗口分层渲染问题

解决方案:Win32 DX11程序中Unity场景与ImGui层级控制

核心问题分析

你遇到的本质问题是:Win32子窗口会默认绘制在父窗口之上,而ImGui是渲染在主窗口(父窗口)的DX目标上,因此Unity子窗口会覆盖ImGui内容;同时Unity内部可能存在强制窗口置顶的逻辑,导致BringWindowToTop无法生效。

下面提供两种可行方案,其中纹理共享方案为最优解。


方案一:调整窗口层级与透明子窗口(无需修改Unity核心逻辑)

通过创建双层子窗口分离Unity渲染与ImGui绘制,确保ImGui窗口层级更高:

  • 创建Unity子窗口:
    使用以下样式创建Unity专属子窗口,绑定为主窗口的子控件:

    HWND unityHwnd = CreateWindowEx(
        0,
        L"UnityWindowClass", // Unity注册的窗口类名
        L"UnityRender",
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
        x, y, width, height, // 指定Unity场景的显示区域
        mainHwnd, // 主窗口句柄
        NULL,
        GetModuleHandle(NULL),
        NULL
    );
    

    注意:禁止添加WS_EX_TOPMOST扩展样式,避免Unity窗口强制置顶。

  • 创建ImGui透明子窗口:
    创建一个覆盖整个主窗口的透明子窗口,专门用于渲染ImGui,确保其Z序高于Unity子窗口:

    HWND imguiHwnd = CreateWindowEx(
        WS_EX_TRANSPARENT | WS_EX_LAYERED, // 透明+分层属性,允许鼠标穿透
        L"ImGuiWindowClass",
        L"ImGuiRender",
        WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
        0, 0, mainWidth, mainHeight,
        mainHwnd,
        NULL,
        GetModuleHandle(NULL),
        NULL
    );
    // 将ImGui窗口置于Unity窗口之上
    SetWindowPos(imguiHwnd, unityHwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
    
  • ImGui初始化绑定:
    将ImGui的渲染目标绑定到imguiHwnd的DX SwapChain,确保UI绘制在透明子窗口上,不会被Unity遮挡。

  • 鼠标事件处理:
    由于ImGui窗口设置了WS_EX_TRANSPARENT,需要在主窗口的消息循环中,将非ImGui区域的鼠标事件转发给Unity子窗口,保证Unity能正常响应交互。


方案二:DX11纹理共享(最优解,彻底规避窗口层级问题)

通过DX11的纹理共享机制,将Unity的渲染结果作为纹理传入主程序的DX上下文,直接在主窗口绘制Unity纹理后叠加ImGui,完全消除窗口Z序冲突:

1. Unity端配置(C#代码)

  • 创建兼容DX11的RenderTexture,并设置为相机渲染目标:
    RenderTexture unityRenderTex = new RenderTexture(1920, 1080, 24, RenderTextureFormat.ARGB32);
    unityRenderTex.enableRandomWrite = true;
    unityRenderTex.Create();
    Camera.main.targetTexture = unityRenderTex;
    
  • 获取共享纹理句柄:
    通过Unity的原生接口获取RenderTexture对应的DX11纹理,并生成共享句柄:
    IntPtr nativeTexPtr = unityRenderTex.GetNativeTexturePtr();
    ID3D11Texture2D dxTex = (ID3D11Texture2D)Marshal.GetObjectForIUnknown(nativeTexPtr);
    IDXGIResource dxgiRes = dxTex as IDXGIResource;
    IntPtr sharedHandle;
    dxgiRes.GetSharedHandle(out sharedHandle);
    // 将sharedHandle传递给Win32主程序(可通过命名管道、共享内存等IPC方式)
    
    注:需要引用System.Runtime.InteropServices,并定义DX11接口的COM封装(可通过NuGet或手动定义)。

2. Win32主程序端实现(C++代码)

  • 打开共享纹理:
    在主程序的DX11设备中,通过Unity传递的共享句柄获取纹理:
    ID3D11Texture2D* pSharedTex = nullptr;
    HRESULT hr = pD3DDevice->OpenSharedResource(
        sharedHandle,
        __uuidof(ID3D11Texture2D),
        (void**)&pSharedTex
    );
    
  • 创建Shader资源视图:
    ID3D11ShaderResourceView* pSRV = nullptr;
    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
    srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MipLevels = 1;
    pD3DDevice->CreateShaderResourceView(pSharedTex, &srvDesc, &pSRV);
    
  • 主渲染循环流程:
    // 1. 清空主窗口渲染目标
    pImmediateContext->ClearRenderTargetView(pRenderTargetView, clearColor);
    // 2. 绘制Unity共享纹理到指定区域
    SetShaderAndSRV(pSRV); // 绑定自定义Shader和纹理
    DrawQuad(x, y, width, height); // 绘制Unity场景区域
    // 3. 渲染ImGui(此时ImGui直接绘制在主渲染目标上,自然在Unity纹理之上)
    ImGui::Render();
    ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
    // 4. 呈现画面
    pSwapChain->Present(1, 0);
    

关键注意事项

  • 若使用子窗口方案,需检查Unity是否在内部调用了SetWindowPos强制置顶,如有则需修改Unity的启动参数或插件代码禁用该逻辑。
  • 纹理共享方案需确保Unity与主程序的DX11设备使用相同的功能级别,避免兼容性问题。
  • 纹理共享的IPC通信需保证线程安全,可使用命名管道或共享内存实现。

内容的提问来源于stack exchange,提问作者Harald Hansen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 08:35:20