opengl 使用SDL2 TTF和glDrawPixels渲染文本

p8h8hvxi  于 2023-08-04  发布在  其他
关注(0)|答案(1)|浏览(151)

我目前正在为一个库添加对SDL2窗口系统的支持,该库目前使用GLUT。这包括渲染文本。有问题的部分是,我无法访问文本呈现位置的WindowRenderer。以前的文本是使用glutBitmapCharacter函数用GLUT呈现的。
通过使用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

oxf4rvwz

oxf4rvwz1#

好的,看起来OpenGL以像素为单位获取行长度,而SDL以字节为单位提供。这很容易解决。
我们也不需要将曲面转换为RGBA。我们可以直接告诉OpenGL绘制BGRA(尽管SDL说它提供AGBR,但它是有效的)。
最后,我们在使用glPixelZoom(1, -1)绘制之前翻转图像。为了抵消翻转,我们必须向上移动高度。
解决方案如下:

void drawText(std::string text) {
  SDL_Color color = {
    static_cast<Uint8>(255),
    static_cast<Uint8>(0),
    static_cast<Uint8>(0),
  };

  // We dont't need to convert the surface format.
  SDL_Surface* surface = TTF_RenderUTF8_Blended(font, text.c_str(), color);

  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);

  // SDL uses bytes, while OpenGL uses pixels.
  glPixelStorei(GL_UNPACK_ROW_LENGTH, surface->pitch / surface->format->BytesPerPixel);

  // Get the current raster position.
  std::array<float, 4> pos;
  glGetFloatv(GL_CURRENT_RASTER_POSITION, pos.data());

  // Because the texture is upside down we move it up by the height and flip it.
  glWindowPos2f(pos[0], pos[1] + surface->h);
  glPixelZoom(1, -1);

  // Use BGRA as a format.
  glDrawPixels(surface->w, surface->h, GL_BGRA, GL_UNSIGNED_BYTE, surface->pixels);

  glPopAttrib();
  glPopAttrib();
  glPopAttrib();

  SDL_FreeSurface(surface);
}

字符串

相关问题