Three.jsを用いた3Dコンピュータ・グラフィックス


 WebGLは、OpenGLというパソコン用の3D API(グラフィックスカードの能力を引き出し、三次元の絵を作り出すためのAPI)を、Webブラウザに移植したものでした。WebGLはこのOpenGL ESをベースに、Webブラウザ向けに微調整されて出来た規格なのです。いわば、OpenGL ES for Webとでも言えます。

 Three.jsは、WebGLのためのJavaScriptライブラリなので、HTML5で3Dコンテンツを制作するためのJavaScriptライブラリになっています。Mr.doob氏が中心となって開発されており、オープンソースソフトウェアとして個人・商用でも無償で利用できます。前のページで見た通り、Three.jsのライブラリを利用すると、プログラムの記述が極端に少なくて済みます。

 このページでは、JavaScript ライブラリであるThree.js を用いたWebGL の初級程度の利用法を紹介します。ここでは、OpenGLでのシェーディング言語、および、WebGL そのものの説明は行いません。WebGLについては、WebGLのページを読んでください。JavaScript の初歩的な知識があるといいかも知れません。


以下の例はThree.js公式ページの3DCGの例です。

この例を再生する

Last updated: 2019.3.16



Three.js ライブラリを用いた3D CG の基本


 まず最初に作成すべきファイルは、スケルトンのhtml ページです。

 Three.jsの公式ページの説明では、Three.jsを書き始める時のhtmlのスケルトン・ページは以下のようになっています。


// 例1
<!DOCTYPE html>
<html>
	<head>
		<meta charset=utf-8>
		<title>My first three.js app</title>
		<style>
		body { 
		  margin: 0; 
		  width: 100%;
		  height: 100%;
		  overflow: hidden;
		}

		canvas { 
		  width: 100%; 
		  height: 100%;
		}
		</style>
	</head>
<body>
		<script src="js/three.js"></script>
		<script>
...

			// Our Javascript will go here.
...
		</script>
</body>
</html>
 Three.js の解説書として有名な『初めてのThree.js」(Jos Dirksen著)では、スケルトン・ページの書き方は以下のように設定されています。
// 例2
<!DOCTYPE html>

<html>
	<head>
		<title>Basic skeleton</title>
		<meta charset=utf-8>
		<script type="text/javascript" src="../libs/three.js"></script>
		<style>
			body { 
			  margin: 0; 
			  overflow: hidden;
			}
		</style>
	</head>
<body>
		// Div which will hold the WebGL_Output 
	<div id="myWebGL">
	</div>

	<script type="text/javascript">

		// once everything is loaded, we run our Three.js stuff.
	function init() {
...
		// here we'll put the Three.js stuff
...
		}
	window.onload = init

	</script>
</body>
</html>
どちらを採用すべきは各自の好みに委ねます。

 ライブラリ"three.js"はCDN(コンテンツ・デリバリー・ネットワーク)で提供されているURLからダウンロードして使うこともできるので、

<script src="js/three.js"></script>

の部分は、

<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>

と変えてもいいと思います。

 なお、ICS MEDIAさんのチュートリアル・サイトの説明では、スケルトンhtml は以下のような仕様になっています。

// 例3
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/101/three.min.js"></script>
  <script>
    // ページの読み込みを待つ
    window.addEventListener('load', init);

    function init() {

      // ここに、JavaScript の処理コードを書く
    }
  </script>
</head>
<body>
  <canvas id="myCanvas"></canvas>
</body>
</html>

参考までに取り上げました。

 空間にグラフィックスを描画するためには、物体、それを写すカメラ、そして、物体が見えるための光源が必要です。 これらの3要素を配置する空間を[scene(シーン)]と言います。プログラムでは、最初にこのシーンを作成しなければいけない。以下のように記述します。

var scene = new THREE.Scene();


JavaScript での典型的なインスタンスの作成方法です。シーンでは、現実世界での人間の目がカメラになります。ですから、カメラがないと何も見えません。カメラの位置とその方向がシーン中で何が映るかを決めます。カメラをセットアップするために、vertical field of view (fov), an aspect ratio, a near plane, and a far planeを定める必要があります。fov とはカメラの角度のことです。aspect ratio は画面(ウインドウ)の横幅と縦幅の比率のことです。 near plane とは、カメラが写す最も近影までの距離、far plane はカメラが映し出す最終距離のことです。JavaScriptでは以下のように記述します。

