はじめに
three.jsを学ぶため、
オライリー本「初めてのThree.js」をまとめることで内容を咀嚼していこう思う。
前回
今回は第一章の最後まで。
初めてのThree.js 第2版
――WebGLのためのJavaScript 3Dライブラリ
github learning-three-js-2e-ja-support
はじめのまとめ
- code sample
- マテリアル ライト 影の追加
- code sample
- アニメーションの追加
- 統計情報の追加
code sample
↓
// once everything is loaded, we run our Three.js stuff.
function init() {
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 20);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
sphere.castShadow = true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-20, 30, -5);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
renderer.render(scene, camera);
}
window.onload = init;
以下、部分解説
マテリアル ライト 影の追加
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-20, 30, -5);
scene.add(spotLight);
光源をシーンに追加します。
THREE.SpotLight
は自分の位置(spotLight.position.set(-20, 30, -5);
)から光を照らします。
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 20);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 4;
sphere.position.z = 2;
マテリアルを前回のMeshBasicMaterial
からMeshLambertMaterial
に変更しています。
このマテリアルとMeshPhongMaterial
MeshStandardMaterial
が光源を計算に含めるものです。
次に影を追加します。
Three.jsでは影の描写はデフォルトでは無効化されています。
有効化のためには複数箇所コードを追加します。
renderer.setClearColor(new THREE.Color(0xEEEEEE));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
上記はrenderer
に影の使用を伝えています。
次に影を使用するオブジェクトのプロパティを設定します。
plane.receiveShadow = true;
...
cube.castShadow = true;
...
sphere.castShadow = true;
最後にspotLight
のcastShadow
プロパティを設定します。
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-20, 30, -5);
spotLight.castShadow = true; // ←このコード
scene.add(spotLight);
統計情報・アニメーションの追加
code sample
↓
// once everything is loaded, we run our Three.js stuff.
function init() {
var stats = initStats();
// create a scene, that will hold all our elements such as objects, cameras and lights.
var scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
var renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xEEEEEE));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// create the ground plane
var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.receiveShadow = true;
// rotate and position the plane
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 15;
plane.position.y = 0;
plane.position.z = 0;
// add the plane to the scene
scene.add(plane);
// create a cube
var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff0000});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
// position the cube
cube.position.x = -4;
cube.position.y = 3;
cube.position.z = 0;
// add the cube to the scene
scene.add(cube);
var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
// position the sphere
sphere.position.x = 20;
sphere.position.y = 0;
sphere.position.z = 2;
sphere.castShadow = true;
// add the sphere to the scene
scene.add(sphere);
// position and point the camera to the center of the scene
camera.position.x = -30;
camera.position.y = 40;
camera.position.z = 30;
camera.lookAt(scene.position);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x0c0c0c);
scene.add(ambientLight);
// add spotlight for the shadows
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-20, 30, -5);
spotLight.castShadow = true;
scene.add(spotLight);
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// call the render function
var step = 0;
renderScene();
function renderScene() {
stats.update();
// rotate the cube around its axes
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
// bounce the sphere up and down
step += 0.04;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// render using requestAnimationFrame
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
}
window.onload = init;
以下、部分解説
-
requestAnimationFrame
関数
ブラウザによって定義された感覚で呼び出される関数が設定でき、必要なものをその関数内で自由に描画できる。利用するにはレンダリングを処理する関数を作成して、引数として渡すだけ。
function renderScene() {
// render using requestAnimationFrame
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
renderScene関数内で を再び呼び出してアニメーションが実行され続けるようにしています。
シーンを完全に作成し終わったあとで、renderer.renderを呼び出す代わりに一度renderScene関数を呼び出してアニメーションを開始します。
// add the output of the renderer to the html element
document.getElementById("WebGL-output").appendChild(renderer.domElement);
// render the scene ←こっちではなくて
renderer.render(scene, camera);
// call the render function ←こっち
var step = 0;
renderScene();
統計情報の追加
アニメーションが実行されるフレームフレートに関する情報を表示してくれるヘルパーライブラリーを追加します。
<script type="text/javascript" src="../libs/stats.js"></script>
<div id="Stats-output"></div>
統計情報を初期化して<div>
に追加
function initStats() {
var stats = new Stats();
stats.setMode(0); // 0: fps, 1: ms
// Align top-left
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
下記で呼び出し
var stats = initStats();
新しいレンダリングサイクルに入ったことをstatsオブジェクトに通知する。
function renderScene() {
stats.update();
...
// render using requestAnimationFrame
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
上記のようにstats.update関数
をrenderScene関数内
で呼び出すことで、新しいレンダリングサイクル入ったことをstatsオブジェクトに通知できるようになる。
これによって左上に統計情報が表示されるようになる。
アニメーションの追加
- 立方体を回転させる
function renderScene(){
...
cube.rotation.x += 0.02;
cube.rotation.y += 0.02;
cube.rotation.z += 0.02;
...
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
renderScene関数
が呼ばれるたびにrotationプロパティのそれぞれの軸の値を0.02ずつ増やすと、立方体がすべての軸に対してなめらかに回転します。
- ボールを移動する
function renderScene() {
stats.update();
...
// bounce the sphere up and down
step += 0.04;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
// render using requestAnimationFrame
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
Math.cos関数
とMath.sin関数
を使用すればstep変数を元に軌跡を作成できる。
step += 0.04
によって弾む球の速さを定義できる。
- dat.GUIを導入する
<!-- head 内で読み込む -->
<script type="text/javascript" src="../libs/dat.gui.js"></script>
//プロパティとデフォルト値を設定
var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
};
//オブジェクトを作成したdat.GUIに渡して2つのプロパティの範囲を設定する
var gui = new dat.GUI();
gui.add(controls, 'rotationSpeed', 0, 0.5);
gui.add(controls, 'bouncingSpeed', 0, 0.5);
//renderSceneループの中で2つのプロパティを直接参照して、datGUIのUIで値を変更したらすぐに反映される様にする
...
// rotate the cube around its axes
cube.rotation.x += controls.rotationSpeed;
cube.rotation.y += controls.rotationSpeed;
cube.rotation.z += controls.rotationSpeed;
// bounce the sphere up and down
step += controls.bouncingSpeed;
sphere.position.x = 20 + ( 10 * (Math.cos(step)));
sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));
...
dat.GUIというライブラリを使用するとコード内の変数の値を変更するための単純なUIコンポーネントを簡単に作成できます。
- ブラウザサイズが変更されたら出力を自動的にリサイズする
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// イベントリスナーの登録
window.addEventListener('resize', onResize, false);
最後にcamera
scene
renderer
の変数定義をinit()関数の外に移動して別の関数からもアクセスできるようにする。
var camera;
var scene;
var renderer;
function init(){
...
// create a scene, that will hold all our elements such as objects, cameras and lights.
scene = new THREE.Scene();
// create a camera, which defines where we're looking at.
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// create a render and set the size
renderer = new THREE.WebGLRenderer();
...
}
まとめ
Three.jsでは、はじめにThree.Sceneオブジェクトを作成する必要があり、それからシーンオブジェクトにカメラ、ライト、描画したいオブジェクトを追加します。
さらに基本的なシーンを拡張して影とアニメーションを追加します。必要があれば、ヘルパーライブラリを追加します。
以上第1章のまとめになります。長くなりましたね。
長文最後までありがとうございました。