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

如何让ImGui UI渲染在SDL_UpdateWindowSurface绘制的内容之上?

问题描述

我尝试将CPU软渲染器与ImGui结合使用。此前,我通过更新windowSurface的像素,使用SDL_UpdateWindowSurface完成渲染,代码如下:

void WindowApp::updateCanvas(uint8_t* pixels, int width, int height, int channel) {
        SDL_LockSurface(windowSurface);
        Uint32* surfacePixels = (Uint32*)windowSurface->pixels;
        for(int i=0;i<height;i++){
            for(int j=0;j<width;j++){
                int index = i*width + j;
                Uint32 _color = SDL_MapRGB(
                        windowSurface->format,
                        pixels[index * channel + 0],
                        pixels[index * channel + 1],
                        pixels[index * channel + 2]);
                surfacePixels[index] = _color;
            }
        }
        SDL_UnlockSurface(windowSurface);
        SDL_UpdateWindowSurface(window);
    }

我注意到ImGui使用SDL_GL_SwapWindow渲染UI,但将两者结合时,只会显示软渲染的原始内容,无法显示UI;删除SDL_UpdateWindowSurface后才能正常显示UI。请问如何实现将ImGui UI渲染在软渲染内容之上?当前错误代码如下:

void WindowApp::updateCanvas(uint8_t* pixels, int width, int height, int channel) {
        // Start the Dear ImGui frame
        ImGui_ImplOpenGL3_NewFrame();
        ImGui_ImplSDL2_NewFrame();
        ImGui::NewFrame();

        static float f = 0.0f;
        static int counter = 0;

        ImGui::Begin("Hello, world!");                          // Create a window called "Hello, world!" and append into it.

        ImGui::Text("This is some useful text.");               // Display some text (you can use a format strings too)

        ImGui::SliderFloat("float", &f, 0.0f, 1.0f);            // Edit 1 float using a slider from 0.0f to 1.0f
        if (ImGui::Button("Button"))                            // Buttons return true when clicked (most widgets return true when edited/activated)
            counter++;
        ImGui::SameLine();
        ImGui::Text("counter = %d", counter);

        ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
        ImGui::End();
        //SDL_LockSurface(windowSurface);
        Uint32* surfacePixels = (Uint32*)windowSurface->pixels;
        for(int i=0;i<height;i++){
            for(int j=0;j<width;j++){
                int index = i*width + j;
                Uint32 _color = SDL_MapRGB(
                        windowSurface->format,
                        pixels[index * channel + 0],
                        pixels[index * channel + 1],
                        pixels[index * channel + 2]);
                surfacePixels[index] = _color;
            }
        }

        //SDL_UnlockSurface(windowSurface);
        ImGui::Render();
        ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
        SDL_UpdateWindowSurface(window);
        SDL_GL_SwapWindow(window);
    }
解决方案

核心问题

你同时混用了SDL软件表面渲染(SDL_UpdateWindowSurface)和OpenGL硬件渲染(SDL_GL_SwapWindow)两条完全独立的渲染路径。SDL_UpdateWindowSurface会直接把软件表面的像素覆盖到窗口显示,之后调用SDL_GL_SwapWindow的OpenGL内容会被覆盖,反之亦然。要让ImGui显示在软渲染内容之上,必须统一渲染路径。

推荐方案:将软渲染内容转为OpenGL纹理渲染

把CPU生成的像素数据上传到OpenGL纹理,用OpenGL绘制纹理作为背景,再渲染ImGui,全程走OpenGL路径,自然实现层级叠加。

步骤1:初始化OpenGL纹理

在窗口初始化时创建一个匹配窗口尺寸的纹理:

GLuint renderTexture;
void WindowApp::initGLTexture(int width, int height) {
    glGenTextures(1, &renderTexture);
    glBindTexture(GL_TEXTURE_2D, renderTexture);
    // 设置纹理参数
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    // 初始化空纹理(格式根据你的软渲染像素格式调整,这里假设是RGB)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
}

步骤2:修改updateCanvas函数

移除软件表面相关操作,改为更新OpenGL纹理并绘制,最后渲染ImGui:

void WindowApp::updateCanvas(uint8_t* pixels, int width, int height, int channel) {
    // 1. 更新OpenGL纹理:将软渲染的像素数据上传到纹理
    glBindTexture(GL_TEXTURE_2D, renderTexture);
    // 注意格式匹配:如果你的pixels是RGB,用GL_RGB;如果是RGBA用GL_RGBA
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, pixels);

    // 2. 绘制纹理覆盖整个窗口(简单的全屏四边形)
    glClear(GL_COLOR_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex2f(-1, -1);
        glTexCoord2f(1, 0); glVertex2f(1, -1);
        glTexCoord2f(1, 1); glVertex2f(1, 1);
        glTexCoord2f(0, 1); glVertex2f(-1, 1);
    glEnd();
    glDisable(GL_TEXTURE_2D);

    // 3. 正常渲染ImGui
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplSDL2_NewFrame();
    ImGui::NewFrame();

    static float f = 0.0f;
    static int counter = 0;

    ImGui::Begin("Hello, world!");
    ImGui::Text("This is some useful text.");
    ImGui::SliderFloat("float", &f, 0.0f, 1.0f);
    if (ImGui::Button("Button"))
        counter++;
    ImGui::SameLine();
    ImGui::Text("counter = %d", counter);
    ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
    ImGui::End();

    ImGui::Render();
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

    // 4. 交换OpenGL缓冲,完成渲染
    SDL_GL_SwapWindow(window);
}

注意事项

  • 确保窗口初始化时是OpenGL上下文(创建窗口时要设置SDL_WINDOW_OPENGL标志)。
  • 纹理格式要和你的软渲染像素格式匹配,比如如果软渲染输出是RGBA,要把GL_RGB改为GL_RGBA。
  • 全屏四边形用的是正交投影,如果你已有自己的OpenGL投影矩阵,需要调整适配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:00:30