正三角形だけでフォントをレンダリングする方法——線形代数と数値最適化で「ムリ」を「ムリじゃない」に変えた話

元記事を読む
キュレーターコメント

「正三角形しか置けないゲームでテキストを表示したい」という動機が、SVDや差分進化まで引き出すとは予想外の展開。数理最適化を「自分のユースケース」で試したい人への最高の実例だと思い選びました。

概要

「正三角形しか置けない環境で文字を表示したい」——そんな一見バカバカしい制約から、SVD・差分進化・三角形分割という本格的な数理最適化の旅が始まった。Qiitaに投稿されたこの記事は、実用とロマンが同居する、エンジニアの本能を刺激する一本だ。

背景:なぜ「正三角形しか置けない」のか

著者が遊んでいる3Dジオラマ系ゲーム「デジタルクラフト」では、シーンに配置できるオブジェクトが限られており、テキストを直接3D空間に置く機能がない。前作では uGUI Text(Unity の旧テキスト機能)で実現できていたが、2021年以降はLegacy扱いとなり、次世代タイトルでは廃止された。そこで著者が取った発想が「正三角形オブジェクトを変形して文字を作る」というものだった。

技術的核心:TRS変換とせん断の壁

3Dオブジェクトの配置には一般的に TRS変換(Translation・Rotation・Scale) が使われる。しかし正三角形を任意の三角形に変形するには「せん断(Shear)」が必要で、TRSだけでは表現できない。この制約を突破するため、著者は複数の数値最適化手法を試みる:

  • SVD(特異値分解):行列を U Σ V^T に分解し、回転・スケール成分を抽出。ただし「せん断なし」という前提があるため、任意三角形への変換には不足。
  • 差分進化(scipy.optimize.differential_evolution:グローバル最適化の王道。探索空間が広くても収束しやすいが、精度と速度のトレードオフがある。
  • fsolve(連立非線形方程式):局所解に引っかかりやすいが、初期値を上手く設定すれば高精度。
  • least_squares(最小二乗法):残差最小化による安定解。
from scipy.optimize import differential_evolution, fsolve, least_squares

# 差分進化による変換パラメータ探索(例)
result = differential_evolution(cost_func, bounds, seed=42)

文字の三角形分割:Earcut・Triangle・TriWild

変換手法が決まっても、次の問題がある。フォントのグリフ(文字の輪郭ポリゴン)を三角形の集合に分割しなければならない。これは「三角形分割(Triangulation)」と呼ばれる古典的な計算幾何学の問題だ。著者が試したのは以下の3ライブラリ:

  • Earcut:軽量・高速。Web/ゲーム向けに広く使われる耳刈り法。
  • Triangle:Delaunay三角形分割の定番Cライブラリ。品質制御オプションが豊富。
  • TriWild:近年の研究成果。ワイルドな形状にも対応する頑健な三角形分割。

最終的に著者はこれらを組み合わせて「フォントグリフ → ポリゴン → 三角形集合 → 各三角形を正三角形から変換する最適パラメータ計算 → ゲームに配置」というパイプラインを実現した。

まとめ・所感

この記事が面白いのは、ゲームの制約という具体的な動機から、線形代数・数値最適化・計算幾何学という本格的な数学ツールを次々に召喚している点だ。「ムリ」と思われた問題をブレイクダウンして、既存ライブラリを組み合わせて解くアプローチは、実務エンジニアにも刺さる。

Python + scipy で数値最適化を試してみたい方、Unity での3Dオブジェクト変換に興味がある方、「制約の中でなんとかする」系の技術記事が好きな方——ぜひ元記事で完全なコードと数式を追ってほしいFuture Works セクションにはまだ改善余地が示されており、続報にも期待大だ。