Twitter
2020年12月25日 - Three.js・WebVR

Three.jsのPointsで頂点アニメーション

three.js blending test」を参考に、Three.jsのPointsで頂点アニメーションを試してみました。※Three.jsはr124を使用しています。

球体上の頂点座標

Three.jsで螺旋アニメーション」での円周上の頂点座標を求めましたが、球体上の頂点座標を求めます。円周上の座標は、半径(r)と角度(θ)を使用して求めましたが、球体上の座標は、半径(r)と仰角(ぎょうかく)(θ)と方位角(θ')を使用して求めます。

// 仰角
const radian1 = degree * Math.PI / 180;
// 方位角
const radian2 = degree * Math.PI / 180;

//X座標
const x = Math.cos(radian1) * Math.cos(radian2) * r;
//Y座標
const y = Math.sin(radian1) * r;
//Z座標
const z = Math.cos(radian1) * Math.sin(radian2) * r;

Three.jsのPointsで頂点アニメーション

● 頂点を球体上に配置

頂点を球体上に配置します。

Geometryは形状が空なので、球体上に配置した頂点座標を設定し、THREE.Pointsでメッシュを生成して配置します。頂点座標は管理しやすいように、管理用のクラスを作成します。

const phisicsArr = [];

const geometry = new THREE.Geometry();
const material = new THREE.PointsMaterial({
	color:0x47656d,
	size:6,
	transparent:true,
	opacity:0.5,
	blending:THREE.AdditiveBlending,
	depthTest:false
});

for(let i = 0; i < 120; i++){
	for(let j = 0; j < 120; j++){

		//頂点座標のインスタンスを生成
		const phisics = new Phisics();

		//仰角と方位角のラジアンを設定
		const rad1 = (i * 3) * Math.PI / 180;
		const rad2 = (j * 3) * Math.PI / 180;
		phisics.init(rad1,rad2);

		//Geometryの頂点座標配列に追加
		geometry.vertices.push(phisics.position);

		phisicsArr.push(phisics);
	}
}
//頂点の生成
const points = new THREE.Points(geometry,material);
scene.add(points);

//頂点座標の管理用クラス
class Phisics{
	constructor(){

		//頂点座標
		this.position = new THREE.Vector3();

		//半径
		this.radius = 300;

		//仰角
		this.rad1 = 0;
		//方位角
		this.rad2 = 0;
	}

	init(rad1,rad2){
		this.rad1 = rad1;
		this.rad2 = rad2;
		this.setPosition();
	}

	setPosition(rad1,rad2){

		//頂点座標を球体上に設定
		const x = Math.cos(this.rad1 ) * Math.cos(this.rad2) * this.radius;
		const y = Math.sin(this.rad1 ) * this.radius;
		const z = Math.cos(this.rad1 ) * Math.sin(this.rad2) * this.radius;
		this.position.set(x,y,z);
	}
}

● テクスチャの設定

THREE.PointsMaterialはテクスチャ画像を設定できます。テクスチャを設定しないと、正方形の頂点になるので、円を少しぼかして背景を透過したテクスチャ画像を設定します。

//テクスチャ画像の読み込み
const texture = new THREE.TextureLoader().load('./img/point.png');

const material = new THREE.PointsMaterial({

	//テクスチャを設定
	map:texture,
	color:0x47656d,
	size:6,
	transparent:true,
	opacity:0.5,
	blending:THREE.AdditiveBlending,
	depthTest:false
});

● 頂点のアニメーション

頂点をアニメーションさせます。方位角のラジアンの値を更新すると、頂点が球体にそってアニメーションします。

function rendering(){
	requestAnimationFrame(rendering);

	for(let i = 0; i < phisicsArr.length; i++){

		//頂点座標のインスタンスを取得
		const phisics = phisicsArr[i];

		//アニメーション
		phisics.updata();
	}
	points.geometry.verticesNeedUpdate = true;

	renderer.render(scene,camera);
}

class Phisics{
	constructor(){
		this.position = new THREE.Vector3();
		this.radius = 300;
		this.rad1 = 0;
		this.rad2 = 0;
	}

	init(rad1,rad2){
		this.rad1 = rad1;
		this.rad2 = rad2;
		this.setPosition();
	}

	setPosition(rad1,rad2){
		const x = Math.cos(this.rad1 ) * Math.cos(this.rad2) * this.radius;
		const y = Math.sin(this.rad1 ) * this.radius;
		const z = Math.cos(this.rad1 ) * Math.sin(this.rad2) * this.radius;
		this.position.set(x,y,z);
	}

	//アニメーション
	updata(){

		//方位角のラジアンの値を更新
		this.rad2 += 0.1 * Math.PI / 180;
		this.setPosition();
	}
}

● script.js

必要なライブラリを読み込みます。

<script src="js/preloadjs.min.js"></script>
<script src="js/TweenMax.min.js"></script>
<script src="js/script.js" type="module"></script>

完成したscript.jsになります。

//===============================================================
// Import Library
//===============================================================
import * as THREE from './lib/three_jsm/three.module.js';
import { OrbitControls } from './lib/three_jsm/OrbitControls.js';
import { scene, camera, container, renderer } from './lib/basescene.js';

//===============================================================
// Init
//===============================================================
window.addEventListener('load',function(){
   init();
});

let orbitControls;
let value = 0;
let points;
const phisicsArr = [];

function init(){
	setLoading();
}

function setLoading(){
	TweenMax.to('.loader',0.1,{opacity:1});
	TweenMax.to('#loader_wrapper',1,{
        opacity:0,
        delay:0,
        onComplete: function(){
            document.getElementById('loader_wrapper').style.display = 'none';
            TweenMax.to('.loader',0,{opacity:0});
        }
    });
	threeWorld();
	setLight();
	setControll();
	rendering();
}

//===============================================================
// Create World
//===============================================================
function threeWorld(){
	renderer.outputEncoding = THREE.sRGBEncoding;

	const axesHelper = new THREE.AxesHelper(500);
	const gridHelper = new THREE.GridHelper(1000,50);
	//scene.add(axesHelper);
	//scene.add(gridHelper);

	const geometry = new THREE.Geometry();
	const texture = new THREE.TextureLoader().load('./img/point.png');
	const material = new THREE.PointsMaterial({
		map:texture,
		color:0x47656d,
		size:6,
		transparent:true,
		opacity:0.5,
		blending:THREE.AdditiveBlending,
		depthTest:false
	});

	for(let i = 0; i < 120; i++){
		for(let j = 0; j < 120; j++){
			const phisics = new Phisics();
			const rad1 = (i * 3) * Math.PI / 180;
			const rad2 = (j * 3) * Math.PI / 180;
			phisics.init(rad1,rad2);

			geometry.vertices.push(phisics.position);
			phisicsArr.push(phisics);
		}
	}
	points = new THREE.Points(geometry,material);
	scene.add(points);
}

function setLight(){
	const ambientlight = new THREE.AmbientLight(0xFFFFFF,1);
	scene.add(ambientlight);
}

function setControll(){
	document.addEventListener('touchmove',function(e){e.preventDefault();},{passive:false});
	orbitControls = new OrbitControls(camera,renderer.domElement);
	orbitControls.enableDamping = true;
	orbitControls.dampingFactor = 0.5;
}

function rendering(){
	requestAnimationFrame(rendering);

	for(let i = 0; i < phisicsArr.length; i++){
		const phisics = phisicsArr[i];
		phisics.updata();
	}
	points.geometry.verticesNeedUpdate = true;

	value += 0.0025;
	points.material.opacity = Math.min(Math.max(Math.abs(Math.cos(value)),0.1),0.65);

	if(orbitControls){
		orbitControls.update();
	}
	renderer.render(scene,camera);
}

class Phisics{
	constructor(){
		this.position = new THREE.Vector3();
		this.radius = 300;
		this.rad1 = 0;
		this.rad2 = 0;
	}

	init(rad1,rad2){
		this.rad1 = rad1;
		this.rad2 = rad2;
		this.setPosition();
	}

	setPosition(rad1,rad2){
		const x = Math.cos(this.rad1 ) * Math.cos(this.rad2) * this.radius;
		const y = Math.sin(this.rad1 ) * this.radius;
		const z = Math.cos(this.rad1 ) * Math.sin(this.rad2) * this.radius;
		this.position.set(x,y,z);
	}

	updata(){
		this.rad1 += 0.05 * Math.PI / 180;
		this.rad2 += 0.1 * Math.PI / 180;
		this.radius = Math.cos(this.rad1) * 300;
		this.setPosition();
	}
}

● basescene.js

sceneやcameraなど基本的な設定を管理するbasescene.jsです。

//===============================================================
// Import Library
//===============================================================
import * as THREE from './three_jsm/three.module.js';

//===============================================================
// Base scene
//===============================================================
let scene,camera,container,renderer;

init();

function init(){
	scene = new THREE.Scene();
	camera = new THREE.PerspectiveCamera(50,window.innerWidth/window.innerHeight,1,5000);
	camera.position.set(600,600,600);
	scene.add(camera);

	renderer = new THREE.WebGLRenderer({antialias:true});
	renderer.setPixelRatio(window.devicePixelRatio);
	renderer.setSize(window.innerWidth,window.innerHeight);

	container = document.querySelector('#canvas_vr');
	container.appendChild(renderer.domElement);

	window.addEventListener('resize',function(){
		camera.aspect = window.innerWidth/window.innerHeight;
		camera.updateProjectionMatrix();
		renderer.setSize(window.innerWidth,window.innerHeight);
	},false);
}

export { scene, camera, container, renderer }

完成したデモになります。仰角と方位角のラジアンや半径の値、マテリアルの透明度を更新してアニメーションさせました。

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

関連記事

前の記事へ

Three.jsで螺旋アニメーション

次の記事へ

Three.jsでカメラアニメーション