Home
JavaScript
【Three.js基礎】概念の理解と立方体の実装

【Three.js基礎】概念の理解と立方体の実装

【Three.js基礎】概念の理解と立方体の実装

どうも!かけちまるです!

Webサイトで容易に3D表現を実装できるThree.js

初心者向けに概念と簡単な制作物を作成していきます。

本記事の制作物だけでは、実務で使えるレベルになれるわけではありません。

ですが、概要を理解することは大事なので一読しても良いかもしれません。

この記事では、

  • ・Three.jsとは
  • ・3D表現で必要な概念
  • ・立方体を描画する方法
  • ・アニメーションやリサイズ処理などの方法

がわかります。

Three.jsとは?

Three.jsは、Web上で3D表現を容易にできるようにするライブラリです。JavaScriptで書きます。

個人・商用でも無償で利用できます。

WebGLだけで3D表現をしようとすると凄まじい量のコードを書く必要があるのでThree.jsは難しいのに簡単だと言われているのだと思います。

Three.jsで必要な概念

Three.jsでWeb上に3Dオブジェクトを描画するために用意しなければいけないものがあります。

それは、シーン・カメラ・レンダラー・ジオメトリ・マテリアル・メッシュ・ライトです。

このシーン・カメラ・レンダラー・ジオメトリ・マテリアル・メッシュ・ライトを現実世界に例えるとこんな感じです。

現実世界に例える

1つずつ説明していきます。

1

シーン・カメラ・レンダラーの用意

それぞれ現実世界に例えると、

  • ・シーン → ステージ
  • ・カメラ → ビデオカメラ
  • ・レンダラー → YouTube

というような感じ

ステージをカメラで写し、YouTubeでライブ配信してブラウザに表示するイメージです。

ステージをカメラで写し、YouTubeでライブ配信してブラウザに表示
2

ジオメトリ・マテリアル・メッシュを用意

ジオメトリ・マテリアル・メッシュは今回の立方体にあたるものです。

ジオメトリ・マテリアル・メッシュが何なのかというと

  • ・ジオメトリ・・・形を決める骨格のようなもの
  • ・マテリアル・・・色や質感を決めるようなもの
  • ・メッシュ・・・ジオメトリとマテリアルを組み合わせたもの

というところでしょうか。

※画像はわかりやすいように球体になっています。

ジオメトリ・マテリアル・メッシュの仕組み

なので、メッシュをステージに置くことでライブ配信でブラウザに映し出すことができそうですが、まだステージが真っ暗なので映りません。

メッシュをステージに置く
3

ライトを用意

ライトは、ステージを照らす役割をします。
マテリアルの設定によっては不要です。

ライト → 照明のイメージで大丈夫です。

照明でステージを照らすことでメッシュが見えるようになりました。

現実世界に例える

まとめると

  • ①シーン・カメラ・レンダラーの用意
  • ②ジオメトリ・マテリアル・メッシュを用意
  • ③ライトを用意

そんな感じで3Dオブジェクトを映せることがわかりました。

立方体を描画してみる

Three.jsの基本とされている立方体をWeb上に表現してみましょう。

手順としては3D表現で必要な概念とほぼ同じです。

ディレクトリ構成

ディレクトリ構成は次のようにします。

ディレクトリ構成
コピー
index.html css/ -style.css js/ -main.js

立方体をブラウザへ描画する

1

CDNでThree.jsを読み込む

CDNでは、モジュール版が推奨なのでモジュール版を読み込みます。

index.htmlへ次のように記述します。
handタグ内で良いでしょう。

index.html
コピー
<script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script>

読み込み方につまづいたらこちらの記事が参考になりそうです。

2

HTMLとCSSの準備

一応環境を合わせるためにHTMLとCSSを載せときます。
大したことはしていません。

bodyタグ内にcanvasタグを入れるのでbodyタグは書いておいてください。

