p5.jsのWebGLモードで「ブラー」を使ってみる


2018年01月07日
With
p5.jsのWebGLモードで「ブラー」を使ってみる はコメントを受け付けていません

しばらく続けると言っていた「フラクタル」ネタを一時お休みして、昨年末からp5.jsで3D表現ができる「WebGLモード」を手探りでいろいろ試しています。

これまでの「p5.js WebGLモード」ネタ

今回のお題は「p5.jsのWebGLモードでブラーやグローぽい効果を使いたい」。以前、2Dモードでブラーやグローのような効果を出すやり方については、こちらのエントリで試してみていました。

p5.jsで「ブラー」や「グロー」を使う方法

2Dモードでの「ブラー」は

・backgroundでの背景色塗りつぶしをせずに、アルファ値を設定したrectで画面を覆う

というやり方が一番手軽でよさそうでした。

2Dモードと同じやり方だとうまくいかない

テストのために、600×600×600の空間上に100個くらいのsphereをばらまき、それぞれをランダムに動かすというスケッチを使います。

まずは2Dモードと似たやり方を試してみます。draw()内に記述した「background()」は、2Dモードと同じようにフレームごとに背景色での画面塗りつぶしを行います。draw()に記述せずにsetup()の中でだけ指定してやると、フレームが変わっても画面上の絵が消されないので、こんな感じで軌跡が残ります。

うん。これはこれで味がありますね(笑)。でも、これは「ブラー」じゃない。

2Dでのブラーのやり方を参考に、この画面にアルファ値を設定したオブジェクトを重ね描きしてみます。

・HTMLでブラウザの背景色を黒に設定
・カメラの目前に色が「黒」でアルファ値を「20」に設定した「plane」を描画

これで動かして見た結果がこちらになります。

お。イケる? …と、しばらく画面を見ていると、なんかヘンです。画面に謎の黒い軌跡が残り続けてしまい、だんだん黒く塗りつぶされていきます。しまいにはオブジェクトが見えなくなってしまいました。

さすがに、2Dの時とまったく同じやり方では無理そうです。

過去の座標を記憶しておく「王道」がよさげ

仕方がないので、より王道な方法を試します。各オブジェクトに数フレーム前までの自分の座標を覚えさせておき、そのすべてを描くというやり方です。古いフレームのものほど薄くなる(アルファ値が下がる)ように表示すれば、俺が求めているブラーっぽくなるのでは……と試した結果がコチラです。

●WebGLモードでの「ブラー」っぽいやつ(クリックすると別タブが開きます)

おお、イイ感じ。イメージしていたブラーに近いものができました。orbitControlを入れてありますので、ドラッグ操作でぐるぐる回して見られます。

この方法だと、画面上に描くオブジェクト数が「実体の数×軌跡の長さ(世代数)」になるので、表示する実体の数を多くするほど、そして軌跡を長くするほど、文字どおりのかけ算で急速に負荷が高くなっていきます。表現に合わせて、調整が必要でしょうね。

あと、以前試した2Dのやりかただと、表示されているすべてのオブジェクトにブラーが適用されますが、今回の方法なら、ブラーを適用するオブジェクトと適用しないオブジェクトを簡単に分けられます。2Dで使ってもイケそうです。

var sphereArr = [];
var sphereMax = 50;
var trackCount = 15;

function setup() {
	createCanvas(600, 600, WEBGL);
	noStroke();
	camera(0, 0, 350, 0, 0, 0, 0, 1, 0);
	for (var i = 0; i < sphereMax; i++) {
		sphereArr[i] = new PointObj();
	}
}

function draw() {
	background(0);
	orbitControl();
	sphereArr.forEach(sphereObj => { sphereObj.drawSphere(); sphereObj.update(); });
}

class PointObj {
	constructor() {
		this.x = random(-300, 300);
		this.y = random(-300, 300);
		this.z = random(-300, 300);
		this.xmove = random(-3, 3);
		this.ymove = random(-3, 3);
		this.zmove = random(-3, 3);
		this.preX = [];
		this.preY = [];
		this.preZ = [];
	}
	drawSphere() {
		for (var j = 0; j < trackCount; j++) {
			push();
			translate(this.preX[j], this.preY[j], this.preZ[j]);
			ambientLight(120);
			pointLight(255, 255, 255, 255, -600, -600, 300);
			ambientMaterial(255, 255, 255, 255 / (j * 5));
			sphere(3);
			pop();
		}
		push();
		translate(this.x, this.y, this.z);
		ambientLight(120);
		pointLight(255, 255, 255, 255, -600, -600, 300);
		ambientMaterial(255, 255, 255, 255);
		sphere(3);
		pop();
	}
	update() {
		this.x = this.x + this.xmove;
		this.y = this.y + this.ymove;
		this.z = this.z + this.zmove;
		if (this.x > 300 || this.x < -300) { this.xmove = -this.xmove; }
		if (this.y > 300 || this.y < -300) { this.ymove = -this.ymove; }
		if (this.z > 300 || this.z < -300) { this.zmove = -this.zmove; }
		var tmpX = [];
		var tmpY = [];
		var tmpZ = [];
		for (var j = 0; j < trackCount; j++) {
			tmpX[j] = this.preX[j];
			tmpY[j] = this.preY[j];
			tmpZ[j] = this.preZ[j];
			this.preX[j] = tmpX[j - 1];
			this.preY[j] = tmpY[j - 1];
			this.preZ[j] = tmpZ[j - 1];
		}
		this.preX[0] = this.x;
		this.preY[0] = this.y;
		this.preZ[0] = this.z;
	}
}

とりあえず「ブラー」についてはこんな感じで。「グロー」については次回にチャレンジしてみます。

次回に続く