ウェブカメラをテクスチャに適用する

実行結果

今回のサンプルの実行結果

動画をテクスチャに適用してみよう

前回は video タグで動画を読み込み、それを動的にテクスチャに適用する方法について解説しました。

動的に多彩なシーンを WebGL だけで表現するのは大変ですが、動画を用いれば簡単に演出効果を高めることができます。

技術的には、あくまでも再生されている動画のその瞬間瞬間のシーンを、単純にテクスチャに適用するだけでした。これに関してはそれほど難しいことはありませんでしたね。ただし、モバイル端末ではまだまだ再生が難しい場面があるなど、諸手を上げて使っていける状況ではないことについても触れました。この辺りは、今後に期待といった感じですね。

さて、今回はウェブカメラの映像をテクスチャに適用することにチャレンジしてみましょう。

ウェブカメラを使うとは言っても、基本的な考え方は video タグを用いた前回のやり方と同じです。ウェブカメラから動的に、リアルタイムに映像を取得し、それをテクスチャに適用します。

今回のキモになるのは、いかにしてウェブカメラから映像を取ってくるのか。これには WebRTC と呼ばれている次世代技術を利用します。

WebRTC は Web Real Time Communication の頭文字を取った言葉です。その名が示す通り、リアルタイム性の高い処理を行うための技術を定めた API ですね。WebRTC を用いると、今回のようなリアルタイムなウェブカメラからの映像の取得、あるいはそれらを用いたデータ通信といったコミュニケーションに関する処理を行う事が可能になります。

今回はウェブカメラから映像を取得することを主眼に、WebRTC を駆使してウェブカメラからテクスチャに映像を動的に適用するまでの流れをしっかり習得していただければと思います。

Chrome における HTTPS とウェブカメラ

Google Chrome では、HTTPS 接続でない場合ウェブカメラを自動的にブロックします。

当サイトでは SSL を導入したのでサンプルが動きますが、ご自身でウェブカメラを利用したコンテンツを制作する際は注意してください。

WebRTC を利用したカメラへのアクセス

実は今回のサンプルは、前回の内容と基本的な骨組みはほとんど同じです。

まず最初に WebRTC が利用できるのかどうかなどをチェックしながら、ウェブカメラを javascript からアクセスできる状況に持っていきます。これが完了したら、そのウェブカメラをソースとして適用した video タグを作り、これを動的にテクスチャのソースとして適用します。

前回は、読み込んだ動画を video タグに適用しましたが、これをウェブカメラに置き換えるだけなのですね。ですから今回ポイントとなるのは、レンダリングが開始される前の段階、初期化の部分ということになります。

コードを実際に参照する前に、WebRTC 自体がどれくらい使える状況なのかをまず整理しましょう。

WebRTC は WebGL 同様に新しい規格であるため、どんな環境でも万全で使えるというわけには残念ながらいかないのが現状です。

今回挑戦するウェブカメラ(マイクも含む)へのアクセスには、WebRTC の StreamAPI などが利用できる状態になっていなければなりません。主要ブラウザの対応状況を見ると、本テキスト執筆時(2015 年 1 月)の時点では、PC 版の Chrome や Firefox、Opera が対応しています。モバイル環境に目を移すと、Android Chrome と、Android5.0 以上の Chromium ベースのブラウザであれば利用が可能です。残念ながら、現在はまだ iOS Safari では利用できない状況ということになります。

手元にある Android 端末でテストしてみたところ、インカメラを使って映像を取得し、サンプルを動かすことができていました。動画ファイルを読み込む処理ではなぜかコンテキストがロストすることなどがありましたが、普通に動いていて少々不思議です。いずれにしても、モバイル端末ではまだまだ使える状況がかなり限られているというのが現状のようです。

さて状況がある程度わかったところで、具体的な実装方法を見ていきましょう。

まず今回のサンプルを実現させるためには、策定中の API として getUserMedia メソッドと、オブジェクト URL を生成しデバイスの情報を URL に変換する window.URL メンバが利用できるかどうかをチェックする必要があります。これらはいずれもベンダープレフィックス付きで動作する可能性があるものですのでそれを踏まえた初期化を行います。

getUserMedia の初期化

// ベンダープリフィックスを考慮して初期化
navigator.getUserMedia = (
	navigator.getUserMedia ||
	navigator.webkitGetUserMedia ||
	navigator.mozGetUserMedia
);

