使用SDL2 TTF与glDrawPixels渲染文本出现翻转及噪点问题求助
问题
我正在为原本使用GLUT的库添加SDL2窗口系统支持,其中包含文本渲染功能。当前遇到的问题是:文本渲染的代码位置无法访问Window或Renderer对象。此前该库用GLUT的glutBitmapCharacter实现文本渲染。
我尝试用glDrawPixels解决,已经接近成功,但输出的文本是翻转的,且上方带有噪点。调整GL_UNPACK_XXX系列函数后仍未解决,求解决思路。
以下是可复现问题的示例程序:
main.cpp
#include <SDL2/SDL.h> #include <SDL2/SDL_ttf.h> #include <GL/glew.h> #include <string> #include <stdint.h> #include <chrono> #include <thread> const int width = 400; const int height = 400; TTF_Font* font = nullptr; // THIS FUNCTION IS THE ONLY THING I CAN CHANGE! void drawText(std::string text) { SDL_Color color = { static_cast<Uint8>(255), static_cast<Uint8>(0), static_cast<Uint8>(255), static_cast<Uint8>(255) }; SDL_Surface* textRender = TTF_RenderUTF8_Blended(font, text.c_str(), color); SDL_Surface* surface = SDL_ConvertSurfaceFormat(textRender, SDL_PIXELFORMAT_RGBA8888, 0); SDL_FreeSurface(textRender); glPushAttrib(GL_BLEND); glPushAttrib(GL_BLEND_EQUATION); glPushAttrib(GL_CLIENT_PIXEL_STORE_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glDrawPixels(surface->w, surface->h, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels); glPopAttrib(); glPopAttrib(); glPopAttrib(); SDL_FreeSurface(surface); } // EVERYTHING BELOW HERE IS ONLY TO PROVIDE A MINIMUM REPRODUCABLE EXAMPLE! void loop(int64_t frame) { glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glRasterPos2f(-1.0, 0); drawText(std::string("Hello World!") + std::to_string(frame)); } int main() { SDL_InitSubSystem(SDL_INIT_VIDEO); TTF_Init(); auto* window = SDL_CreateWindow("sdl-text", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL); auto* context = SDL_GL_CreateContext(window); glewInit(); bool quit = false; int64_t frame = 0; font = TTF_OpenFont("arial.ttf", 42); if (!font) { quit = true; } while (!quit) { loop(frame++); SDL_GL_SwapWindow(window); SDL_Event e; while (SDL_PollEvent(&e)) { switch(e.type) { case SDL_QUIT: quit = true; break; } } std::this_thread::sleep_for(std::chrono::milliseconds(16)); } TTF_Quit(); SDL_GL_DeleteContext(context); SDL_DestroyWindow(window); SDL_QuitSubSystem(SDL_INIT_VIDEO); return 0; }
编译命令
g++ main.cpp -lSDL2 -lSDL2_ttf -lGLEW -lGL
解决方案
问题根源
- 文本翻转:SDL Surface的Y轴方向是从上到下,而OpenGL光栅位置的Y轴是从下到上,直接调用
glDrawPixels会导致上下颠倒。 - 顶部噪点:
GL_UNPACK_ROW_LENGTH被错误设置为Surface的pitch(每行字节数),但该参数要求的是每行像素数,错误设置会导致内存读取越界,出现噪点。
修复步骤
1. 修正GL_UNPACK_ROW_LENGTH参数
将glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch);改为glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->w);,或者直接省略该设置(默认值即为图像宽度)。
2. 翻转文本Y轴
推荐使用OpenGL的glPixelZoom配合光栅位置调整来实现,高效且不破坏原有状态:
- 先获取当前光栅位置,将Y坐标偏移文本高度,再用
glPixelZoom(1, -1)翻转Y轴方向。
修改后的drawText函数
void drawText(std::string text) { SDL_Color color = { static_cast<Uint8>(255), static_cast<Uint8>(0), static_cast<Uint8>(255), static_cast<Uint8>(255) }; SDL_Surface* textRender = TTF_RenderUTF8_Blended(font, text.c_str(), color); SDL_Surface* surface = SDL_ConvertSurfaceFormat(textRender, SDL_PIXELFORMAT_RGBA8888, 0); SDL_FreeSurface(textRender); // 合并PushAttrib减少调用开销 glPushAttrib(GL_BLEND | GL_CLIENT_PIXEL_STORE_BIT | GL_TRANSFORM_BIT); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 修正UNPACK参数,ROW_LENGTH用每行像素数 glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE); glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE); glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->w); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 调整光栅位置并翻转Y轴 GLdouble rasterX, rasterY; glGetDoublev(GL_CURRENT_RASTER_POSITION_X, &rasterX); glGetDoublev(GL_CURRENT_RASTER_POSITION_Y, &rasterY); glRasterPos2d(rasterX, rasterY + surface->h); glPixelZoom(1.0, -1.0); glDrawPixels(surface->w, surface->h, GL_RGBA, GL_UNSIGNED_BYTE, surface->pixels); glPopAttrib(); SDL_FreeSurface(surface); }
额外优化
- 合并
glPushAttrib的参数,减少状态保存的开销 - 如果不需要严格保留原光栅位置,也可以直接计算新位置,省略
glGetDoublev的步骤
验证编译
使用原编译命令即可正常编译运行:
g++ main.cpp -lSDL2 -lSDL2_ttf -lGLEW -lGL
内容的提问来源于stack exchange,提问作者Jhonny007
相关产品推荐
相关产品推荐

