今回はWebGLのライブラリ「Three.js」を使って3Dコンテンツを制作します。
まずはデモを見てみましょう。
WebGLだけで3Dコンテンツを作ろうとすると、多くの複雑なコードを書くことになりますが、
Three.jsを使うことで比較的短いコードで簡単に実装することができます。
それでは早速コードを書いていきます。
まずはHTML
<div id="three"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
たった3行です。
3D空間を描写するためのdiv要素とthree.jsのファイルを読み込んでいます。
orbitcontrols.jsのファイルはカメラをコントロールできるようにするために必要なのでこちらも読み込んでいます。
次にcss
body {
margin: 0;
overflow: hidden;
}
こちらはbody要素のみの指定です。
bodyにoverflow:hiddenをかけることでブラウザから要素が飛び出した際にスクロールバーを消すことができます。
最後にJS
(function() {
var width;
var height;
var scene;
var camera;
var light;
var ambient;
var renderer;
var controls;
var count = 1200;
var i;
var square;
var size;
scene = new THREE.Scene();
light = new THREE.DirectionalLight(0xfffff0, 1);
light.position.set(0, 100, 30);
scene.add(light);
ambient = new THREE.AmbientLight(0x404040);
scene.add(ambient);
camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(60, 40, 80);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera);
controls.autoRotate = true;
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xefefef);
document.getElementById('three').appendChild(renderer.domElement);
for(i = 0; i < count; i++){
size = Math.random() * 1 + 2;
square = new THREE.Mesh(
new THREE.PlaneGeometry(size, size, size),
new THREE.MeshToonMaterial({
color: new THREE.Color(Math.random() % 50, Math.random() % 50, Math.random() % 50),
side: THREE.DoubleSide
}),
);
square.position.set(
Math.random() * 200 - 100,
Math.random() * 200 - 100,
Math.random() * 200 - 100
);
square.rotation.set(
Math.random() * 360 * Math.PI / 180,
Math.random() * 360 * Math.PI / 180,
Math.random() * 360 * Math.PI / 180
);
scene.add(square);
}
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();
}
function render() {
var objs;
requestAnimationFrame(render);
scene.traverse(function(obj){
if (obj instanceof THREE.Mesh === true){
obj.rotation.x += 0.01;
obj.rotation.y += 0.01;
if (obj.position.x >= 20 && Math.floor(obj.position.x) !== 20){
obj.position.x -= 0.1;
}else if(obj.position.x <= -20 && Math.floor(obj.position.x) !== -20){
obj.position.x += 0.1;
}if(obj.position.y >= 20 && Math.floor(obj.position.y) !== 20){
obj.position.y -= 0.1;
}else if(obj.position.y <= -20 && Math.floor(obj.position.y) !== -20){
obj.position.y += 0.1;
}if(obj.position.z >= 20 && Math.floor(obj.position.z) !== 20){
obj.position.z -= 0.1;
}else if(obj.position.z <= -20 && Math.floor(obj.position.z) !== -20){
obj.position.z += 0.1;
}
}
});
controls.update();
renderer.render(scene, camera);
}
render();
})();
内容は簡単ですが、少し長いので部分ごとに説明していきます。
scene = new THREE.Scene();
light = new THREE.DirectionalLight(0xfffff0, 1);
light.position.set(0, 100, 30);
scene.add(light);
ambient = new THREE.AmbientLight(0x404040);
scene.add(ambient);
camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);
camera.position.set(60, 40, 80);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera);
controls.autoRotate = true;
まず、3D空間の舞台となるsceneを作成します。
sceneの中に色々な設定を入れていくことで3D空間が完成します。
次に、照明、カメラを作ってあげます。
照明はカメラは様々な種類があるのですが、ここで説明すると長くなるので、Three.jsの公式サイトを参照してください。
ここでは、照明はDirectionalLightとAmbientLightの2つの照明を使っています。
カメラは、PerspectiveCameraを使い、第一引数から画角45、アスペクト比はwindowの高さ÷横幅、カメラから見え始める距離は1、カメラが見えなくなる距離は1000と設定しています。
また、Orbitcontrolを使って、カメラを自動で回していく設定をしています。
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0xefefef);
document.getElementById('three').appendChild(renderer.domElement);
次にrenderer(レンダラー)についてです。
レンダラーはHTMLとJavascriptで作成した3Dを紐づけるために使用します。
3Dグラフィックスを扱うときはWebGLRendererを使います。
オプションのantialiasはPhotoshop等で見かける機能のように、物体の輪郭のギザギザを滑らかにする処理です。
setClearColorは描画前に画面をクリアする際の色です。Three.jsでは、色の指定は十進数で指定することが推奨されています。
for(i = 0; i < count; i++){
size = Math.random() * 1 + 2;
square = new THREE.Mesh(
new THREE.PlaneGeometry(size, size, size),
new THREE.MeshToonMaterial({
color: new THREE.Color(Math.random() % 50, Math.random() % 50, Math.random() % 50),
side: THREE.DoubleSide
}),
);
square.position.set(
Math.random() * 200 - 100,
Math.random() * 200 - 100,
Math.random() * 200 - 100
);
square.rotation.set(
Math.random() * 360 * Math.PI / 180,
Math.random() * 360 * Math.PI / 180,
Math.random() * 360 * Math.PI / 180
);
scene.add(square);
}
ここでは、肝心の物体を作成しています。
Three.jsで物体を作るには、Geometry(形状)とMesh(材質)を組み合わせます。
GeometryとMeshに関しても様々なものがあるので公式サイトを参照するといいと思います。
まず、for文の繰り返し処理を使って1200個の正方形を作っています。
PlaneGeometryで平面の物体を作成し、MeshToonMaterialでアニメのようなトゥーンな材質に設定しています。
サイズは、Mathオブジェクトのrandom()を使ってランダムな数値を変数に入れていきます。
物体の色、位置、角度なんかも同じようにしてランダムで設定しています。
第一引数から第三引数までは、それぞれx軸、y軸、z軸となっています。
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();
}
リサイズイベントの処理です。
最初にonResize()関数を実行しているのはサイズの初期化のためです。
ここで3D空間を全画面表示にしています。
setPixelRatioは、解像度の高いRetinaディスプレイに対応させるための処理です。
function render() {
var objs;
requestAnimationFrame(render);
scene.traverse(function(obj){
if (obj instanceof THREE.Mesh === true){
obj.rotation.x += 0.01;
obj.rotation.y += 0.01;
if (obj.position.x >= 20 && Math.floor(obj.position.x) !== 20){
obj.position.x -= 0.1;
}else if(obj.position.x <= -20 && Math.floor(obj.position.x) !== -20){
obj.position.x += 0.1;
}if(obj.position.y >= 20 && Math.floor(obj.position.y) !== 20){
obj.position.y -= 0.1;
}else if(obj.position.y <= -20 && Math.floor(obj.position.y) !== -20){
obj.position.y += 0.1;
}if(obj.position.z >= 20 && Math.floor(obj.position.z) !== 20){
obj.position.z -= 0.1;
}else if(obj.position.z <= -20 && Math.floor(obj.position.z) !== -20){
obj.position.z += 0.1;
}
}
});
controls.update();
renderer.render(scene, camera);
}
render();
最後にレンダリングの処理です。ここで描画が出力されるので、このrender関数は最後に書く必要があります。
requestAnimationFrame()メソッドはブラウザでアニメーションさせたいことを知らせ、描画の前に呼び出してくれます。
このままここに関数を書いていくと、sceneに追加された最初の要素(先ほどfor文で作成した一番最初の四角形)しかアニメーションされないので、
scene.traverseとしてあげて、sceneの中の子要素を全て取得し、アニメーションさせてあげる必要があります。
要素の位置や角度を変えながらif文でx,y,z軸が20もしくは-20に到達した要素はアニメーションを止めるように設定します。
controls.updateはOrbitControlsでautoRotateをtrueにしたときに必要になります。
描画のたびにアップデートをしてくれるようになります。
最後にrender()関数を使ってあげておしまいです。
まとめ
Three.jsはモダンブラウザでしか動かないことや、Three.js自体のアップデートが速いため扱いづらい技術ですが、
日々進化していく最新技術の一つで、取り入れると非常にインパクトのあるサイト作りにも役立ちます。
これから台頭してくる技術だと思いますので、ぜひ挑戦してみてください。