JavaScript
UI
WebGL
WebGLDay 13

WebGLで映画のようなblurエフェクトを作る

More than 1 year has passed since last update.

はじめに

 WebGLを使った2Dのライブラリというと PixiJS が有名で、自分もときどき使います。
 しかしこれは、テキストとスプライトを扱うのは便利で2Dのゲームあたりには向いてますが、込み入った形状の「図形」を描くのはいまいち機能が足りません。またライブラリ自体のサイズが大きく、ちょっとしたUIの用途に使うには大げさすぎます。
 そこで今回は、図形の例として「ぼかし」(blur)のエフェクトを使って、ライブラリは使わず「生のWebGL」でごく普通のボタン等を恰好よく表示するテクニックを紹介してみようと思います。WebGLを学習者にお勧めの内容にしました。

 blurエフェクトとはこういうやつです。
ぼかし比較.png

 上が普通のボタン、下が縁取りをぼかしています。わずかな差ですが、こういうエフェクトをつけたほうがちょっと見た目きれいですね。

 それでもよくわからない、というなら、blurを強調したこのサンプルはどうでしょう? ちょっとボケていますね。
角丸長方形_1.png

 静止画ならAdobe Illustratorとかでさっさと作ればいいのですが、これをWebGLでやることでHTML Canvas内で光沢をアニメーションしたりが簡単にできます。ちょっと凝ったログイン画面などで使えそうですね。ただのbuttonタグではUI上満足できない場合に使うことを想定しています。

これの何がいいのかわからない、という人へ

 こんなの大した差じゃないし、特に格好良くもないよ、という人がよくいます。感じ方は人それぞれですし、どういうアプリケーションで使うのかにもよるので唯一の正解というのはありませんが、SF系の映画では「線」を表示するためにこのエフェクトがとにかくたくさん出てきます。少なくとも映画界では、こういうのが未来的なコンピュータの画像である、というのが信じられているわけで、我々エンジニアとしてもこの事実を無視するわけにはいきません。

 実例を紹介しましょう。

パシフィック・リムの場合

その1
pacific_rim_02.jpg
その2
pacific_rim_03.jpg
↑画像だとわかりづらいですが、HMDとスーツをつけて巨大ロボに乗ってます

エイリアン・コヴェナントの場合

alien_covenant.jpg

 どうです? 他の映画でももちろんたくさん出てきます。
 なお、どうしてこういう記事を書いたかというと、パシフィックリムの続編が来年公開予定と知ってムクムクと旧作も見たくなり、それを見たときにこのエフェクトを多用していることを改めて確認、さらに同じ日にこのAdvent Calendarのことも知ったのでついでにWebGLの記事にしてみた、というわけです。

WebGLでの描画

 WebGLに限らずGPUによる描画はなんでもそうですが、

  • 描画の単位は三角形
  • 三角形の頂点ごとに色またはテクスチャを指定する

 のは絶対条件です。なので、この光沢つき角丸長方形を三角形に分割してやる必要があります。

 なお、今回はテキストの描画については省略し、エフェクト付きの直線を表示することに話題を絞ります。
 枠線を太くして強調した図がさっきも出ましたがこれになります。

角丸長方形_1.png

 拡大するとこんな感じです。これをWebGLで描きたい。

角の拡大.png

角丸長方形

 まず、角丸長方形について説明します。これは線分4本と角度π/2の円弧4個からできています。
 描画単位が三角形である以上、円弧をいきなり描くわけにはいきません。なので、円弧を分割し、細かい3角形の繋がりとして四隅を書いていきます。正n角形を描くにあたり、n=8あたりだとカクカクしますが、n=256とかになれば円とほぼ見分けがつかない、というのと同じ原理ですね。分割が細かすぎれば描画パフォーマンスが悪化するので、実際には適当に分割する数を調整して感触を見ます。サイズが大きいほど、分割数も大きくしないと滑らかになりません。
 これを考慮に入れて、WebGLに与える頂点を構成すると次のような図になります。まだぼかしの部分は考慮に入れません。

vertex1.png

 赤い丸が定義すべき頂点です。急いで作った図なので雑なのは勘弁してください。赤い点が実線部を構成する頂点で、明るめの(背景とは明度が逆の)色を指定します。

ぼかしのエフェクト

 ではぼかしのエフェクトにいきます。ぼかしの部分を四角形の内側・外側両方につけるとすると、見た目1本の線は3本の線として描画すればいいのです。ぼかしている側のうち、線の本体から遠い部分は背景と同一の色を指定します。

vertex2.png
 ピンクはぼかしの中で最も明るい部分です。上の画像の赤い丸と背景の間の色で適当に決めます。黒い点は背景と同じ色、もしくは透明です。こうすると、中間のピクセルはWebGLがよろしくやってくれます。

 単なる「ぼかしつき角丸長方形」でも、実際には数十個の頂点が必要なんですね。リソースを贅沢につかってる気もしますが、最近のGPUのパワーを持ってすれば10000個くらいでもスマホですらまったく余裕なのでバンバン使いましょう。並列処理のパワーを有効に使う必要があります。GPUのような環境下では、コードを工夫して頂点数を削減し最適化に成功した気になっても、単に遊んでいるハードウェアリソースができるだけで時間的な効率にはまったく寄与しない、というケースがよくあります。

デモとまとめ

いきなり結果ですが、こういうのが描画できました。
ss.png

 静止画では「最初から画像ファイルでいいじゃん」という批判を免れることができませんので、光沢の色合いが時間とともに変化するようにして、こちらに実動するデモを作りました
 一番上は静止、真ん中は実線部と同じ色合いで明るさが周期的に変化、下は色合いも周期的に変化、というものです。これを応用して、マウスがボタンの中に入ったり、タップしたりのイベントで色が変わるようにすればアプリ用になってきます。

 コードを実際に確認したい人もこのリンク先のJavaScriptソースを見てください。

 WebGL/OpenGLというと、3次元CGを描画するための技術と考えている人が多いです。もちろんそういう用途が主力ではあるのですが、2次元グラフィックで高度なエフェクトやアニメーションをつけることで、3次元とは無縁のごく普通のアプリケーションのUXを高めることに最近個人的には注目していて、日々テクニックを研究しています。この方面の技術に興味がありましたら協力しますのでコンタクトとってください。