Web技術で実装する歌詞×プロジェクションマッピング (マジカルミライ2020プロコン参加記)
First published Tue Dec 1 22:05:00 2020 +0900 ; substantive revision Wed Jan 6 21:52:07 2021 +0900
概要 (TL; DR)
- ブラウザで動く「リリックプロジェクションマッピング」ソフトウェア mmmapper を作りました (Svelte + three.js + PixiJS + TextAlive App API)
- 「マジカルミライ2020」プログラミング・コンテストで入選しました
- どんな感じで開発したか書きました
背景
マジカルミライ2020
- https://magicalmirai.com/2020/
- クリプトン・フューチャー・メディア社が主催する、初音ミクをはじめとするバーチャルシンガーのライブ+企画展イベント (2013〜)
- 企画のひとつとしてプログラミングコンテストを今年度初開催
コンテストについて
-
「マジカルミライ2020」プログラミング・コンテスト
- マジカルミライ公式 https://magicalmirai.com/2020/procon/
- TextAlive公式 https://developer.textalive.jp/events/magicalmirai2020/
-
応募条件
- TextAlive App API を使うこと
- ブラウザで動作 & フロントエンドで完結すること
- 外部Web APIの利用は可
- だが、若干基準が微妙 (自分でパブリックなAPIサーバーを立てればバックエンドがあるのと実質同じになってしまうので)
-
審査基準
- クリエイティビティ、イノベーション、完成度
-
TextAlive
- https://textalive.jp/
- 楽曲・歌詞解析技術のパワーで、音楽と同期したキネティックタイポグラフィ (特に歌詞PV) が手軽な感じで作れるやつ
- JSTグラントのOngaACCELプロジェクトの成果物
- 基本的な楽曲情報は Songle から来ている
- 私のイメージはこれです 【雪ミク】「SNOW MIKU LIVE! 2018」ライブ映像/四角い地球を丸くする 他【初音ミク】 - YouTube
応募作品について
- GitHub: nolze/mmmapper
- デモ: https://mmmapper.github.io/
- (追記)「マジカルミライ2020」 in OSAKA 企画展ステージでの紹介 (31:54〜):
- (追記)「マジカルミライ2020」 in TOKYO 企画展ステージでの紹介 (28:21〜):
序盤
構想
-
コンテストの「プログラミング」要素を生かす
- アート/デザイン/CGに強い人は界隈に多そうなので、別の路線で攻めたい
- daniwellPの作例がすでにすごい (それはそう)
- 仕様上元動画の表示が必須なので、元PVとよさが競合しないことも求めたい
- プログラムとして出すメリットは汎用性、インタラクティブ性あたり?
- 枠組みとしても使えると、コンテンツに関してはセンスのある人に使ってもらう/生かしてもらうことができる
- アート/デザイン/CGに強い人は界隈に多そうなので、別の路線で攻めたい
-
リアルとバーチャルの統合
- 個人的な大きなテーマの一つ
- 特に最近はコロナウイルス情勢で、ビデオ会議からバーチャルイベントまで、一般的にもバーチャル的なものに触れる機会が増えてきた
- それに先立って以前から、ボカロ/バーチャルシンガーはバーチャルなもののおそらく代表格だが、そのリアルイベントであるマジカルミライはわれわれに何を教えてくれるのか?
- 個人的にマジカルミライで感じるのは、バーチャルなものとリアルなものは表層的に切断されているにすぎない、ということ
- ライブも企画展も、いろいろな仕方や角度で両者を繋いでいる
アイディア
-
ゲーム
- 「プログラミング」要素を生かせるし、「リアルとバーチャルの統合」も工夫すればできる
- 先の展開を考えた場合に権利関係が難しそう?
-
AR
- 現状のカメラARでは、(工夫次第とはいえ)「画面の中の出来事」になってしまいがちなので、「リアルとバーチャルの統合」の個人的イメージと若干ずれる
- と言いつつ本格的にやってみたことはないので、いつか挑戦したい
- ホログラムとかに期待があります
- 現状のカメラARでは、(工夫次第とはいえ)「画面の中の出来事」になってしまいがちなので、「リアルとバーチャルの統合」の個人的イメージと若干ずれる
-
VR
- YouTubeの埋め込み動画の規約を満たせるのかわからなかった (後述)
- VRデバイスは今のところ、みんなが/いつでも持ってるわけではない
-
プロジェクションマッピング
- ライトアートには関心があり、プロジェクションマッピングを見るのも (言うほど見れてはいないが) 好き
- 音楽と合わせたプロジェクションマッピングはよくあるが、さらに歌詞を生かしたものはあまりない気がする
- 以前 METAPHIG という作品でプロジェクションマッピングに似たようなことをやったので、実装のイメージは持てた
今年のTSGブースにはカメラに映っている人間からキャラ髪・顔・服装を生成してグラフィグ素体にプロジェクションマッピングするMETAPHIGを展示しました pic.twitter.com/Wt9VXy2Ggw
— nolze (@nolze) November 24, 2014- このときは openFrameworks (C++) を利用
- Anker Nebulaのような最近話題のモバイルプロジェクターも気になるし、こういうのがあれば家でもできる
- ただ、(VRデバイスと同等かそれ以上に) プロジェクターをみんなが/いつでも持ってるわけではないので、単に投影できますというだけでは限定的すぎる → シミュレーター的な形でもかっこいい/楽しめるのでは? → シミュレーター+実投影のハイブリッド
- 一人でも (シミュレーター)、少人数でも (モバイルプロジェクター+屋内など)、多人数 (屋外など) でも楽しめるものにできるのでは
- 目下のコロナウイルス情勢を踏まえた一つのコンセプトということも念頭に置きつつ
- (余談) 作っている途中に谷中初音町よりハローを思い出しました
技術スタック
-
JavaScript or TypeScript → JavaScript
- 開発のスピード感重視のときは JavaScript を使う (動けばいいので)
- Denoとかの流れが来たら変わるかも?
-
フロントエンドフレームワーク → Svelte
- Svelte か React が選択肢でした (後述)
-
CSS → Tailwind
- Vue や Svelte ではかなり楽、React では twin.macro を使っていますが正解はわかりません
- (その他) 一般投票はミクナビ (スマートフォンアプリ) からということなので、モバイル対応がかなり望ましそう
- 手持ちの iPhone/iPad だけテストした
-
TextAlive App API
- https://developer.textalive.jp/
- GitHub https://github.com/TextAliveJp
- 今回のコンテストと同時に公開された、TextAlive (+Songle) の解析結果を取得するAPI/ライブラリ
- 既存のTemplate APIとは別
- 内部向けがメインと思われるがTextAlive React APIも公開されている (利用例)
- チュートリアル・サンプルが親切
- ただ、チュートリアルをやれば理解できるという感じのものでもないので、サンプルコードを見て雰囲気を掴むべきかもしれません
- 参考になりそうなもの:
- 基本 https://github.com/TextAliveJp/textalive-app-basic/blob/master/src/index.js
- 平面上に歌詞 (canvas) https://github.com/TextAliveJp/textalive-app-lyric-tiles/blob/master/src/index.js
- キューブに歌詞 (three.js) https://github.com/TextAliveJp/textalive-app-char-cube/blob/master/src/index.js
- p5.js https://github.com/TextAliveJp/textalive-app-p5js/blob/master/src/index.js
- ドキュメントも開発中に必要になる情報はしっかりカバーされており役立った
- GitHubなどから窺い知れる限りでは、今回はかなりの部分をJun Kato氏がお一方で回しておられそう (ありがとうございます)
-
3D → three.js (+Svelte)
- 候補: three.js 一択
- WebGLを厭わなければ若干選択肢は広がる (reglとか?)
- 別プロジェクトで多少触っていて、感覚はわかる
- というか three.js はかなりよくできていて、やりたいことは頑張ればだいたいできる印象
- 今回も PixiJS の canvas の描画をリアルタイムでテクスチャに反映できるのかが懸案だったが、普通にできた
- その上でさらに選択肢: react-three-fiber+React vs. three.js+Svelte
- react-three-fiber は単なるラッパーではなく React→three.js の reconciler として実装されており (すごい)、Reactを介することによるオーバーヘッドがないというのが売りだが、three.js をコンポーネントとして書けるだけで普通に便利
- React は done right 感はあるものの手数が多くなるためスピード感が重要なときは避けがちで、react-three-fiber は便利そうだが玄人向けっぽくとっつきにくい
- react-three-fiber はその後個人的に使っており、慣れれば React の手間を差し引いても相当に楽
- Svelte はコンパイル方式による省サイズやパフォーマンスが注目されがちだが、個人的にはモダンなフロントエンドの流儀でかつ直感的に書けるのが大きい
- React は Java/Scala を書いている気分になり、Svelte は jQuery を書いている気分になる (いい意味で)
- ハッカソンに近い開発スタイルになりそうだったので、Svelte + three.js を選択
- 3D (three.js) と2D (PixiJS) を併用することになったので、Svelte でパフォーマンス上の恩恵も受けたかも (比較してみないとわかりませんが)
- ちなみに Svelte 版 react-three-fiber 的なプロジェクトとして svelte-gl, svelthree があるが、まだどちらも発展途上
- 候補: three.js 一択
-
2D → PixiJS (+Svelte)
- 2Dはしばらく触ってないので、まず最近の動向を調べた
- 候補: Konva, Fabric.js, Paper.js, PixiJS, EaselJS, two.jsあたり?
- WebGL を使うもの (
getContext('webgl')
) といわゆる Canvas 2D API を使うもの (getContext('2d')
) がある - 結構パフォーマンスが変わってくるので、WebGL が使えるとよさそう
- PixiJS は WebGL or Canvas 2D API フォールバック
- three.js に渡すために、単一 canvas に描画されることが望ましい
- Konva, Fabric.js はこの辺の設計がマッチしなかった
- 2Dも three.js で完結させるのは検討の価値があるかも
-
アニメーション → anime.js
- 慣れない分野で調べるのが大変だった
- 候補: GSAP, anime.js, tween.js, Popmotion, TweenJSあたり?
- 実は (?) TextAlive にも TweenJS (CreateJS) の関数が port されているので、それを使ってもいい
- アクティブ度、ライセンス (MIT) と名前で anime.js に決めた
- 使ってみて便利な一方、若干HTML寄りに opinionated なので、別のを試してもいいかもという気持ちはある
プロトタイプ1 (9/20)
-
設計の雰囲気
-
TextAlive + Svelte 周り
- TextAlive App API の基本的な使い方はほぼ形が決まっているので、サンプルコードとほとんど変わらない
- 大きな流れとしては、初期化した後、ループの中で楽曲再生に合わせて毎時点の歌詞や楽曲情報を取得し、画面に反映することになる
- 各時点で取れる歌詞の単位は「1文字 (Char)」「1単語 (Word)」「1フレーズ (Phrase)」で、それぞれ親子関係の情報を持っている
- 子→親の情報は、歌詞の切れ目を判定する上で有用 (利用例)
- 初期化時にすべて読み込まれているので、再生時点の情報しか取れないわけではない
- その他取れる音楽情報はPlayerクラスの
get*
やfind*
が参考になる
- 各時点で取れる歌詞の単位は「1文字 (Char)」「1単語 (Word)」「1フレーズ (Phrase)」で、それぞれ親子関係の情報を持っている
- 情報をリアルタイムに各コンポーネントに流すのに Svelte の store を使った
- パフォーマンスが不安だったが特に工夫なしで問題なく、しかも手軽に呼び出せてよかった
-
プロジェクション周り (three.js)
- three.js でやってる人がいればラッキーで、最悪自前で実装するにしても、貼り込み後に投影された形になるように逆算して事前にテクスチャを変形するようにすれば何とかなりそう、という目算
- 調べたところ前者のパターンで、Marco Fugaro氏によって texture projection についての非常に丁寧な解説記事が公開されていた
- Playing with Texture Projection in Three.js | Codrops
- これが完全に正解の方法っぽい
- ライブラリ (three-projected-material, MITライセンス) としても公開されており、このおかげで大幅に実装を短縮できた
- ただし、ライブラリでは最初の投影でテクスチャが固定される仕様なので、レンダーループでテクスチャを更新する形に変更する必要がある
- フォークして若干手を加えた (コード)
- 最近 master に更新が入って、より新しいバージョンの three.js に対応した様子
- 投影映像周り (PixiJS)
プロトタイプ2 (9/22)
-
ライティング周り (three.js)
- 単なるシミュレーターなら投影結果だけ確認できればいいが、今回のプロジェクトではバーチャルなアートとしても見て楽しめるということが重要だったので、光の感じをいい感じにする必要があった
- three.js に同梱されている post processing shader pass に UnrealBloomPass があり、これを挟んでいろいろパラメーターをいじっていい感じになった (この時点の方がエフェクト強めで、最終版より見映え的にはよかったかも?)
-
投影映像周り (PixiJS)
- 簡単なエフェクトを作った (流れる線)
中盤
状況
- 忙しくなったのであまり時間を取れず
- 10月に発表になった SYNCHRONICITY 2020 (10/16-17) が「ユーザーのスマートフォンから流れる音楽と、光などの街そのものに仕込まれた演出が同期する」イベントで TextAlive も活用ということだったので、まさかのネタ被りかと思って焦ったが大丈夫だった (VRで参加しました)
開発
-
設計の雰囲気
- 「エレメント」(投影映像に配置できる、演出やアニメーション付きのテキストや図形など) と「スケッチ」(エレメントの配置やデータの処理)、「ステージセット」(オブジェクトやその配置など) の概念を導入した
- 投影映像の演出に TextAlive の情報を自由に使える
- スケッチレベルで TextAlive の情報を処理することで、文字をどのタイミングでどこに出すかなどをコントロール可能に (実装したのは終盤)
- 今回の3曲のスケッチ (開発した順序の関係でステージセットだけ別ファイルにある)
- 「エレメント」(投影映像に配置できる、演出やアニメーション付きのテキストや図形など) と「スケッチ」(エレメントの配置やデータの処理)、「ステージセット」(オブジェクトやその配置など) の概念を導入した
-
Warping 周り (three.js)
- プロジェクションマッピングでは、投影対象に合わせて投影するものの位置合わせや変形をする必要がある (warping)
- 高級なプロジェクションマッピングソフトでは自由に投影面を設定できたりするが、最低限矩形エリアの頂点を動かせればいい (quad warping)
- PixiJS の pixi-projection が quad warping をサポートしている (サンプル)
- PixiJS に慣れていなかったこともあり、エレメントの移動も含めてかなりデバッグに苦戦したが、最終的になんとかなった
-
three.js 周り
- react-three-fiber や marcofugaro/threejs-modern-app を意識しつつ、コードベースを整理 (中途半端)
-
ステージ/ステージセット周り (three.js)
- ステージセットと次にあるエフェクトは最も悩んだ部分で、まだ妥協点という感じ
- いろいろアイディアを出していくつか簡単なものを実装したが、このとき作ったのは最終版の3ステージセット中1個で、残りの2ステージセットはほぼ最終日に作った (コード)
- 原理的には three.js で作成/読み込みできる任意のオブジェクト/モデルに投影できるので、今後実装したい
-
エレメント/エフェクト周り (PixiJS)
- 最初の設計ではエレメント (文字や図形) とエフェクト (アニメーションやフィルタなどの演出効果) が分離されており、エレメントにエフェクトを適用するという考え方になっていた
- エレメントのテキストに対するエフェクト (テキストアニメーションやフィルタ)、ビジュアルエフェクト (図形などによる投影映像の視覚演出) に分けてそれぞれアイディアを出した
- バーチャル限定のエフェクトとして、ステージエフェクト (ステージ内を動くオブジェクトなど)、インタラクティブエフェクト (クリックに反応など) も考えたが、間に合わず断念
- エレメントとエフェクトの分離はあまりうまくいかず、エレメントがエフェクトも含むようにした
- 実装したエレメントの一覧 (使っていないものも含む)
- これもいろいろ作ったが結局ほぼ直前に作ったものを使った
-
PixiJS + Svelte 周り
- PixiJS のインスタンスを子孫コンポーネントで共有するのに Svelte の Context API を使った
- store 同様便利
-
UIデザイン周り (Tailwind)
- クールな感じを目指しました
終盤
状況
- 追い込みでがんばった
- 実装する機能を絞り込んだ
- 今回断念したもの:
- クリエイティブモード
- 投影映像 (スクリーン) の完全な自由編集
- ステージ自由編集 (オブジェクトの作成/読み込みや配置)
- TextAlive 関連
- コード進行情報の利用
- レイテンシの調整
- serviceworker 化
- その他
- ステージや投影映像全体のエクスポートやインポート、URLでの共有
- プロジェクターの詳細設定
- 視点の自動移動
- VR対応
- クリエイティブモード
開発
-
プレイヤー周り (TextAlive, Svelte)
- 楽曲選択・再生停止などのプレイヤーコンポーネントを作成 (コード)
- UI と TextAlive の Player オブジェクトを疎結合にできれば将来的に再利用しやすいと思ったが、なかなか難しく中途半端に
-
アンチエイリアス (three.js)
- FXAA を採用してパフォーマンスと雰囲気を改善
-
シャドウマッピング (three.js)
- デフォルトでは影は処理されないので、投影した映像が後ろのオブジェクトにも映ってしまう
- 今回はプロジェクションマッピングという性質上結構目立ってしまったので、パフォーマンスを気にしつつシャドウマッピングを有効にする
- ピーターパニングというバグが発生し取るのに苦労した
-
プロジェクターモード (Svelte)
- 基本的には編集画面に出しているものを出せばいい
- 全画面にすると動画が隠れてしまうので困っていたが、元ウィンドウを維持しつつ投影画面だけを新しいウィンドウで開くことでYouTubeの埋め込み動画の規約に準拠
- PixiJS 側のキャンバスをループで取得して生キャンバスに描画する素朴な実装だが、パフォーマンスはなんとか大丈夫そう
- デモ用にモバイルプロジェクターを買ったが、動画撮影用のきれいなスペースが部屋になかった……
-
ビルド周り (Svelte)
- 公式推奨の
npx degit sveltejs/template ...
でセットアップしていたが、このテンプレートではベースパスの指定がサポートされていない (多分)- 公開時には静的ファイルをサブディレクトリに設置することになると思うので、ベースパス指定は必須
- Sapper はビルド時SSRによる静的エクスポートでベースパス指定をサポートしているのでこれを使えばできるが、codechips で svite の存在を知る
- svite がSSR経由でないベースパス指定をサポートしている感じだったので、これを使うことに
- 移植はそれほど大変でなかった
- 少し前 (10/18) に SvelteKit なるものが発表されていたらしい。流れが早い
- 公式推奨の
まとめ
反省点
- 見映え
- 紹介映像で見るとちょっと地味かも
- TextAlive の楽曲情報のバージョン固定
- 思ったより変更があり、固定しないと変更内容によってはタイミングがおかしくなる
所感
- 結構高めのゴールだったが、なんとかマネジメントして形になってよかった
- マジカルミライや TextAlive のよさを自分なりに形にしつつ、今後の発展性のあるものにできたと思う
- ボカロや創作のカルチャーにおいて (VOCALOIDや歌声合成それ自体はもちろん) MMDのようなソフトウェアが果たした役割を考えると、確かにソフトウェアのイノベーションも重要そう
- ありがとうございました & 今後ともよろしくお願いします