Work

WebGPU Pixel Sort

Role
個人開発
Period
2025/11 — 2025/12
Stack
  • TypeScript
  • Babylon.js
  • WebGPU
  • WGSL
  • Vite
Site
https://qiita.com/Yohei_Suda/items/7f2356b1d82ee3dd361b

WebGPU Pixel Sort

動画と Webcam の入力に対して、動きのある領域だけに選択的にピクセルソートを掛けるリアルタイムエフェクト。WebGPU の Compute Shader でパイプラインを組み、GPU 上で Bitonic Sort を走らせて毎フレーム処理する。

Overview

  • フレーム差分 + モルフォロジー で動き領域のマスクを生成
  • 行ごとにマスクを走査してソート対象セグメントを抽出
  • 各セグメントを並列で Bitonic Sort → ソート対象だけがピクセルソートされ、動画全体の構図は保たれる
  • 全部 Compute Shader、最終 draw だけフルスクリーン quad

Pipeline

graph LR
  Cam["Webcam"] --> FrameDiff["FrameDiffPass<br/>(輝度差 + EMA)"]
  FrameDiff --> Mask["MaskTexture"]
  Mask --> Morph["MorphologyPass<br/>(膨張 → 収縮)"]
  Morph --> MaskBuf["MaskBuffer"]
  MaskBuf --> SegExt["SegmentExtractPass<br/>(行走査)"]
  SegExt --> Segments[/"Segment[]<br/>{row, start, length}"/]
  Video["Video texture"] --> CopyFrame["CopyFramePass"]
  CopyFrame --> OutTex["OutputTexture"]
  Segments --> Sort["PixelSortPass<br/>(Bitonic Sort)"]
  OutTex --> Sort
  Sort --> Canvas["Canvas"]
7 個の Compute Shader を連結したリアルタイム pipeline / click to zoom

Shaders

7 個の WGSL コンピュートシェーダをチェーン:

  • frameDiff.compute — 現在 / 前フレームの Rec.709 輝度差を取り、EMA で平滑化 + 閾値で二値化してマスク生成
  • morphology.compute — 3×3 近傍の max/min で膨張・収縮。ノイズ除去と領域連結
  • maskTextureToBuffer.compute — マスクをテクスチャ → バッファへ変換(次段はバッファ前提)
  • segmentExtract.compute — 各行を左→右に走査、連続するマスク領域を {row, start, length} として atomic 付きで配列に書き出し
  • copyFrame.compute — 動画フレームを出力テクスチャへコピー(ソート前のベース)
  • pixelSort.compute — セグメント単位で Bitonic Sort を実行。workgroup 共有メモリで輝度値を比較、閾値以上のみ対象
  • copyStorageTexture.compute — モルフォロジー間で使うテクスチャ swap 用

Key Decisions

  • Fragment ではなく Compute: fragment で pixel sort を書くと分岐・書き戻しが非効率。WebGL2 に compute は無いので WebGPU 前提
  • 生 WebGPU ではなく Babylon.js 経由: 生 API は儀礼が多くリソース管理が重い。Babylon.js に寄せると実装量が大きく減る
  • Bitonic Sort: 並列度が高く workgroup 共有メモリと相性が良い。セグメント長が可変なのでマスクしつつ sort
  • アトミックなセグメント追加: 複数スレッドから同時にセグメントを書き込むので atomic<u32> でインデックスを確保

Constraints

  • WebGPU 対応ブラウザ必須(Chrome/Edge 113+、Safari 18+)
  • セグメント最大数 262144(超過時は overflow フラグで警告)
END © 2026 Yohei Suda
yoheisuda.me