index.html
コピー
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" type="text/css" href="./css/style.css"> <script async src="https://unpkg.com/es-module-shims@1.5.8/dist/es-module-shims.js"></script> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.142.0/build/three.module.js" } } </script> <script src="./js/main.js" type="module"></script> </head> <body></body> </html>
style.css
コピー
* { margin: 0; padding: 0; box-sizing: border-box; }
3

シーン・カメラ・レンダラーを用意する

コードにコメントで説明が書いてあるので軽く解説します。

シーンの用意は1行で一瞬ですね。(7行目)

カメラは、PerspectveCameraを使います。(10行目)
設定は、PerspectveCamera(視野角, アスペクト比, near, far)で設定します。
カメラで設定した範囲が映されることになります。
※カメラやライト・ジオメトリ・マテリアルなどは種類がたくさんあるので公式サイトなどで調べてみてください。

PerspectveCameraの設定

今回、nearを0.1にしているので青い部分が手前にある感じです。

レンダラーは、ブラウザのサイズに合わせてbodyタグ内に挿入しています。(20、21行目)
最後にレンダラーにシーンとカメラをレンダラーに認識させる感じです。(24行目)

main.js
コピー
import * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ // シーンを用意 const scene = new THREE.Scene(); // カメラを用意 const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; // カメラをz軸方向に5ずらす(後ろにずらす) // レンダラーを用意 const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーをwindowのサイズと合わせる document.body.appendChild(renderer.domElement); // レンダラー(canvas)をbody内に挿入 // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera);

これでブラウザ全体が真っ黒になったらOKです。

4

ジオメトリ+マテリアル=メッシュを用意する

今回は立方体を描画するのでジオメトリはBoxGeometryを使います。(8行目)
設定は、BoxGeometry(幅, 高さ, 奥行き)で設定します。

マテリアルは、光沢感のないマットな質感を表現できるMeshLambertMaterialを使います。(11行目)
色は青っぽい色にします。(12行目)
色の指定方法ですが、0x3da8e60x部分は#と同じ意味合いです。

そして、ジオメトリとマテリアルを合わせてメッシュ化し、シーンに読み込ませます。(16、17行目)

main.js
コピー
・・・ document.body.appendChild(renderer.domElement); // レンダラー(canvas)をbody内に挿入 /** * ジオメトリ+マテリアル=メッシュを用意する */ // ジオメトリを用意 const boxGeometry = new THREE.BoxGeometry(1, 1, 1); // (幅, 高さ, 奥行き) // マテリアルを用意 const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x3da8e6, // 色を設定 }); // ジオメトリ+マテリアル=メッシュ化 const boxMesh = new THREE.Mesh(boxGeometry, lambertMaterial); scene.add(boxMesh); // メッシュをsceneにaddする // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera);
5

ライトを用意する

ライトは、DirectionalLightを使います。(9行目)
DirectionalLightは、特定方向に光を照らすやつです。
引数では、光の色と光の強さを設定しています。

それから、ライトの位置を調整します。(10行目)
今回は、左斜め上くらいからライトを当てるようにしています。

最後にライトをシーンに読み込ませます。(11行目)

main.js
コピー
・・・ // ジオメトリ+マテリアル=メッシュ化 const boxMesh = new THREE.Mesh(boxGeometry, lambertMaterial); scene.add(boxMesh); /** * ライトを追加する */ const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // (光の色, 光の強さ) directionalLight.position.set(-1, 2, 4); // (x, y, z) scene.add(directionalLight); // ライトをsceneにaddする // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera);
6

完成

完成はこんな感じです。

完成イメージ

コードの全体は以下です。

