p5.jsで3次元アニメーションを試してみた


2017年09月25日
With
p5.jsで3次元アニメーションを試してみた はコメントを受け付けていません。

気づけば前回のエントリから既に1カ月以上が経過。いかんですね。正直、コードはいっぱい書いているのですが、それをエントリ化しようと整理し始めると、追加で調べてみたいことや試してみたいことが次から次へと出てきて、なかなか手が進みません。

今回は「p5.js」の3次元機能について。Processingでは、「size」関数の第3引数に「P3D」あるいは「OPENGL」と書いておくことで、スケッチ内で3次元座標を扱えるようになります。「p5.js」にも同等の機能が用意されており、createCanvas関数の第3引数として「WEBGL」と指定してやります。

試しに、以前Processingで書いた3次元のスケッチをp5.js向けに書き直してみました。

(2017/10/12追記:このスケッチは5.14向けのものですが、p5.jsでは5.16以降でWebGL関連の機能が大きくアップデートされており、ここにあるコードは動きません。とりあえず、5.16で動くようにmobile版を書き直したものをこちらのエントリに上げています)

●[DEMO] 3Dアニメーションのテスト for PC(別タブが開きます)
●[DEMO] 3Dアニメーションのテスト for Mobile(別タブが開きます。モバイルブラウザ向けにテクスチャ画像をはずしてカンバスサイズを調整したもの。PC向けが重い場合はこちらをどうぞ)

派手に動いてますが、コードはほんの40行弱。立方体の固まりを軸をずらしながらグルグルと回転させ、その中を通る直線上でカメラを動かしています。

var boxSize = 50;
var distance = 170;
var halfDis;
var boxNum = 6;
var angle = 0;
var cameraPos = 1100;
var cameraZ = 5;
var img;

function setup() {
	createCanvas(600, 600, WEBGL);
	ambientLight(255);
	pointLight(255, 255, 255, 255, 1000, -500, 0);
	ambientMaterial(100,100,140,255);
	img = loadImage("tex-test.jpg");
	halfDis = distance * (boxNum-1)/2;
}

function draw() {
	background(220, 220, 255);	
	camera(width/2, height/2 , cameraPos);
	if (cameraPos > 1100 || cameraPos < -850) {cameraZ = cameraZ*-1; }
	cameraPos = cameraPos+cameraZ;
	translate(width/2, height/2);
	angle = angle + 4;
	rotateY(angle/180);
	rotateX(angle/180);
	for(var z = 0; z < boxNum; z++){
		for(var y = 0; y < boxNum; y++){
			for(var x = 0; x < boxNum; x++){
				push();
				translate(x*distance-halfDis, y*distance-halfDis, z*distance-halfDis);
				box(boxSize);
				texture(img);
				pop();
			}
		}
	}
}

p5.jsを3次元モードで使うときには、2次元モードから頭を切り替えないといけないところがいくつか出てきます。

まず、大きいのは座標系がイメージしづらくなること。2次元モードのとき「canvas左上の頂点が原点」というのは紙のメタファで比較的容易にイメージできますが、3次元モードの時、canvasとして作る窓は「カメラのファインダー」のような役割を果たします。つまり「カメラ位置」が3次元上を移動すると、相対的にオブジェクトの表示位置も変化してしまうのですね。空間の中で「オブジェクトをどこに描くか」と同時に「カメラをどこに置くか」を意識して作る感じになります。

また3次元モードでは、前出の「カメラ」のほか、「ライト」、オブジェクトの「材質」「テクスチャ」なども意識する必要があります。カメラ位置はもちろん、レンズの焦点距離はどうするか、どんな光をどこからどのくらいの強さで当てるかといったことを、専用の関数で指示して、イメージしている出力に近づけていくといった作業が必要です。

コードでは、12~14行目でライトとオブジェクトの材質、21行目でカメラ位置をそれぞれ指定し、15行目であらかじめ用意しておいた画像をテクスチャとして34行目で張り付けています。

全部を試したわけではないのですが、リファレンスを見てみると、このあたりをいじるための関数がほかにもいろいろあるようです。行単位で消してみたり、数値を変えてみたり、別のものに入れ替えたりすると、結果がまったく変わってきます。ちなみに、オブジェクトの材質を指定しないと、デフォルトでは表面反射がなんかサイケデリックな感じになりますね。これはこれで味がありますけど。

……で、実際に3次元モードを試してみて思ったのが、正直なところ、3次元の作品については、専用のツールを使った方がラクそうだということ。僕の場合はゲームエンジンの「Unity」上で「Unicessing」を使うのが、一番学習コストが低そうです。「Unicessing」というのは、Unity上でProcessingの記法を使って絵を描けるアセットです。

Unicessing (Unity Asset Store)

ベースがProcessingなので、言語仕様はJavaなのですが、あまり難しいことをしようと思わなければ、p5.jsからの移植には、それほど手間が掛かりません。また、カメラやライト、エフェクトをプレビューを見ながらGUIでいじれるのが大変ラクです。さらに、Unityは標準でVR開発に対応しています。つまり、Oculus RiftやHTC Viveを被れば、自分でUnicessingを使って描いた3次元スケッチの中に入ることができるんですね。

うちには発売後すぐに買ったOculus Rift CV1があるので、Unicessingを使ってUnity上に先ほどの3Dスケッチを描き、さらにVRモードでRiftからプレビューするというのを試してみました。あの、グルグル回る四角形の固まりに、頭を突っ込んでいるような感じになるんですよ。もちろん、周囲を見回すこともできます。ちょっと感動しますよ、これ。

Unity上の開発画面になりますが、Unity + Oculus Rift + Unicessingによる作業の様子を動画にしました。途中、画面が上面図に切り替わりますが、その時、カメラの視界がグルグルと回っているのが分かると思います。それが、Riftからリアルタイムで周囲を見回している状態です。

そんなわけで、3次元がやりたくなったときには、Unity + Unicessingを本格的に使ってみることにして、p5.jsについては、まだまだ2次元ベースでいろいろ試していこうと思った次第です。