WebGLベースのアプリでニコファーレでVJをした話

# はじめに
## 背景
昨年の話になってしまうのですが、冬に社内イベントがあって、[ニコファーレ](http://nicofarre.jp/)を使わせて頂く機会がありました。社内ではメディア・アート系のサークルで部長をやっていまして、照明まわりの演出をお願いできないか、という依頼があり、二つ返事で引き受けたのでした。

今までも社内イベントで演出を担当することはありopenFrameworksをメインに使っていたのですが、当時はWebGLにガッツリはまっていまして、せっかくだから全てブラウザでやってしまおう、と。かなり本格的な（ページ開くだけでメモリ1GB消費する……）アプリを作る事になりました。去年のうちに話す機会がなかったので、一年越しですが今年のAdvent Calendarで少し紹介させて頂こうかと思います。

## 凄いぞニコファーレ
まず、ニコファーレがどんな場所かご存知でしょうか？前後左右、天井、そして前方左右に小さなパネル2枚、合計7枚の巨大LEDスクリーンを持つイベントホールです。ニコファーレさんのホームページに[LED映像フォーマット](https://web.archive.org/web/20160131055037/http://nicofarre.jp/smarty.php?tmpl=led_format.html&sctn=led_format)が公開されているので詳細はそちらで確認してもらうとして、ざっと説明すると1920×1080の画面の中に7画面分の映像を決まった場所に配置しニコファーレのPAシステムに流し込むと、1枚の絵が適切に分割されて7つのディスプレイに配信されます。

![配信映像例](https://qiita-image-store.s3.amazonaws.com/0/12843/03d7b69b-d4f1-382f-45fb-6faa0ef7b5d8.png)

こんな感じの入力になります。ホームページ上では事前に動画ファイルとして提出する事で自前映像を投影できる、とありますが、要相談でPCからの直接投影もできます。その場合もPCの外部ディスプレイの解像度を1920×1080に設定し、同じレイアウトで映像を配信すればOK。ただ、機材の相性もあるので、事前打ち合わせの段階で本番機材を持ち込み、会場の設備（ケーブル、変換器等）で問題なく動作するか確認する必要があります。変換器を噛ますとそれだけ遅延も増えるので注意が必要です。また、打ち合わせと本番のスタッフが違うこともあるので、この時利用した設備は記録しておいたほうが良いかもしれません。

# 準備
## 動作確認
さて、この凄いシステムなんですが……独自システムになっているため、既存のソフトでさくっと作るわけにはいきません。自分で作りこむにしても、動作確認のために毎回ニコファーレにお邪魔して確認……というわけにはいきませんよね。という事で、まずは自分で作ってきたライブラリをベースにして、このLED映像フォーマット（以下、ニコ形式）に合わせて作ったデータが、正しく表示されるか確認するツールを作ることにしました。作成後は同ライブラリをベースに制作していけば、いつでも会場映像をシミュレートできる、というのを目標にします。

![シミュレーション結果](https://qiita-image-store.s3.amazonaws.com/0/12843/20263967-47e8-8507-81d1-2eaa0a8cb485.png)

さっきの映像だと、こんな感じの出力をみたい、という話です。もちろん、視点移動なんかもできるようにします。

## MajVj
で、ここで少し自分のフレームワークについて説明しなければいけないのが辛いところです。WebGL学習と作品制作の両方を目的とした俺々ライブラリtmalibの上に載っていまして、普及する気もなく、勝手気ままに改造を重ねてきたシロモノなのです。MajVj自体はopenFrameworks版がもともと存在し、その一部をJavaScriptに持ってきた形です。コードは[GitHub](https://github.com/toyoshim/tmalib/tree/master/ext/mv)にあります。

このフレームワーク、基本的には2種類のモジュール群を再帰的に組み合わせることで全体の映像を構築するようになっています。1つはeffectと呼ばれるモジュール群で、その名の通り映像に対してエフェクトを与えます。基本的にはテクスチャを受け取って加工後のデータを現在のコンテキストに描画する、という動作です。もう1つはframeと呼ばれるモジュール群で、動画の再生、静止画の表示などのシンプルなものから、いわゆるジェネ系のモジュールなんかもあります。frame自体がeffectや別のframeを内包して映像を生成する事もあります。

![MajVj Overview](https://qiita-image-store.s3.amazonaws.com/0/12843/13afeb95-3ccf-6b55-4b0f-e3cef2fcee8c.png)

openFrameworks時代に書いた絵があったので、参考までに貼っておきます。ざっくりこんな構成で、さらに各mvFrameが入れ子になって同様の構成を内包できる、と思ってください。JavaScript版には当然ながらOSC連携もありませんし、細かいところは違うのですが、コンセプト的なところは共通です。

[GLSL Sandbox](http://glslsandbox.com/)のシェーダーをそのまま流用できる[frame](https://github.com/toyoshim/tmalib/tree/master/ext/mv/frame/sandbox)、[Shadertoy](https://www.shadertoy.com/)のシェーダーを流用できる[frame](https://github.com/toyoshim/tmalib/tree/master/ext/mv/frame/shadertoy)もなど作ってあるので、最悪ネタはどうにかなります。どんどん混ぜて使い倒せる。はっきり言ってシェーダー使ってる限りはopenFrameworksだろうがWebGLだろうが、はたまたAndroid用のJavaアプリだろうが、たいした違いがないのが嬉しいところです。シェーダー最強。未来のポータブル言語バンザイ。Write once, run anywhere!

## ニコ形式変換effect
ひとまずはこのフレームワークのeffectとして、[ニコ形式のテクスチャを3次元表示するモジュール](https://github.com/toyoshim/tmalib/tree/master/ext/mv/effect/nicofarre)を起こしました。ニコファーレの[フォーマット解説ページ](http://nicofarre.jp/smarty.php?tmpl=led_format.html&sctn=led_format)にbefore/afterなサンプル動画があるので、beforeな動画をmovie frameで再生させつつ、ニコ形式変換effectを噛ませてafter動画と比較、一致していれば正解、と。変換の正しさが比較的簡単に確認できるのがこの作戦の良いところです。

基本的にはニコ形式のテクスチャを受け取り、会場のLEDパネルに見立てたポリゴンに対して適切にテクスチャをUVマップしてあげれば良いだけです。上下反転や裏表反転がないように注意します。また正面左右にあるミニパネルについては座標データがありませんので、現地で調整することとします。また、天井はLEDがスパースなため、だいぶ暗くなります。

## キャリブレーション
ここまで作ったところで会場と一旦打ち合わせをしました。パネルの位置を確認するために以下のようなキャリブレーション用映像を作り、PCの相性確認も兼ねて実際のシステムから投影しました（ここに貼ってあるのはアニメGIF）。

![キャリブレーション用 before](https://qiita-image-store.s3.amazonaws.com/0/12843/e5548fe0-f4e2-c6b3-47e2-c0f390180df7.gif)

下がニコ形式変換effectを通した時の出力。前方左右2枚のパネルの位置が不明なため、この絵で言うと、どのタイミングで青くフラッシュすべきかを正しく調整する必要があります。青ラインが実パネルを通過するタイミングで座標を拾い、シミュレーション上のパネルの位置を実物と一致させます。なんか絵で見るとつまらない映像ですが、会場で見るとこれだけでも大迫力。

![キャリブレーション用 after](https://qiita-image-store.s3.amazonaws.com/0/12843/53916337-1f92-433d-69e7-f5476cb555ed.gif)

で、実のところ、この前方のミニパネルは移動が可能との事。取り外しもできるようなので、投影する側としては外したほうがやりやすいのです。が、その場合は舞台の袖が見えてしまい、全体としては演出が難しくなってしまいます。なので、この2枚に関してはキャリブレーションはしたものの、厳密な一致を求めるような表現は避ける方針にしました。例えば3次元的な映像はこれらを除いたディスプレイで行い、この2枚だけは音声に同期したFFT映像を流す、などです。

![別の映像を混ぜる](https://qiita-image-store.s3.amazonaws.com/0/12843/0ef05308-9fc9-8aed-534e-3c9f27a63f1b.gif)

例えばこんな感じで別の映像を混ぜたりとかですね。

## 本番機材
Chromebook Pixel（初代）で臨みました。映像はWebGL、音声はgetUserMediaで吸い上げてWeb Audioを使ってFFT。操作系はWeb MIDIを使ってMIDIコントローラから行います。映像は外部ディスプレイに向けて全画面でニコ形式で配信し、ノートのメイン画面にはコンソールが表示させます。DevToolsなんかも広げっぱなしになってて、何かあったら気合でlive codingできるので安心。驚くほどマシンが熱くなるので扇風機も持参。

![PixelのあるPA卓風景](https://qiita-image-store.s3.amazonaws.com/0/12843/acb0eb5a-2df4-9aae-185a-48b0685c1405.jpeg)

アプリ自体はいわゆるHTML5で書かれていたわけですが、会場が地下である事、本番のネットワーク環境に対して不確定要素が多いなどから、Chrome Appsとしてパッケージしたものを利用しました。コード中ではXHRとか使ってるけどAppsにしてしまえばそのままでもオフライン動作可能です。ローカルでサーバを立てる必要とか特に無し。

# 作り込み
## まだまだ必要なライブラリ
とりあえずニコ形式の出力をシミュレーションで確認する事はできるようになりました。ただ、このままではまだ特殊フォーマットの映像を作る難しさは解決していません。ここから更に2つの仕組みを作ることにします。

1. **レイアウトライブラリ**： 今までframeとして貯めこんでいたコード、あるいは再利用可能なシェーダーを任意の1ディスプレイ、あるいはディスプレイ群に展開する（例えば左右中央の3面を1つのディスプレイに見立てて表示する）仕組み。他にも左右のディスプレイで鏡像表示などができると良い。
2. **ニコ空間描画ライブラリ**： 3次元座標軸を使ってline(x1, y1, z1, x2, y2, z2)などとすると、(0, 0, 0)視点での擬似3次元映像が全方位のディスプレイに向けて投影される仕組み。会場中心にいないと歪むが、それでも相当な迫力のはず。

## レイアウトライブラリ
とりあえず、これさえあれば既存の素材で最低限はなんとかなるので、バックアップのためにもこちらを先に開発。このモジュールはeffectではなく[frame](https://github.com/toyoshim/tmalib/tree/master/ext/mv/frame/nicofarre)として作られました。投影するディスプレイ（群）の指示とframeを対にして呼び出すと、指定したframeが該当ディスプレイの表示枠内で動作するようになっています。

[![3面でGLSL Sandbox](https://qiita-image-store.s3.amazonaws.com/0/12843/c8bd2b97-4508-f10c-d459-309e1d49f241.gif)](https://tmalib.herokuapp.com/nico3/)

例えばこの映像なんかもGLSL Sandboxのシェーダーを走らせているだけですが、3面に展開しただけで迫力が倍増（アニメGIFだと辛いので、クリックしてもらえれば実際にWebGLで動くデモページに飛びます）。

## ニコ空間描画ライブラリ
こっちが本命。とりあえずライン引けるだけでも凄く楽しい。おなじみラインアートだけど、多面ディスプレイに展開するだけで迫力満点（WebGL版にリンクしてます）。

[![ラインアート真3D](https://qiita-image-store.s3.amazonaws.com/0/12843/f62cdae9-4761-390b-cb06-767ff33dedac.gif)](https://tmalib.herokuapp.com/nico4/)

シミュレーション画面での視点移動もできます。キーボードのxでシミュレーションのON/OFF、hjklで視点移動です。

![ランダムに線を書くだけ](https://qiita-image-store.s3.amazonaws.com/0/12843/c6cb5a0d-323b-d0b9-8ea5-52d7d0cb350e.gif)

もちろん、線以外のプリミティブもサポート。アニメGIFはセガサターンっぽい正方形ポリゴンですが、リンク先のWebGL版では立方体になっています。

[![サターンぽい](https://qiita-image-store.s3.amazonaws.com/0/12843/a453f4bb-8e34-b549-66d6-02597f079c63.gif)](https://tmalib.herokuapp.com/nico5/)

展開図だとこんな感じです。

![せーがー](https://qiita-image-store.s3.amazonaws.com/0/12843/e0760b23-5dc4-9095-3f32-03fb1863cacc.gif)

木星。ドロテアまだあります。

![TAITO臭](https://qiita-image-store.s3.amazonaws.com/0/12843/375277e2-6df3-f08c-26de-95fe8ef64303.gif)

パーティクル飛ばして絵を作る事も。これもリンク先は別バージョン。

[![パーティクル](https://qiita-image-store.s3.amazonaws.com/0/12843/ca419392-2cf7-ada7-077d-a9f5b968fca1.gif)](https://tmalib.herokuapp.com/nico6/)

# 最後に
あまり技術的なところは解説していないんですが、もしWebGLでこういうことするの好きな人がいたら、ぜひ一緒にお話しましょう。そこそこのGPUさえ載っていて、JavaScriptの性能上の落とし穴さえ知っていれば、かなりの事ができるようになってきた印象です。

アプリ全体で公開したいところですが、個人情報やら版権やらが絡むので、あくまでもベースとなったライブラリだけをGitHubで公開しています。

あと[当時ブログに書いていた記事](http://blog.toyoshima-house.net/search/label/WebGL)もボチボチあって、合わせて読むと制作過程の様子とかわかって面白いかもしれません。

それでは、最後にPerfumeの1mmをどうぞ。YouTubeでフルバージョンのPVが公開されていたので、強引に同期させてみました。
[![Perfume 1mm](https://qiita-image-store.s3.amazonaws.com/0/12843/f91add01-3c01-4d30-8279-12a57a07728d.png)](http://tmalib.herokuapp.com/nico8/)