
2026/02/08 9:58
ファントレンダリングの基礎から
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
著者は、GPU上で鮮明かつスケーラブルなテキストを可能にする、Raw Bitmap Rasterization を Signed Distance Field(SDF)レンダリングに置き換えるカスタム TrueType フォントパイプラインを構築しました。
cmap・loca・glyf といった必須 TTF テーブルを手動で解析することで、FreeType のような外部ライブラリに頼らずにグリフ抽出とキャッシュ管理を細かく制御できます。グリフの輪郭は二次ベジェ曲線から構成されており、パーサはオフ―カーブ点が現れた場合に暗示的なポイントを挿入し、変換付きサブグリフを参照する複合グリフも処理します。また、スキャンライン交差判定で各ピクセルの winding number を算出します。得られた高解像度ビットマップは、広がりカーネル内でピクセル単位の距離を計算し、結果を 8‑bit アトラスへマッピングすることで SDF テクスチャに変換されます。GLSL のフラグメントシェーダでは、threshold と smoothing ユニフォームで制御される smoothstep を距離値に適用し、任意の解像度でアンチエイリアス付きエッジを生成します。この手法は従来のビットマップレンダリングに伴う aliasing と低解像度での拡大時の粗さを解消し、モダンな GPU‑centric フォントソリューションと整合性があります。将来的には kerning メトリクス(kern)やさらなるタイポグラフィ的忠実度をパイプラインに組み込む予定です本文
2026年2月1日
フォントレンダリングは、よく当たり前だと考えられがちな技術です。
それなしでコンピュータを使うことを想像するのは難しいでしょう。
しかし、本当にどれほど難しいのでしょうか?実際には思っているよりもずっと大変です。
1. 独自フォントレンダラーを構築する理由
- 深い理解 – インターネットを利用できるようにしてくれる基盤技術を把握できます。
- 作業量の直感 – Webページや GUI を描画するために必要なものを直感的に掴めます。
- パフォーマンス洞察 – フォントキャッシュが重要である理由と、レンダリング時間を短縮する方法を学べます。
- 拡張性 – SDF を使ったプログラム的境界線の追加など、機能拡張が可能です。
2. TTF ファイル形式
2.1 高レベル概要
TTF は Unicode コードポイント → グリフ情報をマップします。
主なテーブルは以下の通りです。
| テーブル | 用途 |
|---|---|
| cmap | コードポイントをグリフインデックスに変換 |
| loca | グリフインデックスから テーブル内のオフセットへ |
| glyf | 実際のグリフ形状データを格納 |
その他有用なテーブル(必ずしも必要ではない):
,head
– グローバルフォント情報maxp
– 上端/下端、全体縦サイズhhea
– 各グリフの水平進行量hmtx
– カーニングペアkern
2.2 文字から形状へ
- cmap を使ってグリフインデックスを取得。
- loca でそのオフセットを探す。
- glyf から形状を抽出。
3. グリフ解析
TTF のグリフは 輪郭(contour)で構成され、各輪郭は一連の二次ベジェ曲線で記述されます。
- 曲線は開始点・制御点・終了点の三点からなる。
- 輪郭は閉じたループで、外側の輪郭は時計回り、穴(inner contour)は反時計回りに描かれる。
3.1 ポイント構造
struct GlyphPoint { B8 on_curve; // 曲線上にある場合 true V2 position; // 修正済み(絶対座標) };
3.2 圧縮処理
- 欠損点 – オフ曲線点が連続している場合、両者の中点に暗黙的な on‑curve 点を挿入。
- 直線ケース – 二つの連続した on‑curve 点は直線であり、退化ベジェとして扱う。
便利なモデル例:
struct GlyphCurve { V2 point; // 曲線開始点 V2 control; }; // この曲線の終点は次の曲線の開始点に暗黙的に設定される。
3.3 合成グリフ
一部のグリフはサブグリフ(例えばアクセント)から構築されます。
仕様では、これらを結合する際に適用すべき変換が定義されています。
4. グリフラスタ―化
- ターゲットビットマップ – アトラスのどこへ描画するか決める。
- 各スキャンライン(
)で:y- ターゲットピクセル空間からグリフ座標系に変換。
- すべてのベジェ曲線との交点を求める:
- 二次方程式を解き、
を満たす値を取得。0 ≤ t ≤ 1 - 分母がゼロになる退化(直線)ケースに対処。
- 二次方程式を解き、
- ウォーリングルール – 交点の数 (+1 / -1) をカウントして「内部」か「外部」か判定。
- 内部にあるピクセルを塗りつぶす。
結果: 生のビットマップアトラスが得られる。
5. 生ビットマップの欠点
| 問題 | 原因 |
|---|---|
| アンチエイリアスなし | ピクセルごとに二値決定を行うため。 |
| スケーリングが悪い | ビットマップフォントは拡大縮小すると品質が落ちる。 |
| 高品質には大きなアトラスが必要 | 1つのグリフあたり多くのピクセルが必要。 |
6. Signed Distance Fields (SDF)
6.1 SDFとは?
SDF は各ピクセルに対し、形状の最も近いエッジまでの符号付き距離を格納します:
- 内部では負値
- 外部では正値
この滑らかな勾配は、ハードウェアが任意解像度で線形補間できるようにする。
6.2 SDF の構築
- 高解像度ビットマップを描画(例:高さ=64)。
- ターゲット SDF ビットマップの各ピクセルについて:
- カーネル(拡散係数、例 4)内で反対側のピクセルまでの距離を検索。
- 距離を記録し
にクリップ。[0, max]
- 距離を 8 ビット値へマッピングしてアトラスに保存。
6.3 GLSL でのレンダリング
#version 330 core uniform sampler2D atlas_image; uniform vec3 text_color; uniform float threshold; // 内部/外部の切り替え点 uniform float smoothing; // 遷移帯域幅 in vec2 glyph_tex_coord; out vec4 frag_color; void main() { float dist = texture(atlas_image, glyph_tex_coord).r; float alpha = smoothstep(threshold - smoothing, threshold + smoothing, dist); frag_color = vec4(text_color, alpha); }
は形状が内部から外部へ変わる位置を決めます。threshold
はその遷移の滑らかさを調整します。smoothing
6.4 結果
SDF を使ったレンダリングは、すべての解像度でクリーンに見え、複数高解像度ビットマップを保存するよりメモリ効率が良いです。
7. 要約と参考資料
- 独自レンダラーを構築するとフォント技術への深い洞察が得られます。
- TTF の解析はコードポイント → グリフインデックス → 形状データのマッピングで構成されます。
- 生ビットマップのラスタ―化は単純ですが制限があります;SDF がスケーリングとアンチエイリアス問題を解決します。
参考資料
| 資源 | 内容 |
|---|---|
| Sebastian Lague – Coding Adventure: Rendering Text | 入門ビデオ |
| Sphaerophoria’s series | 上級テクニック |
| Tsoding – Rasterizing Splines in C | ベジェ曲線の基礎 |
| stb_truetype library | 参考実装 |
実装は[こちら](リンク省略)で確認できます。