// add a camera
// THREE.PerspectiveCamera(fov, aspect, near, far)
var camera = new THREE.PerspectiveCamera(
  75, 
  window.innerWidth/window.innerHeight, 
  0.1, 
  1000
);

// place the camera at z of 100
camera.position.z = 100;

 canvasにレンダリングして映像を書き入れる命令について説明します。rendering とはGPUが影をつけてディスプレイに映し出すことです。以下のように、// add a renderer の後の命令文のように記述します。renderer というインスタンスを作成して、appendChild関数でHTMLのbody要素(この例2では、div要素)にカメラの映像の中身を入れます。
// add a renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
// add the renderer element to the DOM so it is in our page:example1
document.body.appendChild( renderer.domElement );
        // add the output of the renderer to the html element:example2
        document.getElementById("myWebGL").appendChild(renderer.domElement);


 [scene]に、カメラと物体が存在しても、光がなければ何も映りません。光源を設定する必要が出てきます。three.js では、様々な色の光源と照射方向や反射効果などを設定できる命令文があります。


/* we need to add a light so we can see our cube - its almost
as if we're turning on a lightbulb within the room */
var light = new THREE.PointLight(0xFFFF00);
/* position the light so it shines on the cube (x, y, z) */
light.position.set(10, 0, 25);
scene.add(light);


 ”scene.add(light);”で[scene]に光源を追加しています。最後に、[scene]に物体を配置しましょう。立方体を配置するときは、以下のように記述します。

/* we're creating a cube to put in our scene - don't worry
if you don't follow this part, we'll cover geometry and materials
in future posts */
var geometry = new THREE.BoxGeometry(20, 20, 20);
var material = new THREE.MeshLambertMaterial({color: 0xfd59d7});
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);


 ”scene.add(cube);”で[scene]に物体(立方体)を追加しています。

 これらを完成させると、以下のプログラムになります。以下をクリックするとこのプログラムが実行されます。

<!DOCTYPE html>
<head>
  <meta charset="utf-8"/>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/100/three.min.js"></script>
  </head>
<style>
body { 
  margin: 0; 
  width: 100%;
  height: 100%;
  overflow: hidden;
}

canvas { 
  width: 100%; 
  height: 100%;
}
</style>
<body>
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

