p5.jsのWebGLモードでなんとなく3Dフラクタル


2017年12月25日
With
p5.jsのWebGLモードでなんとなく3Dフラクタル はコメントを受け付けていません。

「p5.jsでフラクタルイメージを描く」シリーズ。今回はWebGLモードを使って3Dで描いてみます。題材は「メンガーのスポンジ」です。

・メンガーのスポンジ - Wikipedia
・Menger sponge - Wikipedia(英語版、解説はこちらのほうが詳細)

これを描いてみようと思ったきっかけは、こちらのビデオ。

・Coding Challenge #2: Menger Sponge Fractal - The Coding Train

「The Coding Train」というYouTubeチャンネルには、主にProcessingとp5.jsを使ったクリエイティブ・コーディングのチュートリアルが大量にアップされています。やたらハイテンションなヒゲの講師はダニエル・シフマン先生といい、Processing界隈では大変有名な方です。日本語版も出ている「Nature of Code」という本の著者でもあります。僕も持ってます。

■ Nature of Code – Processingではじめる自然現象のシミュレーション

Coding Challengeシリーズは、いわゆる「10分で作る○○」的なヤツで、シフマン先生が毎回、終始ハイテンションでライブコーディングします。「メンガーのスポンジ」のビデオはProcessing向けなのですが、英語字幕を目で追いながら、p5.js向けに移植してみました。あ、今回はES2015準拠で書いています。

●メンガーのスポンジ powered by p5.js(クリックすると別タブが開きます)

開くと、画面一杯に立方体が回転しはじめます。ブラウザ上で1回クリックする度に、再帰が1段階進みます。ビデオに出てくるコードは、クリックすればするほど、どんどん再帰が深くなっていくものだったのですが、当然のごとく再帰が深くなればなるほど処理が重くなり、しまいにはほぼ完全な静止状態(処理待ち)になってしまいます。それを避けるために、こちらのプログラムでは、最深の再帰回数を3回とし、4回目にクリックしたら初期状態に戻るようにしました。環境によっては、3回再帰でも相当重くなるかと思われます。

あと今回は、試しに「p5.sound.js」を使って音を付けてみました。クリックしたときに「カチッ」と鳴るようになってます。今回のスケッチとは直接関係ないのですが、p5.sound.jsでBGMを流したい場合、AndroidとiOSとで、自動再生の処理が異なる(iOSの場合だけ?、一度何らかのユーザーイベントを取らないと再生が始まらない?)みたいですね。p5.jsでゲームっぽいものを作ろうと思うと、このあたりをどう回避、吸収するかも課題になりそうですねぇ。

今回のコードはこちらです。(要「p5.js」「p5.sound.js」)

let a = 0;
let b;
let sponge = new Array();
const recursiveLimit = 4;
let recursiveCount = 0;
let clickSe;

function setup() {
	clickSe = loadSound('./assets/clickse.mp3');
	createCanvas(600, 600, WEBGL);
	ambientLight(100);
	directionalLight(120, 120, 120, -1, -1, 1);
	ambientMaterial(230, 210, 255, 255);
	noStroke();
	resetCube();
}

function mousePressed() {
	clickSe.play();
	++recursiveCount;
	if (recursiveCount >= recursiveLimit) {
		resetCube();
		return;
	}

	let next = new Array();
	sponge.forEach(abox => {
		let newBoxes = abox.generate();
		next = concat(next, newBoxes);
	});
	sponge = next;
}

function draw() {
	camera(0, 0, 500, 0, 0, 0, 0, 1, 0);
	background(30, 30, 60);
	rotateX(a);
	rotateY(a * 0.4);
	rotateZ(a * 0.1);
	sponge.forEach(abox => { abox.show(); });
	a += 0.01;
}

function resetCube() {
	b = new Box(0, 0, 0, 300);
	sponge = [b];
	recursiveCount = 0;
}

class Box {
	constructor(x, y, z, r_) {
		this.pos = createVector(x, y, z);
		this.r = r_;
	}

	generate() {
		let boxes = new Array();
		for (let x = -1; x < 2; x++) {
			for (let y = -1; y < 2; y++) {
				for (let z = -1; z < 2; z++) {
					let sum = abs(x) + abs(y) + abs(z);
					const newR = this.r / 3;
					if (sum > 1) {
						let b = new Box(this.pos.x + x * newR, this.pos.y + y * newR, this.pos.z + z * newR, newR);
						boxes.push(b);
					}
				}
			}
		}
		return boxes;
	}

	show() {
		push();
		translate(this.pos.x, this.pos.y, this.pos.z);
		box(this.r);
		pop();
	}
}

さて、これまで何度かp5.jsのWebGLモードでスケッチしているのですが、最新のバージョンアップで一部機能変更があったりしているので、今回、メンガーのスポンジを描くにあたって、改めて基本的な使い方を復習してみました。そのあたりの簡単なまとめを含む3Dスケッチを追って作ってみます。