2020年07月04日

BufferGeometryで頂点アニメーション

BufferGeometryとシェーダーを使用していろいろ制作したいと思い、Three.jsのサンプルを参考に、頂点アニメーションを試してみました。※Three.jsはr117を使用しています。

BufferGeometryで頂点アニメーション

● BoxHelper

立方体のフレームを表示します。頂点アニメーションが綺麗に見えるように、各面の対角線が表示されないBoxHelperを使用します。

//立方体の辺の長さを設定
const r = 10;

const geometry = new THREE.BoxGeometry(r,r,r);
const material = new THREE.MeshBasicMaterial();
const box = new THREE.Mesh(geometry,material);

//BoxHelperを生成
const frameBox = new THREE.BoxHelper(box,0x111111);
scene.add(frameBox);

● 頂点の生成

Pointsを使用して頂点を生成します。

Geometryは毎フレームごとに頂点データをCPUからGPUに転送するのに対し、BufferGeometryは最初の1度だけGPUに転送しその後はGPUで処理するため、大量の頂点を操作する場合に効果を発揮します。

const r = 10;

//頂点数を設定
const particleNum = 2;
const pointMaterial = new THREE.PointsMaterial();

//型付配列で頂点座標を設定
const particlePositions = new Float32Array(particleNum * 3);

for(let i = 0; i < particleNum; i++){

	//頂点座標(x、y、z)を設定
	particlePositions[i*3] = Math.random() * r - r / 2.0;
	particlePositions[i*3+1] = Math.random() * r - r / 2.0;
	particlePositions[i*3+2] = Math.random() * r - r / 2.0;
}

//バッファーオブジェクトを生成
const particles = new THREE.BufferGeometry();

//バッファーオブジェクトのattributeに頂点座標を設定
particles.setAttribute('position',new THREE.BufferAttribute(particlePositions,3).setUsage(THREE.DynamicDrawUsage));

//頂点の生成
const pointCloud = new THREE.Points(particles,pointMaterial);
scene.add(pointCloud);

● 頂点のアニメーション

頂点座標に速度を加算して、頂点をアニメーションさせます。

const r = 10;
const particleNum = 2;

//最大速度の設定
const maxVelocity = 0.1;
let pointCloud;
const pointMaterial = new THREE.PointsMaterial();

//速度用の配列
const particleVelocity = [];
const particlePositions = new Float32Array(particleNum * 3);

for(let i = 0; i < particleNum; i++){
	particlePositions[i*3] = Math.random() * r - r / 2.0;
	particlePositions[i*3+1] = Math.random() * r - r / 2.0;
	particlePositions[i*3+2] = Math.random() * r - r / 2.0;

	//速度(x、y、z)を設定
	particleVelocity[i] = new THREE.Vector3();
	particleVelocity[i].x = -1 + Math.random() * 2.0;
	particleVelocity[i].y = -1 + Math.random() * 2.0;
	particleVelocity[i].z = -1 + Math.random() * 2.0;

	//速度の調整
	particleVelocity[i].multiplyScalar(maxVelocity / Math.sqrt(3.0));
}

const particles = new THREE.BufferGeometry();
particles.setAttribute('position',new THREE.BufferAttribute(particlePositions,3).setUsage(THREE.DynamicDrawUsage));
pointCloud = new THREE.Points(particles,pointMaterial);
scene.add(pointCloud);

function rendering(){
	requestAnimationFrame(rendering);

	const rHalf = r / 2.0;

	//頂点座標を取得
	const particlePositions = pointCloud.geometry.attributes.position.array;

	for(let i = 0; i < particleNum; i++){

		//頂点座標に速度を加算
		particlePositions[i*3] += particleVelocity[i].x;
		particlePositions[i*3+1] += particleVelocity[i].y;
		particlePositions[i*3+2] += particleVelocity[i].z;

		//立方体フレームの外側に移動した場合に速度を反転
		if(particlePositions[i*3] < -rHalf || particlePositions[i*3] > rHalf){
			particleVelocity[i].x *= -1;
		}
		if(particlePositions[i*3+1] < -rHalf || particlePositions[i*3+1] > rHalf){
			particleVelocity[i].y *= -1;
		}
		if(particlePositions[i*3+2] < -rHalf || particlePositions[i*3+2] > rHalf){
			particleVelocity[i].z *= -1;
		}
	}

	//更新を通知するフラグ
	pointCloud.geometry.attributes.position.needsUpdate = true;

	renderer.render(scene,camera);
}

