[Astro] #125 Photo Editor — WebGLシェーダーによるブラウザ完結型フォトエディタを実装

[Astro] #125 Photo Editor — WebGLシェーダーによるブラウザ完結型フォトエディタを実装

概要

WebGLフラグメントシェーダーを使い、Polarr Pro Photo Editorのような本格的な画像調整をブラウザだけで行えるフォトエディタを実装しました。

すべての画像処理はGPU上のGLSLシェーダー1パスで実行されるため、4000×3000ピクセル級の高解像度画像でもスライダー操作の瞬間にリアルタイムで結果が反映されます。サーバーへの画像アップロードは一切不要で、完全ローカル処理です。

機能一覧

カテゴリ内容
基本調整(LIGHT)Exposure, Brightness, Contrast, Highlights, Shadows
色調整(COLOR)Saturation, Temperature(色温度), Tint(緑/マゼンタ)
トーンカーブ(TONE CURVE)Master / R / G / B 個別チャンネル、スプライン補間、ポイント追加/移動/削除
エフェクト(EFFECTS)Vignette(周辺減光), Grain(フィルム粒子ノイズ)
LUT(.cube)3D LUTファイル読み込み・適用、Intensityスライダーでブレンド調整
フィルムプリセットPortra 400 / Velvia 50 / Tri-X 400 / Cinematic / Faded / Cool Blue / Warm Glow / High Key
クロップドラッグハンドル操作、アスペクト比プリセット(Free / 1:1 / 4:3 / 3:2 / 16:9)
リサイズプリセット(640×480〜3840×2160)+ カスタム入力、アスペクト比ロック
回転/反転90° CW/CCW、水平/垂直フリップ
Undo/Redo全操作をスナップショットスタックで管理(最大50段)
プリセット保存名前付き保存、適用、削除。JSON Export/Importで他ブラウザ・スマホに移植可能
エクスポートPNG / JPEG / WebP、品質スライダー、フル解像度書き出し

1. WebGLシェーダーパイプライン

本ツールの核は、すべての画像調整を1つのフラグメントシェーダーで処理するアーキテクチャです。

シェーダー処理の流れ

元画像テクスチャの各ピクセルに対し、以下の順序でパラメータを適用します。

  1. Exposurepow(2.0, exposure * 2.0) によるEV段階の露出補正
  2. Brightness — RGBへの加算
  3. Contrast — 中間グレー(0.5)基準のスケーリング
  4. Highlights / Shadows — 輝度の smoothstep マスクで明部/暗部を個別調整
  5. Saturation — 輝度(BT.709係数)との mix で彩度制御
  6. Temperature / Tint — R/B / G チャンネルへの直接オフセット
  7. Tone Curve — 256×4テクスチャによるLUT参照(チャンネル別→マスター)
  8. 3D LUT — .cube ファイルから生成した2Dテクスチャでトリリニア補間
  9. Vignette — UV座標からの距離による減光
  10. Grain — 疑似乱数によるフィルムノイズ

全工程がGPU上の1回のドローコール(gl.drawArrays)で完了するため、CPU側のボトルネックが存在しません。

スクリーンショット

Photo Editor — 基本調整(LIGHT / COLOR)

2. トーンカーブ(TONE CURVE)

4チャンネル独立カーブ

Master(全体)/ R / G / B の4チャンネルを独立して編集可能。Catmull-Romスプライン補間で滑らかなカーブを生成し、256エントリのLUTに変換してシェーダーに渡します。

境界処理の工夫

デフォルトの2点((0,0)と(1,1))だけの場合は線形補間に直接フォールバック。3点以上では端点に反射ゴーストポイント(2*p1 - p2)を生成してCatmull-Romの接線を自然にし、端点での不自然な歪みを防止しています。

テクスチャパッキング

4チャンネル分のLUT(各256値)を256×4ピクセルの gl.LUMINANCE テクスチャとして1枚にパック。シェーダー内ではY座標(0.125 / 0.375 / 0.625 / 0.875)でチャンネルを選択し、チャンネル別カーブ→マスターカーブの順で2段階参照します。