var geometry = new THREE.BoxGeometry( 20, 20, 20);
var material = new THREE.MeshLambertMaterial( { color: 0xfd59d7 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );

camera.position.z = 100;

var light = new THREE.PointLight( 0xFFFF00 );
light.position.set( 10, 0, 25 );
scene.add( light );



var render = function () {
  requestAnimationFrame( render );

  cube.rotation.x += 0.1;
  cube.rotation.y += 0.1;
  camera.updateProjectionMatrix();

  renderer.render(scene, camera);
};

render();

</script>
</body>
</html>

 Three.min.jsはJavaScriptのライブラリですが、このファイルを読み込むことによってはじめてThree.jsが利用できるようになります。CDN(コンテンツ・デリバリー・ネットワーク)で提供されているURLを使います。"var render = function () "でrender関数を定義して、render()でGPUに命令を送っています。

 HTMLでグラフィック関連のタグとして追加された canvas は、その名の通り図形や画像を描画するためのキャンバスとして動作するタグで、javascript を使って操作します。もちろん、Three.js もこの canvas を利用することになるので、コード を記述する前に canvas タグについて簡単に解説します。canvas要素には属性としてid(ID値)を設定することもできます。この場合、以下のように記述します。

<body>
  <canvas id="myCanvas"></canvas>
</body>

canvas要素の内容はJavaScriptを使って設定します。

Three.js ライブラリGeometryの使用法


 すでに、箱を描くプログラムを実行しました。球体の3D CGを作成するときは、'sphereGeometry'を使用します。箱を作成という部分を以下のように修正します。

 // 球体を作成
  const geometry = new THREE.SphereGeometry(300, 30, 30);
  const material = new THREE.MeshStandardMaterial({color: 0xFF0000});
  // メッシュを作成
  const mesh = new THREE.Mesh(geometry, material);
  // 3D空間にメッシュを追加
  scene.add(mesh);

 JavaScriptコード全体は以下のようになります。

<script>
// ページの読み込みを待つ
window.addEventListener('load', init);

// サイズを指定
const width = 960;
const height = 540;

function init() {
  // レンダラーを作成
  const renderer = new THREE.WebGLRenderer({
    canvas: document.querySelector('#myCanvas')
  });
  renderer.setSize(width, height);

  // シーンを作成
  const scene = new THREE.Scene();

  // カメラを作成
  const camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
  camera.position.set(0, 0, +1000);

  // 球体を作成
  const geometry = new THREE.SphereGeometry(300, 30, 30);
  const material = new THREE.MeshStandardMaterial({color: 0xFF0000});
  // メッシュを作成
  const mesh = new THREE.Mesh(geometry, material);
  // 3D空間にメッシュを追加
  scene.add(mesh);

  // 平行光源
  const directionalLight = new THREE.DirectionalLight(0xFFFFFF);
  directionalLight.position.set(1, 1, 1);
  // シーンに追加
  scene.add(directionalLight);

  tick();

  // 毎フレーム時に実行されるループイベントです
  function tick() {
    // メッシュを回転させる
    mesh.rotation.y += 0.01;
    // レンダリング
    renderer.render(scene, camera);

    requestAnimationFrame(tick);
  }
}
  </script>

以下のファイルを開いてください。球体の3Dグラフィックスが描かれます。ICS MEDIAさんのチュートリアル・サイトから引用しています。

 球体上のような形状がThree.jsで扱える基本的な形状として、いくつか用意されています。代表的なものは、
平面
球体
直方体
三角錐
円柱
カプセル形状
ドーナツ形状
などです。Three.jsでは形状とマテリアルを結合しメッシュを作成することによって、表示可能な3Dのオブジェクトを得ることができます。具体的なコードは、ジオメトリをドーナッツ状のケースのとき以下のように記述します。


var geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var torus = new THREE.Mesh( geometry, material );
scene.add( torus );


 Torus に変更すると、以下のようなファイルになります。ここをクリックしてグラフィックスを表示してください。

 ジオメトリを自由に変形して自由な立体図形を描くことができます。例えば、直方体の頂点数を増やしたり、頂点座標をランダムに変化させるとどのような立体図形が生まれるのでしょうか。JavaScript を以下のようにします。

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 30, window.innerWidth/window.innerHeight, 0.1, 1000 );

var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

// add cube
var geometry = new THREE.BoxGeometry( 20, 20, 20);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );

