← ソースコード説明書
packages/web — フィルター & エフェクト実装(GLSL + JSON)
作成日: 2026-06-10 / 更新日: 2026-06-10
スクリーンフィルター(GLSL)と JSON 宣言型エフェクト(effect.json)の実装。概要は 09-pkg-web-engine.md、コマンドは 07-pkg-compiler.md、ネイティブ側は 13-pkg-native-engine.md を参照。
概要
- GLSL フィルター:約 48 種のスクリーンフィルター。
ScreenFilter.tsが管理し、PixiJS のContainer.filtersに適用する。 - JSON 宣言型エフェクト:
effect.jsonで「素材+レイヤー+Timeline+画面効果」を宣言し、EffectPlayerが再生。@effect <id>から配線。 - ネイティブ parity:
FilterShaders.hppが同じフィルターを移植 GLSL で持ち、GLRenderer(OpenGL/GLES3)で適用。
1. GLSL フィルター(Web)
ScreenFilter.ts — 管理と適用
packages/web/src/renderer/ScreenFilter.ts が全フィルターの単一定義源。
createFilter(type, intensity, options)—switch (type)で約 48 種のフィルター名 →Filterインスタンスを生成(factory。EffectPlayer がレイヤー別シェーダにも再利用)。apply(type, intensity, options)—stage.filters = [filter]で単一適用。applyMix(layers, target)— 2 レイヤーを順次適用(stage.filters = [f1, f2]→ texture→f1→f2→出力)。applyColorAdjust(brightness, contrast, saturation, temperature)— 色調補正(後述の ColorMatrix 経路)。- 品質スケーリング:低品質時は重いフィルター(bloom/rain 等)を簡易版へ差し替え。
- アニメ系:ticker が毎フレーム
filter.timeを更新(rain / snow など手続き的演出)。
filters/ — 各フィルタークラス
packages/web/src/renderer/filters/*.ts(約 38 クラス)。共通パターンは PixiJS Filter 派生+ GLSL フラグメント文字列:
export class RainFilter extends Filter {
constructor() {
const glProgram = GlProgram.from({
vertex: defaultFilterVertex, // 中心基準は centeredFilterVertex
fragment: RAIN_FRAG, // GLSL 文字列
name: "rain",
});
super({ glProgram, resources: { rainUniforms: new UniformGroup({
uTime: { value: 0, type: "f32" }, uIntensity: { value: 1, type: "f32" }, /* ... */
}) } });
}
}
- intensity:多くは
0..1のuIntensityで強度を掛ける(rain *= uIntensity等)。一部は別ユニフォームへマップ(GameBoy は contrast、CRT は曲率/走査線を個別公開)。 - 例:
RainFilter(手続き的多層雨、uTime/uSpeed/uAngle)、BloomFilter(7x7 ガウシアン+スポット、centeredFilterVertex)、CRTFilter(樽歪み+走査線+ビネット)。
ColorMatrix → 単一 GLSL(色調系)
色調系フィルターは個別シェーダではなく、CPU で 4x5 カラーマトリクスを組み、1 本の GLSL(ColorTransformFilter)で適用する。
filters/colorMatrices.ts—sepiaMatrix()/warmPattern()/colorAdjustMatrix(...)等(CPU 計算)。filters/ColorTransformFilter.ts— 行列+オフセットを 5 つのvec4ユニフォームで受け、outColor = M·color + offsetを計算する単一フラグメント。- 理由:行列をフラグメントで毎回組むと 1280×720×60 ≈ 5500 万回/秒の無駄になり、型ごとに分岐してシェーダが膨らむ。CPU で 1 回組めば「1 シェーダで色調系をまとめて表現」できる。
- 対象(約 10):
sepia / grayscale / desaturate / warm / cool / vivid / muted / bright / dark / overcast。 applyColorAdjustもcolorAdjustMatrix(brightness, contrast, saturation, temperature)(各 -100..100、0 で no-op)を合成して同じ経路で適用。
PC98Filter.ts
packages/web/src/renderer/PC98Filter.ts — PC-98 風レトロ。16 色パレット / 256 色 RGB332、2x2・4x4 オーダードディザ、PC-98 ピクセル単位での量子化(640×360 相当)。
2. JSON 宣言型エフェクト
スキーマ(effectTypes.ts)
packages/web/src/effects/effectTypes.ts。effect.json は「素材+レイヤー+既存 Timeline+画面効果」を束ねる合成コンテナ。
interface EffectDef {
id: string;
durationMs: number;
assets: EffectAssetsDef; // textureSequences / particles / meshes
layers: EffectLayerDef[]; // 描画レイヤー
timeline?: TimelineRoot; // ★ @kaedevn/core の Timeline v1.1 を再利用
screen?: EffectScreenDef; // flash / shake / 全画面 filter
}
interface EffectLayerDef {
name: string; // timeline の targetId
kind: "sprite" | "spriteSequence" | "particle" | "mesh";
source: string; // assets キー
blend?: "normal" | "add" | "multiply" | "screen";
shader?: string; // FilterType 名(レイヤー別 GLSL)
shaderParams?: Record<string, number>;
x?: number; y?: number; scale?: number; anchor?: [number, number]; alpha?: number;
}
- Timeline 再利用:独自タイムラインは持たず、Timeline v1.1(frame/x/y/scale/alpha/
shaderParams.<name>チャンネル、フレーム保持はeasing: "step")をそのまま使う。 - mesh レイヤー:GLB を Filament で描画(3D の閃光・グロー)。sprite/particle は PixiJS。
再生(EffectPlayer / EffectClipDelegate)
effects/EffectPlayer.ts—load(def, basePath, screenTarget, overrides)で素材解決→レイヤー構築→レイヤー別シェーダをScreenFilter.createFilter()で装着→screen.filterを全画面に適用。play()+ ticker で Timeline を評価し、テクスチャ/座標/ユニフォームを更新、完了で破棄。effects/MeshEffectRenderer.ts— mesh レイヤーの Filament 描画。
@effect の配線
@effect fire_full_01 scale=1.5 intensity=2
→ EFFECT_PLAY op(commandRegistry.ts)
→ EffectClipDelegate.effectPlay(id, opts)(packages/web/src/renderer/delegates/)
→ /assets/effects/{id}.effect.json を fetch
→ EffectPlayer.load(...)(テクスチャ/パーティクルは json 相対で解決=可搬バンドル)
→ overlayLayer(シーンと UI の間)に追加、screen.filter は全シーンへ
→ player.play() を ticker 登録、wait=true なら完了待ち
実 effect.json は packages/web/public/assets/effects/ に 5 本(fire_full_01 / explosion_01 / smoke_01 / heal_01 / fire/fire)。
3. ネイティブ GLSL(parity)
packages/native-engine/src/engine/FilterShaders.hpp— Web と同じフィルターの移植 GLSL(ヘッダ内インライン文字列)。移植ルール:#version 300 es → 330 core、precision mediump除去、vTextureCoord → vUV、finalColor → fragColor、premultiplied→straight alpha。- 自動バインドされるユニフォーム:
uTexture/uIntensity/uTime/uResolution。多パラメータ系は現状const既定値(GLRenderer のパラメータ API は今後拡張余地)。 filterDefaults.cpp/.hpp— 各フィルターの既定パラメータ(Web コンストラクタ既定と対応)とmergeDefaults(user, defaults)。- GLRenderer(OpenGL 3.3 / GLES3)が同じフィルター集合を適用=Web/ネイティブで見た目を揃える。
4. 数とフェーズ
- フィルター約 48 種(
ScreenFilter.tsの switch)。内訳の目安:色調系 約 10(ColorMatrix→単一 GLSL)+ 専用 GLSL クラス 約 38(filters/*.ts)。 - フェーズ移行:Phase 1=色調系を ColorMatrix 化、Phase 2=時間帯・雰囲気・季節フィルターを専用 GLSL クラス化(
overcast等一部は ColorMatrix のまま)。applyMixは行列合成をやめ順次適用に変更(分類漏れ・後勝ちの解消)。 - effect.json 5 本(いずれも Timeline v1.1 でフレーム/アニメ制御、particle+sprite+任意の screen.filter)。
主要ファイル
| ファイル | 役割 |
|---|---|
renderer/ScreenFilter.ts | フィルター管理・適用・factory(48 種 switch) |
renderer/filters/*.ts | 個別 GLSL フィルター(約 38 クラス) |
renderer/filters/colorMatrices.ts | 色調系の CPU カラーマトリクス計算 |
renderer/filters/ColorTransformFilter.ts | 行列適用の単一 GLSL |
renderer/PC98Filter.ts | PC-98 レトロ(パレット/ディザ) |
effects/effectTypes.ts | effect.json スキーマ |
effects/EffectPlayer.ts | effect.json の読込・再生 |
effects/MeshEffectRenderer.ts | mesh レイヤーの Filament 描画 |
renderer/delegates/EffectClipDelegate.ts | @effect(EFFECT_PLAY)配線 |
public/assets/effects/*.effect.json | 宣言型エフェクト定義(5 本) |
native-engine/src/engine/FilterShaders.hpp | ネイティブ移植 GLSL |
native-engine/src/engine/filterDefaults.cpp | ネイティブ既定パラメータ |
Ad: stickyBottom (728x90)