Three.jsでブラウザ上で3Dモデルをマウスで操作

先日、Three.jsを利用したことがない人向けの文書を公開しましたが、今回はobjファイルで作成されたモデルを使用してさらにThree.jsの理解を深めていきます。立方体(cube)などのThree.jsで準備されたGeometryからもっと実用性の高いプログラムをThree.jsで作りたいと思っていた人にもおすすめです。
初回投稿時から時間が経過しているのでthree.jsサイトの構成も変更されており以前の方法ではエラーになる箇所があったので更新を行なっています。
obj(オブジェファイル)とは
3Dモデルデータのファイルフォーマットの1つで、拡張子.objを持つファイルです。objファイルはテキストファイルで記述されており、形状の情報のみ持っています。objファイルとは別に素材の情報を持つmtlファイルがあり、形状の表面に使用する画像等の情報が記載されています。通常は、maya、blendarといった3Dのソフトウェアを利用して作成しますが、本文書では作成済みのものを利用してThree.jsで動作確認を行います。
Free3Dといったサイトからフリーでobjファイルをダウンロードすることができますが、Three.jsの公式サイトにobjファイルとmtlファイルを使ったExampleがあったのでそこで使われているファイルを利用します。
Exampleのダウンロード
Three.jsサイトの構成が変わったたらダウンロードを行うためのリンクの場所が変更になりました。
2022年7月の方法
Three.jsのサイトにアクセスして、左側メニューのDownloadをクリックします。コードだけではなくExampleの中で使用している素材も一緒にダウンロードすることができます。

300MB以上にあるzipファイルなので、環境によっては時間がかかるかもしれません。ダウンロード後、zipファイルを解凍してください
以前の方法
Three.jsのサイトにアクセスして、左側メニューのDownloadをクリックします。コードだけではなくExampleの中で使用している素材も一緒にダウンロードすることができます。

200MB以上にあるzipファイルなので、環境によっては時間がかかるかもしれません。ダウンロード後、zipファイルを解凍してください
前準備
3Dモデルをブラウザに描写し、マウスを使って動かすためには、objファイルとthree.jsファイル以外に3つのjavascriptファイルを準備する必要があります。

objファイルとmtlファイル
objファイルとmtlファイルは、ダウンロードしたフォルダの中のexamples¥models¥objフォルダにあるmale02フォルダを使用します。
objファイルはテキストファイルとなっており、3DのソフトウェアであるBlenderで作成されていることがわかります。
# Blender v2.54 (sub 0) OBJ File: ''
# www.blender.org
mtllib male02.mtl
o mesh1.002_mesh1-geometry
v 4.649472 159.854965 5.793066
v 1.662002 157.904083 5.834653
v 1.600978 157.673813 6.226475
v 4.363278 159.890976 5.326734
v 6.608557 161.854065 3.049800
v 3.466243 161.077850 5.782724
v 5.649033 163.753754 5.049810
v 6.906231 162.829758 -0.187148
v 7.238166 164.680359 1.732683
v 5.584624 163.417984 -3.092680
mtlファイルもobjファイル同様にテキストファイルですが、ファイル内に画像ファイルの情報が入っています。5つの素材から構成され、01_-_Default1noCulling.JPGが画像ファイルです。
# Material Count: 5
newmtl _01_-_Default1noCulli__01_-_Default1noCulli
Ns 154.901961
Ka 0.000000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.165000 0.165000 0.165000
Ni 1.000000
d 1.000000
illum 2
map_Kd 01_-_Default1noCulling.JPG
mtlファイルに記述されている3Dモデルの顔の部分の画像ファイルは3D用なので下記のような画像です。

JavaScriptファイル
objファイルとmtlファイルを読み込むためのLoaderファイルが必要となります。解凍したフォルダのsrc¥loaders¥フォルダの中にあるOBJLoader.js、MTLLoader.jsを使用します。
camera(カメラ)をマウスで操作するためOrbitControls.jsファイルを使用します。解凍したフォルダのexamples¥js¥controlsの中にあります。
以前はthree.jsはcdnを利用していましたが2022年7月ではthree.jsファイルはダウンロードフォルダのbuildに入ったthree.module.jsファイルを利用します。JavaScriptはthree.module.js, MTLLoader.js, OBJLoader.js, OrbitControls.jsの4つのファイルを利用します。
フォルダ構成
プログラム作成前のフォルダ構成は下記のようになります。

以前のCDNのthree.jsファイルを利用していた時のフォルダ構成は下記です。

