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++ で試していること自体、「え? なんでわざわざそんなものを使うの?」と言われそうな気もする…。
[ ツッコむ ]
以上です。
