こんにちは! はっちゃんです。 three.jsの基本をおさらいしてみよう!〜基礎の基礎編〜
今回は、「three.jsの基本をおさらいしてみよう!〜基礎の基礎編〜」の続きになります。
前回生成した物体に、動きやテクスチャを加えて、立体がより分かりやすくなるようにしてみます。
ヘルパーを用意
まずは、物体が今どんな状態になっているのかを確認するため、ヘルパーを追加してみましょう。
- GridHelper
- AxisHelper
- LightHelper
- ヘルパーを示す基本的な関数
- GridHelper・・・平面にグリッドを表示する
new THREE.GridHelper(全体のサイズ, 1グリッドのサイズ)
AxisHelper・・・x,y,x軸を表示する
new THREE.AxisHelper(サイズ)
LightHelper・・・光源を表示する
new THREE.DirectionalLightHelper(光源の変数, サイズ)
new THREE.PointLightHelper(光源の変数, サイズ)
(function() {
var scene;
var sphere;
var camera;
var light;
var gridHelper; // 追加
var axisHelper; // 追加
var lightHelper; // 追加
var renderer;
var width = 640;
var height = 330;
// ステージを作る
scene = new THREE.Scene();
// 球体を作る
sphere = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({color: 0x8dc3ff}) // 材質、色
);
sphere.position.set(0, 0, 0);
scene.add(sphere);
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20); // 追加
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000); // 追加
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20); // 追加
scene.add(lightHelper);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xeeeeee);
document.getElementById('stage').appendChild(renderer.domElement);
renderer.render(scene, camera);
})();
表示は以下のようになります。
See the Pen three.js helper by k_hatsushi (@hatsushi_kazuya) on CodePen.
物体の状態がわかりやすくなりました。
物体にテクスチャを貼り付ける
次に、物体の表面にテクスチャを貼り付けてみましょう。
今回はデザイナーのヴィクトリアにお願いして、地球の平面画像を作ってもらいました。サイズは 2:1 の比率で作ります。
TextureLoader でテクスチャーを読み込む準備をして、load関数でパスを指定します。
第二引数の function の引数に、テクスチャーのオブジェクトが渡ってきます。「テクスチャーを読み込む」と「テクスチャーを貼り付ける」を別の処理に分けたいので、createEarth という名前で関数を作り、load 完了後に実行します。
また、テクスチャーの準備が整ってからレンダリングする必要があるので、render 関数を用意して、createBox 実行後にレンダリングします。
(function() {
var scene;
var sphereEarth;
var camera;
var light;
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var width = 640;
var height = 330;
var loader;
// ステージを作る
scene = new THREE.Scene();
// 追加:地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/earth.jpg', function(texture) {
createEarth(texture);
render();
});
// 追加:地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20);
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20);
scene.add(lightHelper);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js texture1 by k_hatsushi (@hatsushi_kazuya) on CodePen.
あっさり地球ができてしまいました。
ここから細かい調整をしていきましょう。
環境光源を加える
見てみると、ちょっと地球が暗いですね。
光が当たっている部分が狭く、影の領域が広くなってしまっています。
そこで、前回の「three.jsの基本をおさらいしてみよう!〜基礎の基礎編〜」で紹介した、環境光源を使用します。
(function() {
var scene;
var sphereEarth;
var camera;
var light;
var ambient; // 追加
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var width = 640;
var height = 330;
var loader;
// ステージを作る
scene = new THREE.Scene();
// 地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/earth.jpg', function(texture) {
createEarth(texture);
render();
});
// 地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// 追加:環境光源を作る
ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20);
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20);
scene.add(lightHelper);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js texture2 by k_hatsushi (@hatsushi_kazuya) on CodePen.
環境光のおかげで、全体に光が行き渡るようになりました。
物体を回転させる
このまま静止した地球を見ていてもつまらないですね。では、動きを加えてみましょう。
render 関数の中で、sphereEarth.rotation.y の値を更新し続けることで、物体を回転させることができます。
また、requestAnimationFrame で自分自身を呼び出し続けることで、効率よくレンダリングを繰り返すことができます。
▼requestAnimationFrameの詳しい記事 アニメーションを実装するなら知っておきたい「requestAnimationFrame」の使い方
(function() {
var scene;
var sphereEarth;
var camera;
var light;
var ambient;
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var loader;
var width = 640;
var height = 330;
// ステージを作る
scene = new THREE.Scene();
// 地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/earth.jpg', function(texture) {
createEarth(texture);
render();
});
// 地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// 環境光源を作る
ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20);
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20);
scene.add(lightHelper);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
requestAnimationFrame(render);
sphereEarth.rotation.y += 0.01; // 追加
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js rotation mesh by k_hatsushi (@hatsushi_kazuya) on CodePen.
自転するようになりました。
より地球っぽくなってきましたね!
カメラを回転させる
物体が動かせたなら、カメラも動かせるのでは? と発想した方、その通りです!
ただ、その場合、カメラが自転してもしようがないので、地球を中心にカメラを公転させたいと思います。円状に動かすので、サイン、コサインを使用します。
今回、theta という変数を作り、カメラの x 座標にコサインの値を、z 座標にサインの値を渡します。値を求めるには、Math.cos 関数と Math.sin 関数にラジアンの値を渡す必要があります。ラジアンは、THREE.Math.degToRad クラスに角度の度数を渡すことで求めることができます。
THREE.Math.degToRad(度数)
▼円運動の詳しい記事はこちら
http://muumv.com/circle/
自転と同じで、レンダリングするたびに値を更新し続けます。
(function() {
var scene;
var sphereEarth;
var camera;
var light;
var ambient;
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var loader;
var width = 640;
var height = 330;
var theta = 0; // 追加
// ステージを作る
scene = new THREE.Scene();
// 地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/earth.jpg',
function(texture) {
createEarth(texture);
render();
});
// 地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// 環境光源を作る
ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20);
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20);
scene.add(lightHelper);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
requestAnimationFrame(render);
theta += 0.1; // 追加
camera.position.x = Math.cos(THREE.Math.degToRad(theta)) * 300; // 追加
camera.position.z = Math.sin(THREE.Math.degToRad(theta)) * 300; // 追加
camera.lookAt(scene.position); // 追加
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js rotation camera by k_hatsushi (@hatsushi_kazuya) on CodePen.
カメラが地球を中心に公転する動きができました。
マウスやタッチパッドで動かせるようにする
自動で動かせるようになったので、今度は手動で動かせるようにしようと思います。
マウスやタッチパネルで操作できるようにするには、新たに OrbitControls.js を読み込ませる必要があります。
OrbitControlsクラスに、操作したいオブジェクトを渡し、render関数の中で更新し続けます。
<div id="stage"></div>
<script src="https://82mou.github.io/threejs/js/three.js"></script>
<script src="https://82mou.github.io/threejs/js/OrbitControls.js"></script> <!-- 追加 -->
(function() {
var scene;
var sphereEarth;
var camera;
var light;
var ambient;
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var loader;
var width = 640;
var height = 330;
var controls; // 追加
// ステージを作る
scene = new THREE.Scene();
// 地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/earth.jpg', function(texture) {
createEarth(texture);
render();
});
// 地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 平方光源を作る
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(100, 130, 80);
scene.add(light);
// 環境光源を作る
ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
gridHelper = new THREE.GridHelper(200, 20);
scene.add(gridHelper);
axisHelper = new THREE.AxisHelper(1000);
scene.add(axisHelper);
lightHelper = new THREE.DirectionalLightHelper(light, 20);
scene.add(lightHelper);
// 追加:controls
controls = new THREE.OrbitControls(camera);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
requestAnimationFrame(render);
controls.update(); // 追加
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js animation by k_hatsushi (@hatsushi_kazuya) on CodePen.
ほとんど追記なく、画面を自由に動かすことができるようになりました。
three.jsはとても使いやすいです。
タッチパッドを使用している場合、2本指で拡大・縮小することもできます。
まとめ:ちょっとリアルな地球を作る
ここまで来たら、ヘルパーが邪魔なので、一旦コメントアウトしてしまいましょう。
See the Pen three.js2-7 animation no helper by k_hatsushi (@hatsushi_kazuya) on CodePen.
どこからどう見ても地球ですね!
しかし、このままでは「ふぅん……で?」って感じなので、地球をもう少しリアルにしてみましょう。
地球の外側に、地球と同じ要領で2つ球体を作成します。ひとつは雲、もうひとつは宇宙として使用します。
- 雲・・・地球より若干大きめに作って、雲のテクスチャーを透過で貼り付けます。雲の隙間を透過させる必要があるので、THREE.MeshLambertMaterial のオプションに transparent:true を設定します。
- 宇宙・・・かなり大きいサイズにして、宇宙のテクスチャーを貼り付けます。テクスチャーは円の内側から見えなければいけないので、THREE.MeshLambertMaterial のオプションに side:THREE.DoubleSide を設定します。
最後に光源を平方光源から点光源(PointLight)に変更して、地球と雲を時差をつけて自転させれば、ちょっとリアルな宇宙と地球の完成です!
宇宙と雲のテクスチャーは自分のリポジトリに上がっていますので、よろしければご自由にお使いください。
リアルな地球のテクスチャーはこちらで落とせます。
http://earthobservatory.nasa.gov/Features/BlueMarble/BlueMarble_2002.php
(function() {
var scene;
var sphereEarth; // 追加
var sphereCrowd; // 追加
var sphereUniverse; // 追加
var camera;
var light;
var ambient;
var gridHelper;
var axisHelper;
var lightHelper;
var renderer;
var loader;
var width = 640;
var height = 525;
var controls;
// ステージを作る
scene = new THREE.Scene();
// 追加:地球テクスチャーを準備
loader = new THREE.TextureLoader();
loader.load('https://82mou.github.io/threejs/img/real-earth.jpg', function(texture) {
createEarth(texture);
render();
});
// 追加:雲テクスチャーを準備
loader.load('https://82mou.github.io/threejs/img/crowd.png', function(texture) {
createCrowd(texture);
render();
});
// 追加:宇宙テクスチャーを準備
loader.load('https://82mou.github.io/threejs/img/universe.jpg', function(texture) {
createUniverse(texture);
render();
});
// 追加:地球を作る
function createEarth(texture) {
sphereEarth = new THREE.Mesh(
new THREE.SphereGeometry(80, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture
})
);
sphereEarth.position.set(0, 0, 0);
scene.add(sphereEarth);
};
// 追加:雲を作る
function createCrowd(texture) {
sphereCrowd = new THREE.Mesh(
new THREE.SphereGeometry(82, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture,
transparent: true,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphereCrowd.position.set(0, 0, 0);
scene.add(sphereCrowd);
};
// 追加:宇宙を作る
function createUniverse(texture) {
sphereUniverse = new THREE.Mesh(
new THREE.SphereGeometry(10000, 20, 20), // 形状
new THREE.MeshLambertMaterial({ // 材質
map: texture,
side: THREE.DoubleSide // 裏からも見えるようにする
})
);
sphereUniverse.position.set(0, 0, 0);
scene.add(sphereUniverse);
};
// 追加:点光源を作る
light = new THREE.PointLight(0xffffff, 2.5, 0);
light.position.set(100, 130, 80);
scene.add(light);
// 環境光を作る
ambient = new THREE.AmbientLight(0x222222);
scene.add(ambient);
// カメラを作る
camera = new THREE.PerspectiveCamera(60, width / height, 1, 100000);
camera.position.set(200, 100, 300);
camera.lookAt(scene.position);
// helper
// gridHelper = new THREE.GridHelper(200, 20);
// scene.add(gridHelper);
// axisHelper = new THREE.AxisHelper(1000);
// scene.add(axisHelper);
// lightHelper = new THREE.PointLightHelper(light, 20);
// scene.add(lightHelper);
// controls
controls = new THREE.OrbitControls(camera);
// renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(width, height);
renderer.setClearColor(0xefefef);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('stage').appendChild(renderer.domElement);
function render() {
requestAnimationFrame(render);
sphereEarth.rotation.y += 0.0005; // 追加:地球を回転させる
sphereCrowd.rotation.y += 0.0010; // 追加:雲を回転させる
controls.update();
renderer.render(scene, camera);
}
})();
表示は以下のようになります。
See the Pen three.js universe by k_hatsushi (@hatsushi_kazuya) on CodePen.
リアルな宇宙と地球を簡単に作ることができました! こんな簡単に天体作れるなんて、three.js 楽しすぎる!
次回は、地面に影を描画してみたり、パーティクルを作る予定です。
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。