Sessions 2024にCode Graphics作品投稿の振り返り
2024年11月16-17日に日本科学未来館で開催されたSESSIONS 2024には現地で参加することはできませんでしたが、作品投稿という形で参加しました。SESSIONSへの投稿は今回が初めてです。本記事では、作品の制作過程や感想などを簡単に記録します。
提出した作品
投稿した部門はCode Graphicsのサブカテゴリ「Classic GLSL Graphics」です。作品の形式は、twiglというサービスで再生可能な、1つのGLSLファイルにまとめられたものに限定されています。
提出した作品は「A Session of Glyph」というタイトルで、このURLからリアルタイムで再生できます。
また、映像として見ることもできます。
スクリーンショット
さらに、公式アーカイブには上映時の映像が収録されています。
すべての26作品が上映された後、参加者と観客による投票が行われ、最終得点は261点で9位となりました。
着想とインスピレーション
最もインスピレーションを受けたのは、橋本麦さんが制作したMONO NO AWAREのMV「かむかもしかもにどもかも!(imai remix)」です。高速に切り替わる似たような文字を見つめていると、字形が本来の意味を超えて、生き物のように見える、不思議で魅力的でした。
私自身も以前からフォントに興味があり、フォント内に含まれるグリフ全体を把握するために、機械学習を用いて潜在空間を3次元に投影し、それをWebGLでナビゲートできる試みをしたことがあります。
当時は雑な作業でしたが、今回のきっかけで手法を改めて検討し直そうと思いました。同時に、GLSLファイル1つにどこまでグリフを詰め込めるか挑戦したいとも考えました。
グリフをGLSLファイルに詰め込む
フォントはNoto CJKにする
今回使用したフォントはNoto Serif CJK jp Mediumです。その理由は以下の通りです。
- グリフが43,027個含まれている。
- ライセンスがSIL Open Font License 1.1である。
グリフを2次ベジェ曲線に変換する
次に、グリフをGLSLで表現するために、fonttoolsを用いて2次ベジェ曲線に変換しました。
グリフを2次ベジェ曲線に変換する
GLSLでは2次ベジェ曲線を簡単にSDF(Signed Distance Function)で表現できます。ここでは、Inigo QuilezさんとMatt Keeterさんのshadertoyを大いに参考にしました。
2次ベジェ曲線ならグリフをSDFで表現できる
変換後の2次ベジェ曲線は、各グリフあたり主に100~150個程度で、リアルタイム処理に十分対応できると期待しました。
グリフごと2次ベジェ曲線の数の分布
グリフたちを繋げる
続いて考えるのは、グリフたちを滑らかにつなげる(あるいはモーフする)方法です。本質的には、二つグループの2次ベジェ曲線をどうつなげる問題です。
二つの2次ベジェ曲線を繋げる
まずは、2次ベジェ曲線を滑らかに繋げる方法について考えます。例えば、2次ベジェ曲線
これにより、G1連続が保たれるため、滑らかな動きが可能になります。さらに、この3次ベジェ曲線を2つの2次ベジェ曲線で近似[1]することで、GLSL内での表現が可能になります。以下の図では、赤線が3次ベジェ曲線、青線がそれを近似した2つの2次ベジェ曲線です。
この方法は様々なケースに対応でき、以下のデモで直感的に確認できます。
2次ベジェ曲線のグループを繋げる
次に、複数の2次ベジェ曲線を含むグループ同士を滑らかに繋げる方法を検討します。例えば、曲線グループ
ここで求めたいのは、「全体的に最も短くて滑らかな」繋ぎ方です。この目標を数値的に定義すると、以下の条件が挙げられます。
- 接続点の角度が直角に近いほど良い。
- 曲線の長さが短いほど良い。
イメージは下記の図のように、赤線を選べる
赤線を選ぶ
曲線
この値
最適な繋ぎ方は17番のやつ
似たようなグリフを探す
ResNetでContrastive Learning
次は似たようなグリフを探す手法についてです。ここでは、resnet18を用いてcontrastive learningを実行しました。データセットとして、すべてのグリフを
データ拡張したデータセットの一部
実装はJAXを使用し、学習はGoogle Colab上で行いました。全体の処理はおよそ2時間。学習後、得られた潜在空間をTriMapを用いて2次元平面にプロジェクトすると、こんな感じになります
また、ランダムに選んだグリフに対してK近傍法(KNN)を用いて類似するグリフを探索するとこの様子です
KNN近傍探索
グリフを潜在空間に探す
次に、SESSIONSのロゴをSDFに変換し、先ほど学習した潜在空間にマッピングしてみました。この操作により、ロゴが潜在空間内でどの位置に対応するかを特定できます。
最終的に、試行錯誤を重ねて、ロゴを含む9個のグリフを選出しました。
選出されたグリフは、以下の図のように滑らかに繋がることを確認しました。
GLSL
次のステップでは、曲線データをテキスト形式に変換し、GLSL内でvec4の配列として埋め込みました。このデータ部分の記述は以下のようになります。
#define V vec4
const V QUADS[3780] = V[](
V(-.03,-.01,-.03,-.01),V(-.02,-.01,.32,-.32),V(.03,-.69,-.27,-1.05),V(-.27,-.58,-.27,-.54),V(-.27,-.5,-.27,-.43),V(-.32,-.43,-.36,-.43),V(-.36,-.49,-.36,-.52),V(-.36,-.54,-.37,-.65),V(-.49,-.61,-.61,-.56),V(-.51,-.53,-.49,-.53),V(-.46,-.52,-.42,-.51),V(-.42,-.48,-.43,-.46),V(-.48,-.46,-.52,-.46),V(-.56,-.45,-.71,-.44),V(-.72,-.55,-.73,-.65),V(-.58,-.65,-.54,-.65),V(-.5,-.65,.15,-.65),V(.13,-.22,.11,.22),V(-.55,.22,-.61,.22),V(-.67,.22,-.79,.22),V(-.78,.1,-.76,-.02),V(-.65,.05,-.58,.09),V(-.52,.14,-.42,.22),V(-.45,.3,-.49,.38),V(-.6,.3,-.61,.3),V(-.62,.29,-1.14,-.09),V(-.94,-.43,-.74,-.76),V(-.16,-.45,-.12,-.43),V(-.08,-.41,-.01,-.37),V(.04,-.41,.09,-.45),V(.02,-.41,0,-.4),V(-.03,-.37,-.24,-.21),V(-.25,-.14,-.25,-.07),V(.01,-.02,.01,-.02),V(.01,-.02,0,0)
V(-.01,0,-.01,0),V(-.01,0,.37,-.15),V(.16,-.43,-.06,-.71),V(-.35,-.43,-.38,-.39),V(-.42,-.35,-.89,.11),V(-.73,.32,-.57,.54),V(.07,.38,.11,.37),V(.14,.36,.15,.35),V(.14,.36,.14,.38),V(.12,.37,.09,.37),V(.06,.37,.04,.37),V(.04,.37,.03,.36),V(.04,.36,.07,.35),V(.1,.34,.18,.3),V(.2,.36,.22,.42),V(.22,.34,.22,.3),V(.22,.26,.22,.14),V(.19,.13,.16,.12),V(.07,.2,.05,.22),V(.01,.25,0,.26),V(0,.26,-.01,.25),V(0,.24,.03,.18),V(.06,.13,.2,-.08),V(.06,-.18,-.09,-.29),V(-.2,-.07,-.24,.01),V(-.32,.09,-.35,.12),V(-.32,.08,-.28,.03),V(-.25,.07,-.18,.15),V(-.13,.22,-.12,.24),V(-.13,.25,-.14,.26),V(-.15,.24,-.16,.21),V(-.16,.18,-.19,.03),V(-.16,.06,-.14,.09),V(-.05,0,-.05,0),V(-.05,0,0,0),
残る課題は、SDFの実装です。この詳細については本記事では省略しますが、興味のある方はtwiglで公開されているソースコードをご参照ください。
感想
- 観客がいる上映では、今回の作品は演出や情報量が不足していたと感じました。来年はこの点を改善したい
- 個人的には、やはりフルプロシージャル生成よりも外部データを工夫して詰め込む方法に興味があります
-
Truong, N., Yuksel, C., & Seiler, L. (2020). Quadratic Approximation of Cubic Curves. Proc. ACM Comput. Graph. Interact. Tech., 3(2). ↩︎
-
lapjvの時間計算量は
ですが、上記したように一つグリフの2次ベジェ曲線数最大200ぐらいなので大丈夫です ↩︎O(n^3)
Discussion