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

使用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
解决方案

问题根源

  1. 文本翻转:SDL Surface的Y轴方向是从上到下,而OpenGL光栅位置的Y轴是从下到上,直接调用glDrawPixels会导致上下颠倒。
  2. 顶部噪点: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 16:05:04