上記のようにすればいいですね。

これでもし getUserMedia が取得できないようなら、そもそもウェブカメラを用いた処理を行うことはできないということになります。

もうひとつの window.URL も、やはりベンダープレフィックスを付けて初期化できるかどうかチェックします。

URL メンバの取得例

var url = (
	window.URL ||
	window.webkitURL
);

こうして両者の初期化ができたら、いよいよウェブカメラへのアクセスを試みます。しかしウェブカメラのようなデバイスは、ユーザーの許可がなければアクセスできないようになっています。次項ではそのあたり詳細に見ていきましょう。

ユーザーに許可を求める

ウェブカメラやマイクといった、クライアント環境に搭載されているデバイスにアクセスするには、ユーザーの許可が必要です。

PC 版の Chrome であれば、ブラウザのクライアント領域上部にアクセスを許可するかどうを尋ねるインターフェースが出てきます。これは見たことがある人も多いのではないでしょうか。

ユーザーがデバイスへのアクセスを許可すると、javascript でウェブカメラやマイクといったクライアント端末のデバイスにアクセスできるようになります。しかし、このユーザーへ許可を求める UI が出るのは、いったいどんなタイミングなのでしょうか。

このことをしっかり理解していないと、正しい実装ができません。

従来の当サイトのサンプルの多くは、ページのロードと同時に実行されるような実装を行うことが多かったですね。言い換えると window.onload イベントをトリガーにしていることがほとんどだったわけです。

しかし今回のケースは、単純にページのロードで WebGL を動かし始めてしまうと、ユーザーへデバイスへのアクセス許可を求めるインターフェースが画面上に出た状態のまま、ウェブカメラにアクセスすることができない状態で処理が進んでいってしまいます。

これでは困ってしまいますね。

今回のサンプルはそのあたりも考慮しつつ、ユーザーがデバイスへのアクセスを許可したか、あるいは拒否したか、いずれにしてもそのユーザーのアクションをトリガーとして処理がスタートするようにしてみましょう。

ユーザーへの許可を求めるインターフェースが出るのは、以下のようなコードが実行されたその瞬間になります。

getUserMedia メソッドの実行

navigator.getUserMedia( ※ここに適切に引数を与える );

上記のようなコードが実行されると、その瞬間にインターフェースが画面上に出現します。そして、ユーザーがそれを許可したかどうかは、引数に関数を渡すことで、その与えた関数がコールバックとして呼び出されることによりわかるようになっています。

より具体的に言うと、以下のようにみっつの引数を与えます。

getUserMedia に与える引数

navigator.getUserMedia(
	{},           // 第一引数:取得したいデバイスの種類
	function(){}, // 第二引数:取得に成功した場合のコールバック
	function(){}  // 第三引数:取得に失敗した場合のコールバック
);

第一引数には、取得したいデバイスの種類をオブジェクトとして格納して渡してやります。第二引数と第三引数にはそれぞれ関数を与えます。成功した場合と失敗した場合、それぞれの場合に呼び出されるコールバック関数を用意すればいいのですね。

今回のサンプルでは、成功した場合に呼び出されるコールバック関数から、レンダリングを開始する関数を呼び出してやることで、正しくテクスチャを初期化してからレンダリングが開始されるようにしています。

それでは肝心の初期化処理コード全体を見てみます。

ウェブカメラ利用のための初期化関数

// ウェブカメラが利用できるかをチェックする
function canWebcam(){
	// ベンダープリフィックスを考慮して初期化
	navigator.getUserMedia = (
		navigator.getUserMedia ||
		navigator.webkitGetUserMedia ||
		navigator.mozGetUserMedia
	);

	if(navigator.getUserMedia){
		// user media に対する設定
		navigator.getUserMedia(
			// 有効化する user media
			{
				video: true,
				audio: false
			},

			// usre media の取得に成功した場合
			function(localMediaStream){
				var url = (
					window.URL ||
					window.webkitURL
				);

				// video エレメントの生成
				video = document.createElement('video');

				// video エレメントにイベントを設定
				video.addEventListener('canplay', function(){
					// 複数回呼ばれないようにイベントを削除
					video.removeEventListener('canplay', arguments.callee, true);

					// video 再生開始をコール
					video.play();

					// レンダリング関数を呼ぶ
					render();
				}, true);

				// video エレメントのソースにウェブカメラを渡す
				video.src = url.createObjectURL(localMediaStream);
			},

			// user media の取得に失敗した場合
			function(err){
				// 取得に失敗した原因を調査
				if(err.name === 'PermissionDeniedError'){
					// ユーザーによる利用の拒否
					alert('denied permission');
				}else{
					// デバイスが見つからない場合など
					alert('can not be used webcam');
				}
			}
		);
	}else{
		// ブラウザがサポートしていない
		alert('not supported getUserMedia');
	}
}

