2024/01/12(金) [n年前の日記]
#1 [prog] OpenGLで文字描画をしたい
先日書いた、OpenGLを使って宇宙飛行スクリーンセーバっぽい描画をするプログラムを、せっかくだからスクリーンセーバにしたい。ただ、FPSも描画したいなと。そのためには OpenGL で文字描画をしないといけない。
OpenGL には、文字描画用の便利な機能は標準で用意されていない。そのあたりをどうにかしないといけない。ググったところ、色々方法はあるようで…。
今までは glut/freeglut を使って OpenGL のウインドウを表示していたので、glutBitmapCharacter() を使ってFPS値を描画していたのだけど。スクリーンセーバにする際に、glut を含めることができるのか、ちょっとよく分からなくて…。別の方法で文字描画することになるのかなと…。
OpenGL には、文字描画用の便利な機能は標準で用意されていない。そのあたりをどうにかしないといけない。ググったところ、色々方法はあるようで…。
- glBitmap() を使うと 0/1 でピクセルを描画できるので、フォントデータ相当の配列を持っておいて、glBitmap() で描画してやる。0/1で描画するのでアンチエイリアスはかからない。
- glut/freeglut を使っているなら、glutBitmapCharacter() が使える。何種類かのビットマップフォントを選べる。アンチエイリアスはかからない。
- Windowsに特化していいなら、wglUseFontBitmaps() が使える。WindowsにインストールされているTTF(フォントファイル)を使って描画できる。アンチエイリアスはかからない。
- Linux環境なら、FTGL というライブラリがあるらしい。freetype というライブラリを使って処理するらしい。
- 使う文字を画像化して、ポリゴンにテクスチャとして貼って描画する方法もある。元画像上でアンチエイリアスをかけておけばそのまま表示される。おそらくこれが、一番自由度が高くて、見た目も良いのではないか。たぶん。
今までは 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
_Makefile
make でビルド。01_drawtextwgl.exe を実行した結果は以下。
たしかに描画できてる。文字列の内容が変化する場合でも描画できている模様。
ハマった点は…。フォント種類を変えてみても反映されなくて悩んだけれど、CreateFont() に与える CHARSET が SJIS_CHARSET のままになっていたのが原因だった。英数字のみのフォントを使いたい時は、ASCII_CHARSET を指定しないといかんのだな…。
フォント名等を渡すときに警告が出てきて悩んだけれど、あちこちのサンプルを真似して _T("xxxx") で囲んだら警告が出なくなった。何をしているマクロ(?)なのかよく分かってない…。
日本語文字列を描画したい時は wchar_t とやらが絡んでくるらしいけど、今回は英数字のみ描画できればいいので、char に書き換えてしまった。日本語表示については今後の課題ということで…。
以下のページを参考にさせてもらった。ありがたや。
_文字列描画 - 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 を実行した結果は以下。
たしかに描画できてる。文字列の内容が変化する場合でも描画できている模様。
ハマった点は…。フォント種類を変えてみても反映されなくて悩んだけれど、CreateFont() に与える CHARSET が SJIS_CHARSET のままになっていたのが原因だった。英数字のみのフォントを使いたい時は、ASCII_CHARSET を指定しないといかんのだな…。
フォント名等を渡すときに警告が出てきて悩んだけれど、あちこちのサンプルを真似して _T("xxxx") で囲んだら警告が出なくなった。何をしているマクロ(?)なのかよく分かってない…。
日本語文字列を描画したい時は wchar_t とやらが絡んでくるらしいけど、今回は英数字のみ描画できればいいので、char に書き換えてしまった。日本語表示については今後の課題ということで…。
*1: 考えてみたら、Windows上でしか動かないプログラムを書くのだから、フツーは Visual C++ を使うよなと…。Visual C++ が有償だった昔と違って、今は誰でも Visual C++ を利用できる状況になっているわけだし…。gcc/g++ で試していること自体、「え? なんでわざわざそんなものを使うの?」と言われそうな気もする…。
[ ツッコむ ]
以上です。