フィルターの CSS 近似をやめる — 色調を一個ずつ自前 GLSL に置き換える
2026年5月22日
開発日誌から再構成した制作メモ(2026年5月22日) フィルターを「それっぽく見せる近似」から「本物のシェーダー」へ。リリース前の差別化として通った遠回り
近似はいつか嘘をつく
最初、フィルタープロパティパネルは CSS で本物のフィルターを「近似」して見せていた。手っ取り早く、それっぽく見える。便利だ。
でも近似は、エンジンの実描画とどうしてもズレる。エディタで調整した見た目と、実際にプレイ画面に出る絵が食い違う。プレビューが嘘をつくと、調整そのものが信用できなくなる。
だから近似プレビューは廃止した。フィルター・色調補正のターゲットを 3 択に絞り、強度ラベルを整理して、重ね掛けのプロパティを足す。手っ取り早い見せかけをやめて、実体に寄せる。これがすべての出発点だった。
「動くけど嘘をつく」近似は、最初は得でも最後は二重メンテになって破綻する。実描画と近似プレビューの両方を直し続けるくらいなら、最初から実描画一本にした方がいい。
ColorMatrix では「光の質感」が出ない
色調系のフィルターは、長らく ColorMatrixFilter 任せだった。要するに「行列をかけるだけ」。チャンネルの線形変換で色味を寄せる、それ以上のことはできない。
問題は、ノベルゲームで効かせたい演出が、まさにその「それ以上」のところにあることだった。
そこで時間帯系の 5 個 — morning / sunset / twilight / night / moonlight — を、ひとつずつ専用の GLSL シェーダーに書き起こしていくことにした。行列を捨てて、ピクセルごとに何をするかを自分で書く。
特に月明かりは凝った。銀青色の味付けをして、ハイライトに銀のリムライトを足し、全体を少し減光して「月光らしさ」を出す。これは行列では絶対に届かない領域だ。書き終えた瞬間の画面は、明確に違っていた。ColorMatrixFilter が便利なのは確かだが、銀のリムや減光のような「光の質感」は、GLSL を直に書かないと出ない。
差別化したいなら、遠回りでもここを通るしかない。月明かりを実装し終えて、ようやく腹が決まった。
共通基盤は 12 個書いてから見えた
一個ずつシェーダーを書いていくと、案の定、副作用が出てきた。UV の中心や範囲がフィルターごとに微妙にズレている。気づいたものだけで 12 個。
フィルターを個別に書いていると、それぞれが少しずつ違う座標系の前提を抱え込んでしまう。これを、vertex shader で正しい座標を varying として出力する共通基盤に統一して一掃した。やってみると全部きれいに収まる。
ハードコードした texel size を使って端で padding を舐めていたバグも、別途潰した。ぼかしまわりはついでに 2D ガウシアン + スポットライトまで持たせて、機能ごと底上げしている。
最初から共通基盤を敷いておけば良かった、とは思う。ただ、12 個書いてみないとその共通項が見えなかったのも事実だ。先に抽象を立てるより、まず数を書いてから共通項を抜き出す方が、結果として正しい基盤になった。
params が play 画面まで届く配線
シェーダー化と同じくらい地味で、同じくらい効いたのが「パラメータが最後まで届く配線」だった。
ブロックで設定した値が、プレビューではなく実際の play 画面のシェーダー uniform まで貫通する。途中のどこかで値が落ちていたら、いくらシェーダーを凝っても意味がない。この流路を全段直した。
近似プレビューを捨てて実描画一本にしたのも、結局は同じ思想だ。エディタで見える絵と実機で出る絵を、同じ規則・同じデータで縛る。両端が一致して初めて、調整が信用できる。
PC-98 フィルターと FX スライダー
合間に、雰囲気を作る方のフィルターも詰めた。
PC-98 フィルターは大改修した。16色 / 256色のラジオ選択、減色方法(なし / 2x2タイル / 4x4タイル)、専用の低解像度でのピクセル化。レトロな見え方を、かなり真面目に再現している。これも「行列をかけて色を寄せる」では到底届かない、実機の質感そのものを狙った作り込みだ。
FX エフェクト(vignette / blur)はスライダー UI に揃えて、時間で自動解除されるようにした。効かない死にスライダーは、潔く除去した。残すべきものと捨てるべきものを、ここで線引きしている。
フィルターごとに本質を表す具体名のスライダー
設計の方針として一つ決めたのは、フィルター API を「汎用の軸」で揃えないことだ。
たとえば temperature や saturation のような、どのフィルターにも共通する抽象的なツマミを並べる手もある。だがそれだと、月明かりの「銀のリム」や PC-98 の「減色方法」のような、そのフィルター固有の本質が、汎用軸の裏に埋もれてしまう。
だから、フィルターごとに、その効果の本質を表す具体的な名前のスライダーを置く。汎用軸に寄せて見た目を統一するより、何を調整しているのかが名前から分かる方を取った。UI の見た目は別途、強度スライダーを独立行に揃えることで統一できる。
振り返り
一日の大半が「行列をシェーダーに書き直す」という、単調にも見える作業だった。便利な組み込みフィルターを捨てて、ピクセルを自分で計算するコードに置き換える。これはコストでしかない。
それでも通る価値があったのは、二つの理由からだ。一つは差別化。ColorMatrixFilter の線形変換では出ない「光の質感」を出せるのは、GLSL を直に書いたフィルターだけだった。もう一つは移植。PixiJS の組み込みフィルターに依存し続ければ、Switch 移植で「PixiJS が無い世界」に全部書き直す未来が待っている。今ここで自前実装にしておく方が、後で払うより圧倒的に安い。
近似をやめて実体に寄せる、という小さな決断から始まって、最後は色調を一個ずつ自前 GLSL に置き換えるところまで来た。手っ取り早い見せかけは最初だけ得をして、最後は嘘をつく。遠回りでも実体を書く方が、結局は信用できる絵になる。
Claude Opus 4.8