main.js
コピー
import * as THREE from 'three'; /** * シーン・カメラ・レンダラーを用意する */ // シーンを用意 const scene = new THREE.Scene(); // カメラを用意 const camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; // カメラをz軸方向に5ずらす(後ろにずらす) // レンダラーを用意 const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーをwindowのサイズと合わせる document.body.appendChild(renderer.domElement); // レンダラー(canvas)をbody内に挿入 /** * ジオメトリ+マテリアル=メッシュを用意する */ // ジオメトリを用意 const boxGeometry = new THREE.BoxGeometry(1, 1, 1); // (幅, 高さ, 奥行き) // マテリアルを用意 const lambertMaterial = new THREE.MeshLambertMaterial({ color: 0x3da8e6, // 色を設定 }); // ジオメトリ+マテリアル=メッシュ化 const boxMesh = new THREE.Mesh(boxGeometry, lambertMaterial); scene.add(boxMesh); // メッシュをsceneにaddする /** * ライトを追加する */ const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // (光の色, 光の強さ) directionalLight.position.set(-1, 2, 4); // (x, y, z) scene.add(directionalLight); // ライトをsceneにaddする // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera);

追加実装

上記の立方体を描画してみるまで実装できたことを前提として機能を追加で実装してみましょう。

立方体を動かしてみる

アニメーションを実行するためにanimate関数を用意します。(5行目)

animate関数内にrenderer.render(scene, camera);を移します。(7行目)

そして、メッシュをxy方向に傾ける記述を書きます。(9,10行目)

最後に、フレーム毎にanimate関数を実行する設定とanimate関数を実行することでアニメーションを実行することができます。(12,14行目)

main.js
コピー
・・・ // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) // renderer.render(scene, camera); function animate() { // シーンとカメラをレンダラーに読み込ませる(最後に実行するようになっていること) renderer.render(scene, camera); boxMesh.rotation.x += 0.01; boxMesh.rotation.y += 0.01; requestAnimationFrame(animate); // 引数内の関数をフレーム単位で読み込む } animate();

リサイズに対応する

ここまでだとブラウザをリサイズするとおかしな感じになるのでこれに対応していきます。

まず、リサイズした際に何を更新した方が良いかというと

  • ・レンダラーのサイズ
  • ・カメラのアスペクト比

だと思います。

ポイントとしては、カメラを更新した際はcamera.updateProjectionMatrix();を呼び出す必要があるということです。

main.js
コピー
・・・ requestAnimationFrame(animete); // 引数内の関数をフレーム単位で読み込む } animete(); // ブラウザのリサイズに対応させる window.addEventListener('resize', onWindowResize); function onWindowResize() { renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーのサイズをれサイズ対応 camera.aspect = window.innerWidth / window.innerHeight; // カメラのアスペクト比をリサイズ対応 camera.updateProjectionMatrix(); // カメラを更新した際に呼び出す必要がある }

立方体がちょっと粗い

立方体の境目が粗くなっている場合はsetPixelRatio(window.devicePixelRatio)でデバイスのピクセル比を設定します。

main.js
コピー
・・・ // レンダラーを用意 const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); // レンダラーをwindowのサイズと合わせる renderer.setPixelRatio(window.devicePixelRatio); // デバイスのピクセル比を設定 document.body.appendChild(renderer.domElement); // レンダラー(canvas)をbody内に挿入 ・・・

おわり

かけちまる
かけちまる
Webエンジニアをしています。
HTML/CSS/JavaScript/jQuery/PHPができます。
WEB制作を中心に日々学びになったこと、興味が沸いたことについて初心者の方でもわかりやすいようにアウトプットしていくブログです。

関連記事

【videoタグ】HTMLでWebサイトに動画を埋め込む

【videoタグ】HTMLでWebサイトに動画を埋め込む

GLSLの修飾子varyingの使い方【Three.js】

GLSLの修飾子varyingの使い方【Three.js】

floatで雑誌のようなテキストの回り込みを表現する

floatで雑誌のようなテキストの回り込みを表現する

positionで要素を中央寄せにする方法【CSS】

positionで要素を中央寄せにする方法【CSS】

Visual Studio Codeの検索と置換

Visual Studio Codeの検索と置換

【レスポンシブ対応】Facebook・Twitterのタイムライン埋め込み方法

【レスポンシブ対応】Facebook・Twitterのタイムライン埋め込み方法