● 線のアニメーション

LineSegmentsを使用して線を生成し、アニメーションさせます。

const r = 10;
const particleNum = 2;
const maxVelocity = 0.1;
let pointCloud;
const pointMaterial = new THREE.PointsMaterial();

const particleVelocity = [];
const particlePositions = new Float32Array(particleNum * 3);

for(let i = 0; i < particleNum; i++){
	particlePositions[i*3] = Math.random() * r - r / 2.0;
	particlePositions[i*3+1] = Math.random() * r - r / 2.0;
	particlePositions[i*3+2] = Math.random() * r - r / 2.0;

	particleVelocity[i] = new THREE.Vector3();
	particleVelocity[i].x = -1 + Math.random() * 2.0;
	particleVelocity[i].y = -1 + Math.random() * 2.0;
	particleVelocity[i].z = -1 + Math.random() * 2.0;
	particleVelocity[i].multiplyScalar(maxVelocity / Math.sqrt(3.0));
}

const particles = new THREE.BufferGeometry();
particles.setAttribute('position',new THREE.BufferAttribute(particlePositions,3).setUsage(THREE.DynamicDrawUsage));
pointCloud = new THREE.Points(particles,pointMaterial);
scene.add(pointCloud);

//線の頂点数の設定
const segments = particleNum * particleNum;

//型付配列で線の頂点座標を設定
const positions = new Float32Array(segments * 3);

const lineGeometry = new THREE.BufferGeometry();
lineGeometry.setAttribute('position',new THREE.BufferAttribute(positions,3).setUsage(THREE.DynamicDrawUsage));
const lineMaterial = new THREE.LineBasicMaterial({
	color:0xFFFFFF
});

//LineSegmentsで線を生成
lineMesh = new THREE.LineSegments(lineGeometry,lineMaterial);
scene.add(lineMesh);

function rendering(){
	requestAnimationFrame(rendering);

	const rHalf = r / 2.0;
	let vertexpos = 0;

	const particlePositions = pointCloud.geometry.attributes.position.array;

	for(let i = 0; i < particleNum; i++){
		particlePositions[i*3] += particleVelocity[i].x;
		particlePositions[i*3+1] += particleVelocity[i].y;
		particlePositions[i*3+2] += particleVelocity[i].z;

		if(particlePositions[i*3] < -rHalf || particlePositions[i*3] > rHalf){
			particleVelocity[i].x *= -1;
		}
		if(particlePositions[i*3+1] < -rHalf || particlePositions[i*3+1] > rHalf){
			particleVelocity[i].y *= -1;
		}
		if(particlePositions[i*3+2] < -rHalf || particlePositions[i*3+2] > rHalf){
			particleVelocity[i].z *= -1;
		}

		//線の頂点座標に速度を加算
		for(let j = i+1; j < particleNum; j++){
			let linePositions = lineMesh.geometry.attributes.position.array;
			linePositions[vertexpos++] = particlePositions[i*3];
			linePositions[vertexpos++] = particlePositions[i*3+1];
			linePositions[vertexpos++] = particlePositions[i*3+2];

			linePositions[vertexpos++] = particlePositions[j*3];
			linePositions[vertexpos++] = particlePositions[j*3+1];
			linePositions[vertexpos++] = particlePositions[j*3+2];
		}
	}

	//更新を通知するフラグ
	lineMesh.geometry.attributes.position.needsUpdate = true;
	pointCloud.geometry.attributes.position.needsUpdate = true;

	renderer.render(scene,camera);
}

完成したデモになります。Three.jsのサンプルを参考に、頂点数を増やし頂点間の距離によって線の描画色を調整しました。Three.jsの頂点アニメーションのデモなので、パソコンとスマホで見ることができるようにしました。

  • このエントリーをはてなブックマークに追加

関連記事

前の記事へ

Three.jsでシェーダ(GLSL)入門

次の記事へ

RawShaderMaterial