ウチツクシ

やったゲームのこと(大体STG)とか作ったゲームのこと

フォントをなるべく低メモリ・低負荷で拡大時もキレイに描画したい

f:id:K-s:20170129151158p:plain

と思ったけど考えたら2Dゲームに於いてはそんなに拡大することってないかも…でもせっかく調べたしアウトプットしとく。シルエットとかこのブログのロゴみたいなのにも使えそうなので。

特に関係もなく重要でもない前置き

ゲーム中のフォントは情報の表示がメインで補助的な位置付けだけれど、画面の見た目を飾る上では結構大きな役割を果たしていると思います。ゲーム制作ツールで作ったゲームがそのツールのデフォルトのフォントのままだと、いかにもそのツールで作った感が拭えない的な。いや別にその感があってもいいんだけど!でもオリジナルなフォントにするとそれだけで結構オリジナル感が出ると思いますし、英数字だけなら自作してもそこまで手間もかからないので、フォントの自作はオススメで自分もするように心がけています。

フォント自作と言っても「よしTTF作るか!」って人は多分少ないと思います。いや自分が知らないだけで他の人はやってたらごめん…多分普通はビットマップ画像で描いていく、よね…やっても Inkscape とかでベクタ画像作るくらいではないでしょうか。そしてベクタ画像で作ってもゲームで使う際にはラスタ化、ビットマップ画像にコンバートすると思います。自分ももちろんビットマップ、直です。そもそもTTFの作り方知らない…

ビットマップにしろTTFなどのベクタにしろDirectXOpenGLのゲームでは一度テクスチャに落とすと思います。ベクタのまま描画は負荷高そうですし。でテクスチャにしたときの1文字の大きさ、これがメモリやら描画時の品質に関わってきます。

本題

テクスチャに置く文字はメモリの関係上できるだけ小さくしたい!
でもそうすると描画時に拡大すると上の画像の上段みたいにモザイク化したりぼやけたりして汚い!
ベクタ描画?そんなのいちいちやってらんない!

わがまま過ぎる…

でもテクスチャサイズ小さくても拡大時にキレイに描画できる。そう Distance field ならね!

……

うん。そもそもそんなに拡大しないだろう… 上の画像はそれぞれ 32x32 → 512x512 に拡大しています。

この Distance field(距離場?)というのは、対象となる輪郭からの距離を色の濃淡で表したもので、地図の等高線のようなものです。この距離情報をテクスチャとして事前に作っておき、描画時には線形補完フィルター付きでそのテクスチャの情報をシェーダで読み取って描画します。テクスチャのサイズも小さく、シェーダもそんなに複雑でないので低メモリ・低負荷描画ができるようです。

これだけでも十分にキレイなんですがテクスチャが小さいとエッジが丸くなりがちという弱点があります。これも味があっていい感じだとは思うけど、調べると同じテクスチャの大きさでその弱点を改良した Multi-channel signed distance field というのもありました。

github.com

従来の Distance field テクスチャは単色のモノクロ画像で距離情報のみ参照していたのが、Multi~の方は3チャンネル使って距離以外にもそれぞれの輪郭情報が含まれているようです。詳しくは分からないけど頂点毎に輪郭情報を変えてエッジを検出しているように見えます。

実行バイナリも公開されているので落として試してみたのが上画像の下段。フォント以外にもSVGや独自の形状データからテクスチャ用画像を生成できます。SVGいけるとのことでブログのロゴで試してみました。

Failed to load shape from SVG file.

…そんな予感はしてた。SVGの文字に飛びついたから…英語で詳しく分からないけどpath要素のみっぽいから調整して突っ込んだがやっぱりダメ。仕方ないのでソース見ているとパスデータでも未対応の命令があるようで、自分のSVGで使われていた"s"命令が未対応だった。(リリース最新Version 1.3時点)なのでs命令をなくすよう調整して再度トライ。出力されたぞ!

f:id:K-s:20170129194801p:plain

…どこから行こうか。情報テクスチャは 64x64、テストレンダーは 512x512 で出力してる。右側のぐちゃっとしてるのは情報テクスチャの解像度が足らないのかも。チとツもやたら開いているし他の位置関係もちょっとおかしい。何より上に伸びてる黒いのは何だこれ…

ここで詰んでしまってSVGは諦めた。お前は無力な奴だ…

糸が見つかった。形状データで出力を試していたとき。ベジェ曲線には2つの制御点が必要とのこと。

f:id:K-s:20170129202120p:plain:w320 f:id:K-s:20170129202131p:plain:w320

自分のSVGには左の図のように1つのノードから1つのハンドルしか出ていない箇所がいくつもあった。それを右のように2つにした。全部した。結果…

f:id:K-s:20170129202735p:plain

空が澄みきった!まだ位置ちょっとおかしいけど、何かものすごいスッキリした。

もうだいぶ満足したんでこの辺で止めようかと思ったんだけど、次の日にリポジトリSVG関係のバグ修正のコミットがあってz命令に続くm命令の修正みたいで。これ絶対あの位置ズレ関係だって思った。タイミング良すぎるだろ、監視されてないこれ… s命令の対応もあった。やばい。

ただバイナリ公開はまだされてなくて、試すにはソースをコンパイルする必要がある。今手元にあるC環境はMinGWだけどこれでいけるかな。Makefileにはg++ってあるから試してみたら、lfreetypeねーぞ言われた。libフォルダにあるだろ!って検索対象に入ってないのかと思い-Llib追加してやったら、通った…まじか。

作成されたexeで画像生成してみたら、あの例の黒い物体がまた出てきて終わったと思ったけど、今度の物体は何か様子が違って穴が空きまくってる。試しにビルドの最適化-O2を-O0にしてみたら…

f:id:K-s:20170129205925p:plain

きた。朝が来た。ちょっと繋がってる部分は情報テクスチャの解像度を上げればキレイになる。ただ最適化切ってるから実行速度だいぶ落ちてるしexeのサイズもでかい。でも完成した。やったよ…

途中から内容変わってきた気がしないでもないけど、ここまで。

このあとめちゃくちゃ拡大回転させてロゴ眺めた。

2017.2.5追記:テスト用プログラム作りました。