Monacaで始めるthree.js 第1回 ~3Dモデルを表示する~

こんにちは、Monacaチームで3Dモデリングを担当している浦本です。

three.js( http://threejs.org/ )とは、ブラウザベースでリアルタイムな3Dグラフィックス描画を可能とするJSライブラリです。FlashやUnityなどの外部プラグインに依存せずにWebGLを利用して描画が行われるため、HTML5アプリに手軽に3Dコンテンツを組み込むことができます!今回はMonacaを用いて、three.jsをモバイルアプリで利用してみたいと思います。

■デモ

今回作るのは3Dモデルファイルを読み込んで表示するだけの簡単なサンプルアプリです。
マウスを動かすと、Monaca公式キャラクターであるモナカマンがぬるぬる動きます。

■動作環境

WebGLの動作環境として、AndroidアプリであればAndroid 4.0以上、かつCrosswalkを有効にしていることが必要です。MonacaアプリでCrosswalkを有効化するためには、クラウドIDEのAndroidアプリ設定画面から、WebViewエンジンとしてCrosswalkを選びます。これに対応するMonacaデバッガーとして「ハイパフォーマンス版」の方をお使いください。また、iOSアプリであればiOS 8以上が動作環境となります。

■基礎用語の説明

three.jsを利用するとWebGLの複雑なAPI群を操作する必要がなく、three.jsが提供するクラスを用いて簡単に3Dオブジェクトの作成や描画を行うことができます。ここでは第一歩として、基礎用語を説明します。

(重要用語)
・シーン: 描画したい3Dオブジェクト(キャラクターから基本図形まで何でも)の配置先となる、仮想的な空間。
・ライト: オブジェクトを照らしだすライト。初期状態だとシーンは真っ暗なので1つ以上必要。
・カメラ: シーンを覗きこむカメラ。実際のカメラのように位置や向き、フォーカスを設定できる。
・レンダラー: シーンとカメラを指定することで、実際のレンダリングを行ってくれる。

(補足)
・位置: X,Y,Z座標からなる。three.jsの場合は、+Xが右方向、+Yが上方向、+Zが手前方向。
・3Dオブジェクト: Object3Dクラスが基底となっていて、ジオメトリとマテリアル情報を持つ。
・ジオメトリ: 3D形状。頂点、2点からなる辺、3点以上からなる面 で構成される。
・マテリアル: 表面材質。面がどのような色で描画されるかを決定づける。

■サンプルコード

Monacaのダッシュボードから「Add New Project」→「Import Project」を開き、サンプルコードのURL( https://github.com/uramoto/monaca_threejs01 )を指定してプロジェクトをインポートしてください。

●www/index.html


<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
    <script src="components/loader.js"></script>
    <script src="js/vendor/three.js"></script>
    <script src="js/vendor/MTLLoader.js"></script>
    <script src="js/vendor/OBJMTLLoader.js"></script>
    <script src="js/app.js"></script>
    <link rel="stylesheet" href="components/loader.css">
    <style>
        body {
            background-color: #000;
            margin: 0px;
            overflow: hidden;
        }
    </style>
</head>
<body>
</body>
</html>

(説明)
three.jsの基本クラス群はthree.jsを読み込むだけで利用できるのですが、APIドキュメント( http://threejs.org/docs/ )に書いてあるクラスが見つからないことがたまにあります。そういった場合はあせらずに、上記のように必要なクラスファイルだけ読み込みます。

3Dモデルファイルを読み込むためには、各形式に対応したTHREE.Loaderの子クラスが必要です。例えば、独自形式であるJSON形式( https://github.com/mrdoob/three.js/wiki )に対応したTHREE.JSONLoaderや、アニメーションを含むCollada形式に対応したTHREE.ColladaLoaderクラスが用意されています。

今回読み込む静止モデルのファイル形式はWavefront(.obj + .mtl)形式なので、THREE.OBJMTLLoaderクラスを用いています。そのため、OBJMTLLoader.js と MTLLoader.js をこちら( https://github.com/mrdoob/three.js/tree/master/examples/js/loaders )から入手して読み込んでいます。

●js/app.js


// 描画領域のサイズを決める。今回の場合、画面全体に広げる。
var viewWidth =  document.documentElement.clientWidth;
var viewHeight = document.documentElement.clientHeight;
var scene,     // オブジェクトの配置先となるシーン
    camera,    // オブジェクトを捉えるカメラ
    renderer,  // 実際の描画を担当するレンダラー
    chara,     // 表示する3Dキャラクター
    mouse = { x: 0.5, y: 0.5 }   // マウス位置を0,0から1,1までで表現(three.jsとは関係なし)
    ;
// 初期化処理
document.addEventListener("DOMContentLoaded", init, false);
function init() {
    // 空のシーンを作成
    scene = new THREE.Scene();
    
    // カメラの各種設定
    camera = new THREE.PerspectiveCamera(30, viewWidth / viewHeight, 0.1, 1000); 
    camera.position.z = 180;
    camera.position.y = 40;
    
    // シーン全体に広がる環境光を追加
    var light = new THREE.AmbientLight(0xBBBBBB);
    scene.add(light);
    
    // レフ板として点ライトを追加
    var pointLight = new THREE.PointLight(0xFFFFFF, 0.9, 1000);
    pointLight.position.set(100, 0, 200);
    scene.add(pointLight);
    // レンダラーを作り、描画先のDOMエレメントをbody直下に置く
    renderer = new THREE.WebGLRenderer();
    renderer.setSize(viewWidth, viewHeight);
    document.body.appendChild( renderer.domElement );
    
    // OBJ・MTLファイルを読み込み開始
    var objLoader = new THREE.OBJMTLLoader();
    objLoader.load("obj/model.obj", "obj/model.mtl", function(model) {
        // 大きさを調整し、へその位置が地面に来るようにする
        model.scale.set(0.1, 0.1, 0.1);
        model.position.y = -40;
        // モデルをダミーオブジェクトで包む(へそ中心に回転したいため)
        chara = new THREE.Object3D();
        chara.add(model);
        // シーンへ追加する
        scene.add(chara);
        // カメラ方向をオブジェクトへ向ける
        camera.lookAt(chara.position);
        
        doRender();  // 描画開始!
    });
    // タッチやマウス操作のイベント登録
    window.addEventListener("touchmove", onTouchMove, false);
    window.addEventListener("mousemove", onMouseMove, false);
}
// 描画フレーム毎に呼び出される関数(最高で60FPS)
function doRender() {
    // おまじない
    window.requestAnimationFrame(doRender);
    // キャラのY軸・X軸の回転角度を決める。すこし背中側まで回転させたいので1.2を掛けている。
    chara.rotation.y = 1.2 * (-0.5 + mouse.x) * Math.PI;
    chara.rotation.x = 1.2 * (-0.5 + mouse.y) * Math.PI;
    
    // 徐々に正面を向かせたいので、勝手にマウス位置を中央に近づける
    mouse.x += (0.5 - mouse.x) * 0.05;
    mouse.y += (0.5 - mouse.y) * 0.05;
    // シーンを描画
    renderer.render(scene, camera);
}
// タッチイベントを拾う
function onTouchMove(ev) {
    mouse.x = event.changedTouches[0].pageX / screen.width;
    mouse.y = event.changedTouches[0].pageY / screen.height;
    ev.preventDefault();  // スクロール防止
}
// PCでの確認用
function onMouseMove(ev) {
    mouse.x = event.clientX / viewWidth;
    mouse.y = event.clientY / viewHeight;
}

(説明)
・init()関数でシーン、カメラ、ライトを設定し、3Dモデルの読み込みを開始します。
・3Dモデルの読み込みが完了するとコールバックが呼ばれるので、モデルをシーンへ追加し、描画を開始します。
・描画関数doRender()では、レンダラーのrender()メソッドを呼び出す前にモデルの回転角度を調整しています。マウスの左右の動きをY軸回転に、上下の動きをX軸回転に対応させています。先頭で window.requestAnimationFrame() を呼び出していますが、これはおまじないなので気にしないてください。描画関数の名前は自由ですが、形式は以下のとおりです。楽ちんですね。


function doRender() {
    window.requestAnimationFrame(doRender);  // おまじない
    /***** ここで何かシーンに変化をつける *****/
    renderer.render(scene, camera);  // シーンを描画
}

今回のサンプルコードを応用すれば、例えば3Dモデルのカタログアプリが簡単に作れてしまいます。
皆さんも是非、Monacaでthree.jsにチャレンジしてみてください!

お知らせ

Monacaチームでは現在、新規開発メンバーを大募集中です。詳しくはこちらを参照ください!
https://ja.monaca.io/careers.html