mieki256's diary



2024/01/12(金) [n年前の日記]

#1 [prog] OpenGLで文字描画をしたい

先日書いた、OpenGLを使って宇宙飛行スクリーンセーバっぽい描画をするプログラムを、せっかくだからスクリーンセーバにしたい。ただ、FPSも描画したいなと。そのためには OpenGL で文字描画をしないといけない。

OpenGL には、文字描画用の便利な機能は標準で用意されていない。そのあたりをどうにかしないといけない。ググったところ、色々方法はあるようで…。
今までは glut/freeglut を使って OpenGL のウインドウを表示していたので、glutBitmapCharacter() を使ってFPS値を描画していたのだけど。スクリーンセーバにする際に、glut を含めることができるのか、ちょっとよく分からなくて…。別の方法で文字描画することになるのかなと…。

wglUseFontBitmapsを使ってみる :

Windowsに特化したプログラムになってしまうけど、まずは wglUseFontBitmaps() を使って描画できそうか試してみた。そもそも、Windows用のスクリーンセーバを書く時点で、Windows に特化してしまうわけだし…。

以下のページを参考にさせてもらった。ありがたや。

_文字列描画 - OpenGLプログラミングメモ - atwiki(アットウィキ)

元記事のソースは日本語表示ができるようになっているけれど、手元の環境が MinGW(gcc/g++) だったせいか、そのままではビルドできなかったので、英数字のみ(ASCIIのみ)対応させるように修正して実験してみた。 *1

環境は、Windows10 x64 22H2 + MinGW (gcc 6.3.0) or MSYS2 MINGW64 (gcc 13.2.0) + freeglut。

_01_drawtextwgl.cpp
// draw text on OpenGL for wglUseFontBitmaps

#include <GL/freeglut.h>
#include <stdio.h>
#include <tchar.h>

#define WIDTH 512
#define HEIGHT 288

static int count = 0;

class GLFONT
{
public:
  HFONT Hfont;
  HDC Hdc;

  // create font
  GLFONT(LPCTSTR fontname, int size)
  {
    Hfont = CreateFont(
        size,                    // font height
        0,                       // font width
        0,                       // テキストの角度
        0,                       // Orientation
        FW_REGULAR,              // font weight
        FALSE,                   // italic
        FALSE,                   // underline
        FALSE,                   // strikeout
        ANSI_CHARSET,            // charset
        OUT_DEFAULT_PRECIS,      // OutPrecision
        CLIP_DEFAULT_PRECIS,     // ClipPrecision
        ANTIALIASED_QUALITY,     // Quality
        FIXED_PITCH | FF_MODERN, // Pitch And Family
        fontname                 // face name
    );

    Hdc = wglGetCurrentDC();
    SelectObject(Hdc, Hfont);
  }

  // draw string
  void DrawString(int x, int y, char *format, ...)
  {
    int Length = 0;
    int list = 0;

    if (format == NULL)
      return;

    Length = strlen(format);
    list = glGenLists(Length);

    for (int i = 0; i < Length; i++)
    {
      wglUseFontBitmaps(Hdc, format[i], 1, list + i);
    }

    glDisable(GL_LIGHTING);
    glRasterPos2i(x, y);

    // draw
    for (int i = 0; i < Length; i++)
    {
      glCallList(list + i);
    }

    glEnable(GL_LIGHTING);

    // delete displey list
    glDeleteLists(list, Length);

    list = 0;
    Length = 0;
  }
};

GLFONT *font;

// Draw OpenGL
void display(void)
{
  glClearColor(0.2, 0.4, 0.8, 0.0);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  // draw text
  glColor4f(1.0, 1.0, 1.0, 1.0); // set text color

  font->DrawString(40, 40, (char *)"Hello World");

  {
    char buf[256];
    sprintf(buf, "count %d", count);
    font->DrawString(40, 100, buf);
  }

  glutSwapBuffers();
  count++;
}

void idle(void)
{
  glutPostRedisplay(); // redraw
}

void keyboard(unsigned char key, int x, int y)
{
  switch (key)
  {
  case '\x1B':
  case 'q':
    // Exit on escape or 'q' key press
    glutLeaveMainLoop();
    // exit(EXIT_SUCCESS);
    break;
  }
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
  glutInitWindowSize(WIDTH, HEIGHT);
  // glutInitWindowPosition(100, 100);

  glutCreateWindow("Draw string");

  glutKeyboardFunc(keyboard);
  glutDisplayFunc(display);
  glutIdleFunc(idle);

  glOrtho(0, WIDTH, HEIGHT, 0, -1, 1);
  font = new GLFONT(_T("Courier New"), 24);

  glutMainLoop();
  return 0;
}

_Makefile
ifeq ($(MSYSTEM),MINGW64)

# --------------------
# MSYS2 MINGW64 (gcc 13.2.0)

01_drawtextwgl.exe: 01_drawtextwgl.cpp Makefile
    g++ $< -o $@ -lglu32 -D FREEGLUT_STATIC -lfreeglut -lopengl32 -lwinmm -lgdi32 -static

else

# --------------------
# MinGW (gcc 6.3.0)

01_drawtextwgl.exe: 01_drawtextwgl.cpp Makefile
    g++ $< -o $@ -lglu32 -D FREEGLUT_STATIC -lfreeglut_static -lopengl32 -lwinmm -lgdi32

endif

.PHONY: clean
clean:
    rm -f *.exe
    rm -f *.o

make でビルド。01_drawtextwgl.exe を実行した結果は以下。

01_drawtextwgl_ss.gif

たしかに描画できてる。文字列の内容が変化する場合でも描画できている模様。

ハマった点は…。フォント種類を変えてみても反映されなくて悩んだけれど、CreateFont() に与える CHARSET が SJIS_CHARSET のままになっていたのが原因だった。英数字のみのフォントを使いたい時は、ASCII_CHARSET を指定しないといかんのだな…。

フォント名等を渡すときに警告が出てきて悩んだけれど、あちこちのサンプルを真似して _T("xxxx") で囲んだら警告が出なくなった。何をしているマクロ(?)なのかよく分かってない…。

日本語文字列を描画したい時は wchar_t とやらが絡んでくるらしいけど、今回は英数字のみ描画できればいいので、char に書き換えてしまった。日本語表示については今後の課題ということで…。

*1: 考えてみたら、Windows上でしか動かないプログラムを書くのだから、フツーは Visual C++ を使うよなと…。Visual C++ が有償だった昔と違って、今は誰でも Visual C++ を利用できる状況になっているわけだし…。gcc/g++ で試していること自体、「え? なんでわざわざそんなものを使うの?」と言われそうな気もする…。

以上、1 日分です。

過去ログ表示

Prev - 2024/01 - Next
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project