如何让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
相关产品推荐
相关产品推荐