初めての3Dモデルの操作
下記が3Dモデルをマウスで操作するプログラムです。長く感じるかもしれませんが、8つのパーツに分かれており、それぞれのパートは数行程度です。また、半分以上のパートが前回の入門編で使用したものと同じコードのため、非常に簡単なコードになっています。
下記の5箇所はパラメータの数値が少し異なりますが、前回の入門編を参考にしてください。
- scene(シーン)の作成
- camera(カメラ)の作成
- renderer(レンダラー)の作成
- camera(カメラ)の位置を設定
- 繰り返しの処理
8つのパートの残り3つ(ライト,カメラのマウス操作, ファイルの読み込み)は下記で説明を行なっていきます。
利用するコードは下記の通りです。importmapを利用しています。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Load a 3D model</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<script
async
src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"
></script>
<script type="importmap">
{
"imports": {
"three": "./js/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { MTLLoader } from './js/MTLLoader.js';
import { OBJLoader } from './js/OBJLoader.js';
import { OrbitControls } from './js/OrbitControls.js';
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
50,
window.innerWidth / window.innerHeight,
1,
1000
);
var renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var ambientLight = new THREE.AmbientLight(0xcccccc, 4);
scene.add(ambientLight);
camera.position.z = 250;
var controls = new OrbitControls(camera, document.body);
new MTLLoader()
.setPath('models/male02/')
.load('male02.mtl', function (materials) {
materials.preload();
new OBJLoader()
.setPath('models/male02/')
.setMaterials(materials)
.load('male02.obj', function (object) {
object.position.y = -95;
scene.add(object);
});
});
// /* 繰り返しの処理 */
var animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
</script>
</body>
</html>
importmapを利用しない場合は下記のように記述することができます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Load a 3D model</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<script type="module">
import * as THREE from './js/three.module.js';
import { MTLLoader } from './js/MTLLoader.js';
import { OBJLoader } from './js/OBJLoader.js';
import { OrbitControls } from './js/OrbitControls.js';
var scene = new THREE.Scene();
//略
この場合はMTLLOader.js, OBJLoader.js, OrbitControls.jsファイルのimportを変更する必要があります。変更前はimportは’three’から行なっていましたが変更後は’three.module.js’ファイルを指定します。下記はMTLLOader.jsファイルですが残りの2つのファイルも同様に変更してください。
import {
Color,
DefaultLoadingManager,
FileLoader,
FrontSide,
Loader,
LoaderUtils,
MeshPhongMaterial,
RepeatWrapping,
TextureLoader,
Vector2,
sRGBEncoding,
} from './three.module.js';
// } from 'three'; 変更前
scriptタグを利用した以前のJavaScriptファイルの読み込み方法
importを利用せずscriptタグで指定していた時は下記のように記述していました。以前は正常に動作していましたが今はエラーが発生します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Load a 3D model</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.js"></script>
<script src="js/controls/OrbitControls.js"></script>
<script src="js/loaders/OBJLoader.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script>
/* scene(シーン)の作成 */
var scene = new THREE.Scene();
/* camera(カメラ)の作成 */
var camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 1, 1000 );
/* renderer(レンダラー)の作成 */
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
/* Light(ライト)の設定 */
var ambientLight = new THREE.AmbientLight( 0xcccccc, 2 );
scene.add( ambientLight );
/* camera(カメラ)の位置設定 */
camera.position.z = 250;
/* camera(カメラ)をマウス操作する設定 */
var controls = new THREE.OrbitControls(camera);
/* MTLファイルとObjファイルの読み込み */
new THREE.MTLLoader().setPath('models/male02/')
.load('male02.mtl',function(materials){
materials.preload();
new THREE.OBJLoader().setPath('models/male02/')
.setMaterials(materials)
.load('male02.obj',function (object){
object.position.y = - 95;
scene.add(object);
});
});
/* 繰り返しの処理 */
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
Light(ライト)の設定
Light(ライト)にもさまざまな種類、設定がありますが、空間全体に光を当てるAmbientLightを使用します。
camera(カメラ)をマウス操作する設定
下記の1行を加えるだけで、3Dモデルをマウスで操作することが可能になります。
var controls = new THREE.OrbitControls(camera);
MTLファイルとObjファイルの読み込み
MTLLoaderを使用して、mtlファイルの読み込みを行います。setPathではmtlファイルの保存しているフォルダのパスを指定し、loadで読み込むファイル名を指定しています。mtlの素材情報を読み込んだ後にOBJLoaderで形状の情報の入ったobjファイルの内容を読み込みんでいます。必ずMTLLoaderの中でOBJLoaderを使用します。
ブラウザで確認

ブラウザ上でマウスを動かすとカメラの位置が移動して、別の位置、別角度から3Dモデルを見ることができます。


objファイルさえ準備できれば簡単に3Dモデルをブラウザ上で操作することができるかがわかったかと思います。
その他の設定
Light(ライト)のない世界
もしLight(ライト)がモデルにあてられていなかった場合の動作を確認してみましょう。下記のLight(設定)部分をコメントアウトします。
//var ambientLight = new THREE.AmbientLight( 0xcccccc, 2 );
//scene.add( ambientLight );
Light(ライト)の設定がなければ、3Dモデルの姿を確認することができません。

背景に色を入れて、再度確認を行ってみましょう。
renderer.setClearColor(0xeeeeee, 1);
背景を設定すると3Dモデルの確認はできますが、表面は真っ暗で表面素材を確認することができません。それらの確認からThree.jsでのLight(ライト)の存在の重要性が理解できたかと思います。

Renderer(レンダラー)のアンチエイリアス設定
renderer(レンダラー)の設定の部分でantialias(アンチエイリアス)設定をtrueにしていました。アンチエイリアスを設定することで3Dモデルと背景の境界線を滑らかにする効果があります。設定を解除するとどうなるか確認してみましょう。
【アンチエイリアスあり】
/* renderer(レンダラー)の作成 */
var renderer = new THREE.WebGLRenderer({antialias: true});

アンチエイリアスあり
【アンチエイリアスなし】
/* renderer(レンダラー)の作成 */
var renderer = new THREE.WebGLRenderer();

アンチエイリアスなし
アンチエイリアスを設定することによって、境界線が滑らかになっていることがわかるかと思います。
まとめ
three.jsを触り始めた人にとってGeometryで3D描写を行うことは比較的簡単だが、実際の3Dモデルを描写し操作するのは非常に難しいと思っていた人もいたかと思います。しかし、objファイルさえ準備できれば、簡単に操作することができることが理解できたかと思います。