【Three.js】基礎編
はじめに
前回に引き続き、Three.jsについての記事になります。導入からごく簡単な図形を描画することはできたので、今回は新しい要素を加えていきたいと思います。
完成見本とサンプルコード
今回の完成見本はこちらになります。
以下のコードをHTMLファイルに貼り付けると、動画のように複数の板がグループとなって回転している様子が確認できると思います。
<html lang="ja">
<head>
<meta charset="utf-8" />
<script src="https://unpkg.com/three@0.131.3/build/three.min.js"></script>
<style>
body {
margin: 0;
}
</style>
<script>
// ページの読み込みを待つ
window.addEventListener('load', init);
function init() {
// サイズを指定
const width = 960;
const height = 540;
// canvas要素の参照を取得する
const canvas = document.querySelector('#myCanvas');
// レンダラーを作成
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
// シーンを作成
const scene = new THREE.Scene();
// カメラを作成
const camera = new THREE.PerspectiveCamera(45, width / height);
camera.position.set(0, 0, +1100);
//オブジェクトをグループに格納し、グループをシーンに追加
group = new THREE.Group();
// ジオメトリーとマテリアルを定義
const geometry = new THREE.BoxBufferGeometry(20,1,20);
const material = new THREE.MeshStandardMaterial({
color: 0xE1EDED,
flatShading: true
});
// オブジェクトを100個生成
for ( let i = 0; i < 200; i ++ ) {
const mesh = new THREE.Mesh( geometry, material );
// 座標をランダムで決定
mesh.position.x = Math.random() * 500 - 250;
mesh.position.y = Math.random() * 500 - 250;
mesh.position.z = Math.random() * 500 - 250;
// サイズをランダムで決定
mesh.scale.setScalar( Math.random() * 2 + 1 );
// 角度をランダムで生成
mesh.rotation.x = Math.random() * Math.PI;
mesh.rotation.y = Math.random() * Math.PI;
mesh.rotation.z = Math.random() * Math.PI;
group.add(mesh);
}
scene.add(group);
//半球光源の生成
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 1000, 0 );
scene.add( hemiLight );
// 平行光源の生成
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
dirLight.position.set( - 3000, 1000, - 1000 );
scene.add( dirLight );
// フォグの追加
scene.fog = new THREE.Fog(0xffffff, 50, 2000); //(色, 開始距離, 終点距離)
// クロック(時間)の生成
clock = new THREE.Clock();
// アニメーション
tick();
function tick() {
requestAnimationFrame(tick);
group.rotation.y += clock.getDelta() * 0.08;
renderer.render(scene, camera);
}
// リサイズ処理
onResize();
window.addEventListener('resize',onResize);
function onResize() {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width,height);
camera.aspect = width/height;
camera.updateProjectionMatrix();
}
}
</script>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
こちらのコードについて、工程ごとに解説していきます。
環境準備
まずは下準備をします。ページの読み込みを待ってから、canvas要素の準備・レンダラーの準備を行います。
// サイズを指定
const width = 960;
const height = 540;
// canvas要素の参照を取得する
const canvas = document.querySelector('#myCanvas');
// レンダラーを作成
const renderer = new THREE.WebGLRenderer({
canvas: canvas,
alpha: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
続けて、シーンとカメラも用意しましょう。
// シーンを作成
const scene = new THREE.Scene();
// カメラを作成
const camera = new THREE.PerspectiveCamera(45, width / height);
camera.position.set(0, 0, +1100);
ここまでのコードで、3Dオブジェクトを生成・描画するための空間を構築することができました。
次の項目から、新しい要素が加わってきます。
オブジェクトの生成
前回と大きく異なる要素として、「描画するオブジェクトの数」が挙げられると思います。今回は200個ほどのオブジェクトを生成し、それらの座標・サイズ・角度をランダムに設定して配置します。最終的に、「複数のオブジェクトを一つのグループとして扱い、そのグループに対して回転アニメーションを設定する」という流れになります。
//オブジェクトをグループに格納し、グループをシーンに追加
group = new THREE.Group();
// ジオメトリーとマテリアルを定義
const geometry = new THREE.BoxBufferGeometry(20,1,20);
const material = new THREE.MeshStandardMaterial({
color: 0xE1EDED,
flatShading: true
});
// オブジェクトを200個生成
for ( let i = 0; i < 200; i ++ ) {
const mesh = new THREE.Mesh( geometry, material );
// 座標をランダムで決定
mesh.position.x = Math.random() * 500 - 250;
mesh.position.y = Math.random() * 500 - 250;
mesh.position.z = Math.random() * 500 - 250;
// サイズをランダムで決定
mesh.scale.setScalar( Math.random() * 2 + 1 );
// 角度をランダムで生成
mesh.rotation.x = Math.random() * Math.PI;
mesh.rotation.y = Math.random() * Math.PI;
mesh.rotation.z = Math.random() * Math.PI;
group.add(mesh);
}
scene.add(group);
200個のオブジェクトはそれぞれ独立して扱うのではなく一つのグループとして取り扱いますので、「THREE.Groupクラス」を用いて変数groupを用意します。
その後は通常のオブジェクトと同様、ジオメトリ・マテリアルを定義します。「BoxBufferGeometry」で薄い直方体、「MeshStandardMaterial」で均一な塗りつぶしという風に設定しました。
for文を用いて、オブジェクトを200個作成します。先に用意したジオメトリ・マテリアルの情報でメッシュを作成したら、そのメッシュの座標・サイズ・角度をランダムに設定します。座標については、0以上1未満の数をランダムで生成してくれる「Math.random」を用いると良いでしょう。角度を定義する際には「Math.PI」で円周率を取得・使用しましょう。「add()」を用いて、生成したメッシュをグループに追加します。
最終的に、200個のメッシュが格納されたグループをシーンに追加することで、画面上に描画されるようになります。
光源の設置
前回にはなかった要素として、「光源」を追加してみたいと思います。光源を設置することで物体に光が当たり、オブジェクトの質感もわかりやすくなります。今回は、特定の方向に放射される「平行光源」と3D空間全体に光を当ててくれる「半球光源」を設置します。
//半球光源の生成 new THREE.DirectionalLight(色, 光の強さ)
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 1000, 0 );
scene.add( hemiLight );
// 平行光源の生成 new THREE.HemisphereLight(空の色, 地の色, 光の強さ)
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
dirLight.position.set( - 3000, 1000, - 1000 );
scene.add( dirLight );
フォグの追加
さらに新しく「フォグ」も付け足しましょう。フォグ(=fog)とは霧・濃霧という意味で、遠くのものが霞んで見えるような効果を加えることができます。これにより、空間の奥行きを演出することができます。
// フォグの追加
scene.fog = new THREE.Fog(0xffffff, 50, 2000); //(色, 開始距離, 終点距離)
アニメーションの設定
最後にアニメーションの設定を行います。「オブジェクトのグループごと回転させる」ことは先に述べましたが、今回は「異なるブラウザのサイズでも3Dを全画面に表示する」というリサイズ処理の仕組みも実装したいと思います。
まずはアニメーションからですが、「時間経過で関数を呼び続ける」「表示結果を最新のものに更新する」という基本的な流れは前回のものと変わりません。異なるのは回転の挙動を「clock.getDelta()」で制御していることです。「* 0.1」の数値の部分を変更すると、回転のスピードを制御することができます。
// クロック(時間)の生成
clock = new THREE.Clock();
// アニメーション
tick();
function tick() {
requestAnimationFrame(tick); //関数を呼び続ける
group.rotation.y += clock.getDelta() * 0.1; //回転
renderer.render(scene, camera); 表示結果を更新
}
続けてリサイズ処理についてです。リサイズ時に「renderer.setSize」でレンダラーのサイズを画面幅に合わせること、「camera.aspect」でカメラの縦横比を正しく調整することがポイントです。
// リサイズ処理
onResize();
window.addEventListener('resize',onResize); //リサイズイベント発生時に実行
function onResize() {
// サイズ(画面の幅・高さ)を取得
const width = window.innerWidth;
const height = window.innerHeight;
// レンダラーのサイズを調整する
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width,height);
// カメラのアスペクト比を正す
camera.aspect = width/height;
camera.updateProjectionMatrix();
}
以上で完成です!