// update cube vertices
for (var i = 0, l = geometry.vertices.length; i

ここをクリックしてグラフィックスを表示してください。

どんな立体図形が描かれているでしょうか?


Three.js ライブラリMaterialの使用法


 描画物体のマテリアルに画像を使用する方法について説明します。マテリアルとは描画物体の質感設定のことです。見栄えを決める「マテリアル」を指定して、たとえば、着色や画像・陰影の割り当て、ライティングの反射などを適用できます。マテリアルに画像を使うには読み込み処理を作る必要があります。'THREE.TextureLoader'クラスを使って次のように指定します。地球儀のような球体が描画されます。


// 画像を読み込む
var loader = new THREE.TextureLoader();
var texture = loader.load('image/earthmap.jpg');
// マテリアルにテクスチャーを設定
var material = new THREE.MeshStandardMaterial({
  map: texture
});


この部分を追加した以下のファイルを開いてください。
 次に、マテリアルにMeshStandardMaterial ではなくて、MeshPhongMaterial を使用した例を取り上げます。THREE.MeshPhongMaterialクラスはフォン・シェーディングと言う、光沢感のある質感を表現できるマテリアルです。 以下のように、マテリアルの部分を修正します。図形は8面体です。

// add icosahedron
var geometry = new THREE.OctahedronGeometry( 20 );
var color = new THREE.Color( "#7833aa" );
var material = new THREE.MeshPhongMaterial( {color: color.getHex()} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );


以下のファイルを開いてください。8面体が回転するグラフィックが表示されます。『初めてのThree.js』(Jos Dirksen著)から引用。



Three.js でのAnimationの設定


 以上までに、

mesh のposition & rotation
geometry & material
camera のposition & rotation
lighting のposition & direction
などの要素の使用法についてある程度まで学習しました。 ここでは、簡単なアニメーションの使用法について取り上げます。

アニメーションを描画するためには、requestAnimationFrame関数を用います。最初に、シーンを初期化するためにrender()関数を呼び出して、render()関数内でrequestAnimationFrameを使用します。以下のコードのように記述します。
// call the render function
var step = 0;
var scalingStep = 0;

var controls = new function () {
this.rotationSpeed = 0.02;
this.bouncingSpeed = 0.03;
this.scalingSpeed = 0.03;
};


...

render();

function render() {
stats.update();
// 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)));

// scale the cylinder

scalingStep += controls.scalingSpeed;
var scaleX = Math.abs(Math.sin(scalingStep / 4));
var scaleY = Math.abs(Math.cos(scalingStep / 5));
var scaleZ = Math.abs(Math.sin(scalingStep / 7));
cylinder.scale.set(scaleX, scaleY, scaleZ);

// render using requestAnimationFrame
renderer.render(scene, camera);
requestAnimationFrame(render);

}
...


この例では、シーンの中に、3種類のジオメトリ、cube, sphere, cylinder が描かれています。 以下は『初めてのThree.js』(Jos Dirksen著)で提供されているコードを用いた簡単なアニメーションの例です。

この例を再生する


そう少し複雑な例を見てみましょう。以下の布が風になびくanimation の例はhttps://threejs.org/examples/の一つです。

再生する

 Blenderで描いたアニメーションCGをGLBファイル形式でexportして、このファイルをGLTFLoader.jsで読み込んでThree.jsで再現できます。以下のロボット兵士のアニメーションがこの例です。

再生する

 SEA3D Studioで作成されたファイルからThree.js用のファイルにexportされた3Dアニメーションの例を以下に挙げます。

再生する
SEA3D Studioの公式サイトにはThree.js向けに変換されたexamplesが多数紹介されています。

音声付き3DCGアニメーションをWebGLで再現する


 最初に。音声データを3D表現する例を取り上げてみます。Three.js公式ホームページにあるWebAudioのSound Visualizerの例を修正してみました。

webAudio_visualizerのexampleを再生

 Blender Studioが制作したショートムービー'Sintel - Third Open Movie by Blender Foundation'のビデオ音声を用いて、映像をThree.jsで描画したexampleがThree.js公式ホームページにあります。下の写真はSintelの一場面を描いたものです。全体の映像はYouTubeで見れます。

Three.jsのexampleを再生する(スマホでは映像が出ません)

 日本でよく知られているCGアニメーションの代表例は初音ミクが歌ってダンスをするMikuMikuDance(ミクミクダンス)です。MikuMikuDanceは、樋口優氏が制作した、プリセットされたキャラクターの3Dモデルを操作してコンピュータアニメーションを作成する3DCGソフトウェアです。略称でMMDと呼ばれます。デフォルトで初音ミクなどVOCALOIDキャラクターの3Dモデルを使用できるほか、オリジナルのキャラクターモデルやアクセサリを組み込んで操作することも可能です。Three.jsにMMDを読み込むソフトMMDLoaderが開発されているので、WebGLで再現することができます。 MMDLoaderを用いたMikuMikuDanceの例:

MikuMikuDanceを再生(Safariでは音声が出ません)
webgl_mmdloaderの再生(Safariでは音声が出ません)

 VOCALOIDモデル付属の 3DCG動画制作ツール「MikuMikuDance」ソフトの始め方・使用法の紹介はVPVPwikiを参照してください。MMDの使用に関して、以下のように説明されています。「ドラマチックに用意されたシナリオや、明確なゴールがあるゲームではありません。 MMDには人それぞれの楽しみ方があり、またそれは誰かによって定められるものでもありません。MMDはWindows XP/Vista/7/8/10 上で動作するフリーウェアです。 誰でもダウンロードして楽しむことができます。 オリジナルのユーザモデルやアクセサリ、モーションなどを様々な作者が公開しています。 それらをダウンロードして自分のPC上で組み合わせて楽しむことができます。」ここに明記されている通り、もともとのMMDソフトのプラットホームはWindowsですが、上で行ったように、青柳氏が開発したMMDLoaderを利用して、WebGLで描画すれば、OSの種類に依存しなくなります。

 青柳氏が開発して提供するMikuMikuDanceのMMDLoader sample appsはこのGitHubサイトにあります。この中のMikuMikuDanceを再生(Safariでは音声が出ません)音声を出すためには、自分のPCでAppsを展開して、再生してください。上の例では、Web Audio APIを用いて音声を処理しますので、再生してもこれらに対応していないブラウザでは音声が出ません。FireFoxやChromeでは音声が出ますが、Safariやスマホなどでは音声が出ません。


カメラの移動とコントロール


 Three.jsには多数のカメラカメラコントロールがあり、シーン内のカメラを自由に移動させることができます。これらのコントロールのうち、以下のコントロールがよく使用されます。


 TrackBallControlsを使用するときは、以下のようにJavaScriptファイルを読み込む必要があります。

<script type="text/javascript" src="js/TrackBallControlls.js"></script>
...
	var trackballControls = new THREE.TrackballControls(camera);
	trackballControls.rotateSpeed = 1.0;
	trackballControls.zoomSpeed = 1.0;
	trackballControls.panSpeed = 1.0;

 このコントロールを用いてカメラの位置を更新するためには、rende()関数内で

	var clock = new THREE.Clock();

	function render() {
		stats.update();
		var delta = clock.getDelta();
		trackballControls.update(delta);
		//webGLRenderer.clear();
		// render using requestAnimationFrame
		requestAnimationFrame(render);
		webGLRenderer.render(scene, camera)
        }

のように書きます。

 カメラは以下のような操作でコントロールできます。  TrackBallControlsのカメラ操作はこの例で練習してください。
 FlyControlsを使用するときは、以下のようにJavaScriptを読み込むコードを書きます。

// <div id="WebGL-output"></div>でWebGLに描画するとき

	<script type="text/javascript" src="js/FlyControls.js"></script>
...
	var flyControls = new THREE.FlyControls(camera);
	flyControls.movementSpeed = 25;
	flyControls.domElement = document.querySelector("#WebGL-output");
	flyControls.rollSpeed = Math.PI / 24;
	flyControls.autoForward = true;
	flyControls.dragToLook = false;

 このコントロールを用いてカメラの位置を更新するためには、render()関数内で

	var clock = new THREE.Clock();

	function render() {
		stats.update();
		var delta = clock.getDelta();

		flyControls.update(delta);
		webGLRenderer.clear();
		// render using requestAnimationFrame
		requestAnimationFrame(render);
		webGLRenderer.render(scene, camera)
		}

のように書きます。

 カメラは以下のような操作でコントロールできます。  FlyControlsのカメラ操作はこの例で練習してください。なお、このFlyControlsとFirstPersonContolsのカメラ例は、『初めてのThree.js」(Jos Dirksen著)で提供されているものです。


 FirstPersonControlsを使用するときは、以下のようにJavaScriptを読み込むコードを書きます。

...
   <script type="text/javascript" src="./js/controls/FirstPersonControls.js"></script>
...
        var camControls = new THREE.FirstPersonControls(camera);
        camControls.lookSpeed = 0.4;
        camControls.movementSpeed = 20;
        camControls.noFly = true;
        camControls.lookVertical = true;
        camControls.constrainVertical = true;
        camControls.verticalMin = 1.0;
        camControls.verticalMax = 2.0;
        camControls.lon = -150;
        camControls.lat = 120;

render()関数は上の例と同じように記述します。キーボードの上下左右カーソルキーでカメラを移動させます。周囲を見るときは、マウスを移動させます。

 FirstPersonControlsのカメラ操作はこの例で練習してください。

 カメラのOrbitControlsの詳細は、公式ページを参照ください。

**************


続く 
Three.js でコンピュータ・グラフィクスを楽しんで描こう
**************

このページの先頭に行く

Blenderのページに行く

トップ・ページに行く