球体の頂点アニメーション
Three.jsでいろいろと制作できるようになりたいと思い、「Learning 3D Graphics With Three.js | Dynamic Geometry」を参考に、球体の頂点アニメーションを試してみました。
※Three.jsの仕様が変更になったため内容を修正しました。Three.jsはr126を使用しています。(2021年3月27日)
球体の頂点アニメーション
● 球体を生成
まずは、SphereGeometryを使用して球体を生成します。動作確認のため、マテリアルはライティングを必要としないMeshNormalMaterialを使用します。
const geometry = new THREE.SphereGeometry(3,128,128); const material = new THREE.MeshNormalMaterial(); const sphere = new THREE.Mesh(geometry,material); scene.add(sphere);
● 球体の頂点を取得
球体の頂点を取得します。Three.jsのr125からGeometryが廃止され、SphereGeometryはBufferGeometryを継承するようになったため、BufferGeometryと同じ要領で球体の頂点を取得します。
const positions = sphere.geometry.attributes.position.array; for(let i = 0; i < positions.length; i++){ }
● 球体の頂点を操作
取得した頂点を単位ベクトルにし、ランダムな値を積算して球体を凸凹にします。
ベクトルは方向と長さの情報を持ち、長さが1のベクトルのことを単位ベクトルといいます。取得した頂点をnormalizeで正規化し、単位ベクトルにします。
「perlin.js」はパーリンノイズを生成するライブラリで、noise.perlin3(x,y,z)でノイズを生成します。単位ベクトルに対し、multiplyScalarでランダムな値(パーリンノイズ)を積算して球体を凸凹にします。
const positions = sphere.geometry.attributes.position.array; for(let i = 0; i < positions.length; i++){ const p = new THREE.Vector3( positions[i*3], positions[i*3+1], positions[i*3+2] ); p.normalize().multiplyScalar(r + 0.3 * noise.perlin3(p.x * k + time, p.y * k, p.z * k)); positions[i*3] = p.x positions[i*3+1] = p.y positions[i*3+2] = p.z; }
頂点座標の更新を通知して、レンダリングします。
sphere.geometry.attributes.position.needsUpdate = true;
法線は面に対して垂直に伸びる線のことで、光の反射などに影響します。法線を設定すると、影ができて立体的になります。
法線を設定するため、更新を通知します。
sphere.geometry.computeVertexNormals();
● 頂点アニメーション
球体の頂点をアニメーションさせます。performance.nowでタイムスタンプを取得し、パーリンノイズの値に加算してアニメーションさせます。
let sphere; const geometry = new THREE.SphereBufferGeometry(3,128,128); const material = new THREE.MeshNormalMaterial(); sphere = new THREE.Mesh(geometry,material); scene.add(sphere); function rendering(){ requestAnimationFrame(rendering); //タイムスタンプの取得 const time = performance.now() * 0.001; const positions = sphere.geometry.attributes.position.array; //球体の半径 const r = 3; //ノイズのサイズ調整 const k = 2; for(let i = 0; i < positions.length; i++){ const p = new THREE.Vector3( positions[i*3], positions[i*3+1], positions[i*3+2] ); p.normalize().multiplyScalar(r + 0.3 * noise.perlin3(p.x * k + time, p.y * k, p.z * k)); positions[i*3] = p.x positions[i*3+1] = p.y positions[i*3+2] = p.z; } sphere.geometry.attributes.position.needsUpdate = true; sphere.geometry.computeVertexNormals(); renderer.render(scene,camera); }
完成したデモになります。マテリアルはMeshNormalMaterialからMeshPhysicalMaterialに変更し、PointLightを使用してライティングを調整しました。