Photo Editor — トーンカーブ(TONE CURVE)

3. 3D LUT(.cube ファイル)

WebGL 1での3D LUT実装

WebGL 1は3Dテクスチャをサポートしないため、3D LUT(サイズN)を幅 N×N、高さ N の2Dテクスチャに展開。Blue軸をスライス方向として横に並べ、シェーダー内でBlue値から隣接2スライスを参照してトリリニア補間を行います。

Intensity制御

LUT適用結果と元画像を mix() でブレンドすることで、0%〜100%のIntensityスライダーで効果の強さを自由に調節可能。

4. フィルムプリセット

全8種類のフィルムプリセットを内蔵。各プリセットは基本調整パラメータ+トーンカーブのポイントデータをセットで定義しており、1クリックで一括適用されます。

プリセット特徴
Portra 400温かみのある肌色、低コントラスト、シャドウ持ち上げ
Velvia 50高彩度・ビビッド、風景向き
Tri-X 400モノクロ(彩度-100%)、高コントラスト、フィルム粒子
Cinematicティール&オレンジ、ビネット強め
Faded退色フィルム風、浅いコントラスト、黒浮き
Cool Blue寒色シフト、青チャンネルカーブ調整
Warm Glow暖色、柔らかい光、赤チャンネル持ち上げ
High Keyハイキー、明るく飛ばし気味
Photo Editor —  フィルムプリセット

5. クロップ・リサイズ

クロップ

キャンバス上にオーバーレイでクロップ矩形を表示。8つのハンドル(四隅+中点)のドラッグでリサイズ、内側ドラッグで移動。三分割ガイド表示。アスペクト比プリセット(Free / 1:1 / 4:3 / 3:2 / 16:9)対応。

適用時は renderFullRes() でフル解像度レンダリング→指定領域の切り出し→新規画像として再読み込みの手順で、調整結果を焼き込んでからクロップします。

リサイズ

プリセットサイズ(640×480 / 1280×720 / 1920×1080 / 3840×2160)またはカスタム入力。アスペクト比ロックのトグル付き。

Photo Editor — クロップ・リサイズ

6. Undo/Redo・プリセット管理

操作履歴

全パラメータ+カーブ+回転状態をJSONスナップショットとしてスタック管理。各操作は数百バイトのため、50段まで積んでもメモリ負荷は無視できるレベル。

プリセットの保存と移植

調整結果を名前付きプリセットとして保存。localStorage でブラウザに永続化しつつ、JSON Export/Import機能で他のOS・ブラウザ・スマホへ移植可能。

技術実装のポイント

render() の分離設計

画面表示用の render()(ビューポートフィット)とGL描画だけの renderGL()、フル解像度書き出し用の renderFullRes() の3段構成。エクスポート・クロップ・リサイズ時にビューポートサイズで解像度が潰れるバグを防止。

回転のUV実装

画像の90°回転はピクセル操作ではなく、シェーダーに渡すUV座標の変換で処理。GPU側で完結するため、4000万ピクセルの画像でも回転コストはゼロ。

3パネルレイアウト

左パネル(ファイル操作・クロップ・リサイズ・エクスポート)と右パネル(画像調整・トーンカーブ・LUT・プリセット)に機能を分離。右パネルは折り畳み可能で、ビューポートの作業領域を広げられます。

技術スタック

  • WebGL — フラグメントシェーダーで全画像処理をGPU実行
  • GLSL — 1パスシェーダーに10種類の調整+LUT+エフェクトを集約
  • Canvas 2D — トーンカーブUIの描画、クロップオーバーレイ
  • Astro — 静的サイト生成、3パネルレイアウト
  • localStorage — プリセットの永続化

今後の展望

現状でPolarr Pro Photo Editorの基本機能をほぼカバーしていますが、HSL個別調整(色相ごとの彩度/明度制御)、シャープネス/ぼかし(畳み込みシェーダー)、ヒストグラム表示、バッチ処理(複数画像への一括プリセット適用)などの拡張が可能です。いずれもシェーダーへのuniform追加とUI追加で対応できるアーキテクチャになっています。