先ほどまでに説明してきたことを踏まえてひとつひとつ丁寧に見ていけば、それほど難しいことはないと思います。

大事なポイントは navigator.getUserMedia の引数には、オブジェクトと関数をふたつ、合計みっつの要素を与えていること。そしてもうひとつ、video タグのイベント周りをちゃんと実装してやることでしょう。

まず getUserMedia の呼び出しに際し、第一引数には取得したいデバイスの種類を表すフラグを指定したオブジェクトを渡します。今回はウェブカメラのみでマイクの入力は使いませんので、video のみ true にしています。

第二引数はユーザーがメディアデバイスへのアクセスを許可した場合に呼ばれるコールバック関数です。この関数の引数には対象のメディアにアクセスできるオブジェクトが渡されてきますので、これを参照しながら処理を進めるようにします。

今回のサンプルでは、前回同様に video タグを動的に生成し、これにイベントを仕込んでいます。 canplay イベント経由でレンダリング関数が呼ばれる仕組みになっているのがわかると思います。この canplay イベントは何度も発生することがあるために、一度しかレンダリング関数が呼ばれないようにしている点が地味にポイントです。

最終的には video エレメントのソースを指定してやりますが、ここでは window.URL.createObjectURL メソッドに、引数として受け取ったデバイスオブジェクトを渡してやれば OK です。

そして最後に navigator.getUserMedia の第三引数にはユーザーがメディアデバイスへのアクセスを拒否した場合や、そもそもデバイスが利用できない場合の処理を記載した関数を渡します。こちらは引数として拒否された理由が渡されてきますので、それをたよりに処理を分岐しているのがわかると思います。

ユーザーがアクセスを拒否した場合、コールバックに渡されてくるエラーオブジェクトの name プロパティに 'PermissionDeniedError' がセットされています。

補足コラム:アクセス拒否の解除

実装しながらテストを行っていると、意図的にせよ偶然にせよ、アクセス許可の問に対して拒否を選択するケースが出てきます。

ブラウザはこの拒否したというユーザーのアクションを記憶しており、次に同じページを開いた時に無条件で拒否を選択するようになってしまいます。こうなると、一見して二度と有効化することができないように感じてしまいます。

この状態になってしまったら、拒否を解除するための手順を行ってやります。これはそんなに難しいことはなく、単純に Chrome であればアドレスバー(オムニバー)の右側部分にカメラマークが表示されていると思いますので、そこから変更してやります。これで再度、有効化を尋ねる UI が表示されるようになりますので、そこからアクセスを有効化してやれば OK です。

まとめ

さてウェブカメラの映像をそのまま WebGL でテクスチャとして利用するテクニックについて見てきましたがいかがでしたでしょうか。

video タグを用いた動画テクスチャの実装と、基本的な原理は同じだというのがわかったのではないでしょうか。また WebRTC に関する処理の部分を見てみても、全体的な構造自体はそれほど難しくないので、普通に WebGL でテクスチャを利用した経験があれば比較的簡単に対応ができるのではないかなと思います。

ウェブカメラから入ってくる映像をテクスチャとして活用できるということは、ポストエフェクトのような処理を適用してみたり、あるいは映像を解析してシェーダでなにかしらの処理を行ったりといったことができるということでもあります。

スマートフォンやタブレット端末の普及もあり、今後はカメラをデフォルトで搭載している機器で WebGL が動く状況も増えてくると思います。工夫次第で面白いコンテンツを生み出せる可能性を秘めたウェブカメラ+WebGL、ぜひチャレンジしてみていただければと思います。

今回も実際に動作するサンプルを用意してあります。以下のリンクから試してみてください。

entry

PR

press Z key