WebGL Advent Calendar 2015 の 21 日目の記事です。
ごあいさつ
みなさんこんばんは!! 🌙 ムーン!
今年はお正月に連休とっていいらしいということで人生初と言っても過言ではないとても一般的な年越しができそうなことを本気で嬉しく感じております doxas ともうします! 🎍
さて、今年の WebGL アドベントカレンダーはとてもガチな人たちがたくさんの有用な記事を書いてくださっており、超個人的な感想をいきなり捩じ込みますがわたくしとても嬉しいです。
思えば 3 年ほど前、wgld.org を書き始めたころは WebGL というと動作も不安定で、本当にマニアみたいな一部の人しか使っていない謎技術でありましたが、今日こうして一般的なウェブサイトでも割と WebGL が使われるようになってきているのを見るにつけ、本当に時代の変化を感じますし、またそのすさまじい性急さについていくのが大変だわーなどと年寄り臭く感じる次第です。
今年はアドベントカレンダー全体で、かなり技術的に有用なもの、または解説として非常に密度の濃いものが多い気がします。
わたしも普段は解説のようなことばかりしておりますが、ここではちょっと気分を変えて、たまには消える魔球と見せかけて消えないただのストレートでも投げてみようと思い立ち、柄にもなくがんばりました。
そのキャプチャが像が以下のようになっております。
「え?」
「なにこのへんな映像……」
「ついに doxas 氏が天に召されましたか……」👼👼👼 ==3
……
勝手ながらみなさんの心の声を代弁させていただきました。
そうです、この変なよくわからん映像が今回のデモ作品です。
これ失敗じゃないです。
ちゃんとうまくいってます。
でも明らかに描画結果としてはおかしなことになってますね。ここは、ひとつ心を落ち着けて画面をよーく見てみましょう。
右端のほうに、なんかぺろんちょしている付箋みたいなのがありますね。🚩
はい。このぺろんちょしているやつを、おもむろに引っ張って画面内に移動させてみてください。そうすると、なんかよくわからんことが起こります。
ゆっくり動かしたほうがいい感じになりますが、トチ狂ったように左右にブンブン動かすのも、また自由です。
実際に動くデモは以下に置いてあります。
デモ:Lillusion
なにが起こっているのか
さて、実際にデモを動かしてみると、なるほどそういうことねとわかっていただけたかと思います。
要するに、錯視を使った古典的な動画表現とでも言いましょうか、柵と柵の隙間から立方体がこんにちは! と言いながら狂ったように回転するデモです。フレームは全部で 6 フレームあります。
画面の右端から引き出すことができるレイヤーには、6px にひとつの割合で透明なピクセルが 1px だけ仕込まれています。その他の部分は、真っ黒に塗りつぶされているので、このレイヤーをかぶせると、透明になっている全体の 1/6 だけが人間の目に捉えられます。
背景部分に描画されている風景は、この透明な 1px から見える部分で整合性が取れるように計算して、6px 間隔でシーンが描かれています。ですから、レイヤー部分をゆっくり移動させることで、まるでアニメーションしているかのようにシーンが動いて見えるというわけです。
きちんと意味のある映像に見える場所が等間隔に 1px ずつ並んでいるので、マウス操作をゆっくり行ったほうがより綺麗なアニメーションとして見えるわけですね。完全に余談ですが、わたくしマウスカーソルの動き最速にしていることが多いのでめっちゃ難しいですね! 自分で言うのもなんですけど! 🐭🐭🐭 チュー
以下は、レイヤーを画面の中央辺りまでひっぱりだしてみた様子をキャプチャしたものです。
レイヤーが乗っかっている部分は、キューブ状のオブジェクトがくっきりと浮きだして見えます。
原理がわかっていても、なんか不思議ですね。 👀❣
レイヤーをかぶせる前の状態では、なんとなく 3D シーンっぽい雰囲気だけがあって、その全容がはっきりしていません。レイヤーをかぶせることでエッジの利いた立方体がぐりぐり動き、うまく言葉で言い表すことのできない、表現しにくい微妙な、なんとも言えないかすかな感動がある……かもしれません。
ほんの少しだけ技術的な話
背景のシーンは、GLSL を使ってレイマーチングで描いています。
GLSL では、これから描画しようとしているピクセルの座標を返す組み込みの変数があります。それがみなさんが三度の飯より大好きな、そうです gl_FragCoord
です。これから描画しようとしている座標がわかるのであれば、あとはその X 座標値をうまく利用してあげれば、等間隔に 1px ごとに描くシーンを変化させることは簡単ですね。
modulo = mod(gl_FragCoord.x, split); // split == 6.0
上記のようにすれば、変数 modulo
には、0.0 から 5.0 までの数値が得られます。これを立方体の Z 軸回転に適用してやればいいわけです。ちょっと気をつけないといけないのは、今回の手法の場合、6 フレームしか表現できません。また、最初のフレームと最後のフレームとがスムーズに繋がるようにうまくアニメーションさせてやらないといけませんね。
先ほどのデモ作品の場合は、立方体が 6 フレームでちょうど 90度 回転するようにしているため、最初と最後のフレームがスムーズに繋がるように見えているはずです。☃
GLSL によるレイマーチングを用いる場合、描画命令は一回だけで済みます。もしこれを普通に drawArrays
などの WebGL の描画命令で同じようにやろうと思うと、どうしてもステンシルバッファなどを使って複数回描画を行う必要があるでしょう。そういった点では、今回のデモを行うためにはフル GLSL だけで描画していることがむしろメリットになっているとも言えますね。
GLSL 最高ですね。大好きです! ♥
起こりうる問題点
今回のデモの難しいところは実は結構あって、このデモを表示するスクリーンがドットバイドットの場合はいいんですが、Retina ディスプレイなどのように高解像度で各ピクセル間が OS の制御により微妙に補間されるような場合、色が滲んでしまい残念なことになってしまいます。高解像度最高ですね!! 💻
高解像度のディスプレイに対応する方法というのはもちろんあるのですが、今回はそこまではしていません。
また、今回の作品の場合は画面が表示されたタイミングで一発描画しており、逐一アニメーションしているわけではありません。ですから、最初にちょっとだけ負荷の高い瞬間があるものの、継続して描画し続けるわけではないので比較的貧弱なデバイスでも動くとは思います。ただし、レイマーチングを使って結構シビアな計算をすることになるため、その他のシェーダ系のデモにも言えることですが端末による描画結果の微妙な違いが出ると思います。
試していませんが、端末によっては変なノイズが出たりすることもあると思いますし、このあたりは調整が難しいですね。📡
あ、ちなみにレイヤー部分の黒い柵のような模様は Canvas2D を使ってプロシージャルに、その場で動的に描いています。「透明、黒、黒、黒、黒、黒」という感じの、6 x 1 ピクセルサイズの小さなキャンバスを作って、これをレイヤーになっているエレメントの background-color
に toDataURL()
を使ってリピートな感じのスタイル当てつつ流し込んでます。💃💃💃💃💃
それと、最後の最後にとても素敵なことをいいますが、実用性は皆無 ですので、せめてアドベントカレンダーを見ながら楽しい気持ちになっていただけますと幸いです。
すごく、実のない話をしている!
やばい!
すごい!
おすし 🍣 が食べたいです! 酔ってないです!
みなさんも、ぜひ自分なりの、オリジナルなデモを作ってみてください。特にシェーダは本当に工夫する余地が多く、リアル世界の錯視などをシェーダで再現するのは割と簡単にできますし、とても楽しいですよ。
それでは来年もよろしくお願いします!!!!! 🌠
(いつも以上にとても冷静に記事を書くことができました。いつもありがとうございます)