Web上で3D表現を!WebGLのリッチな表現を短時間でお手軽に作る方法

Web上で3D表現を!WebGLのリッチな表現を短時間でお手軽に作る方法

セイト

セイト

こんにちは。LIGフィリピン支社代表のせいと(@seito_horiguchi)です。

最近セブ島でしょうもないことしてる記事ばっかり書いていたので、たまには真面目に技術系の記事を書きたいと思います。というわけで、今回はテーマはWeb上で3D表現を行う技術WebGLについて。

もくじ

今回の記事でやること

WebGLおよびThree.jsの概要・基本的な実装方法から知りたいという方は、エンジニアのはっちゃん氏が以下にまとめているのでこちらをご覧になってください(´ω`)っ

Three.jsを使えばWeb上でも3D表現が実装できますが、凝ったものを作ろうと思うと正直けっこう難しいです……。でも! なんかこう、初音ミクみたいなキャラクターとか街や草原といったフィールドなど出してみたい、と思うじゃありませんか。

というわけで、今回はやってみたのがこちら↓

デモ
https://seito2014.github.io/three-tutorial/city-study/

GitHub

市街地の3Dモデルを描画するサンプルです。

ただ、ごめんなさい。

尺の関係上これ全部解説するのは長くなっちゃうので、今回はこちらの静的なデモの実装方法を主に解説します。アニメーションやインタラクティブな表現は次回、Vol.2にて解説できればと思います。

順番に解説していくので、自分で実装してみたいエンジニアさんやデザイナーさんのほか、WebGLを利用して何かしらのプロダクト制作を検討中の担当者さんもぜひ参考にしてみてください。

Three.jsに対応した3Dモデル素材を用意する

さて、まずはWeb上で描画する3Dモデルの素材を用意しましょう。ただしThree.jsを使う場合はサポートされている形式の素材を用意する必要があります。Three.jsが対応しているフォーマットは以下の通り。

Three.jsがサポートしているフォーマット
JSON, OBJおよびMTL, Collada, STL, CTM, VTK, AWD, Assimp, VRML, Babylon, PDB, PLY, MMD, dae

けっこう多いですね! 僕が知ってる限りで一部だけ解説しますと……。

OBJおよびMTL 採用されているケースが多いフォーマットの1つ。
OBJとMTL2つからなるファイルを組み合わせて使う。
(ちなみに今回のデモではOBJおよびMTLの素材を使っています。)
Collada OBJおよびMTLに並んで採用されているケースが多いフォーマットの1つ。
STL 3Dプリンターの出力で主に採用されているフォーマット。

ざっとこんな感じです。ちなみに、みんな大好きニコニコ動画でおなじみのMMDもThree.jsで描画可能のようです。僕はまだ試していないのでその話はまた次回にでも。

3Dモデル素材の入手・作成方法

Three.jsではたくさんの形式がサポートされていることはわかりました。でも、それはどうやって用意すればいいのか? 方法は大きく分けて3つあります。

  1. 自分で作る
  2. 制作会社に依頼する
  3. 素材サイトで買う

1. 自分で作る

自分で作る場合、方法は「Three.jsのコードでつくる」か「3Dモデル作成用のアプリでつくる」のどちらかです。前者の場合、冒頭でご紹介したはっちゃん氏の記事で言っているように、Three.jsが用意している関数を使って実装します。ただし、こちらは単純なオブジェクトを作る場合は効率的ですが、複雑なものを作るには向いていません。

より高度なものを作る場合は、それ専用のアプリケーションを使う必要があります。有名どころでいえばMAYAとかCOLLADA辺りでしょうか。MAYAは有料ですがCOLLADAはフリーで使用できるので、すぐに試してみることが可能です。

COLLADA公式
MAYA公式

2. 制作会社に依頼する

自分で作るのが困難な場合、プロの制作会社ないしクリエイターに依頼する手があります。ただし、やっぱりそれなりのお値段はします。

何を作るかにもよるので一概には言えませんが、数十万〜数百万は大体かかるとのこと。ひとつ言えるのは安くはない、ってことですね。デザインの難易度やポリゴン数、ポージングや表情のバリエーションの量に応じて変わるようです。「予算はあるからクオリティの高いオリジナルのものを作りたい」という場合にいいかもしれません。

3. 素材サイトで買う

一番お手軽なのはこれですね。ただし望みどおりの素材があるかどうかは素材サイト次第なのと、ライセンスがサイトや作者によってはマチマチなので気をつけましょう。フリー素材のサイトの場合は無料、有料の場合ひとつ数千円〜数万円で購入できます。

tf3dm 海外のフリー素材サイト。
かなり豊富でクオリティも高い。
無料
ニコニ立体 ドワンゴさんが展開するニコニコ動画のコーナーの1つ。
MMD形式でアニメ系の素材が多い。
無料
amana images | 3Dモデリング素材 写真素材で有名なアマナさん。
3Dモデリングの素材も豊富です。
有料
cgtrader 海外のサイトですが、種類の豊富さが広いです。 有料
cgstud こちらも海外サイト。
乗り物と建物の素材が特に多いです。
有料

ちなみに今回のデモではtf3dmさんにあるこちらの素材を使わせていただきました。

ダウンロードしてきた中身を開くとこんな感じ。

city

色々入っていますが、今回使用するのはcity.mtl, city.objとMaps/以下のファイルです。Maps/の中に入っているのはテクスチャと呼ばれるファイル群で、3Dモデルの表面を装飾するためのものです。

さて、次の章から実際の実装方法を見ていきましょう。

Three.jsコーディング

Loaderファイルを使う

3DモデリングをThree.jsで描画させるには、それぞれのフォーマットに合わせた「Loader」と呼ばれるファイルが必要です。LoaderはThree.jsを公式からダウンロードすると、同じファイル内の“three.js-master/examples/js/loaders/に入っています。場所めっちゃわかりにくいですね!

loaders

ここにxxxLoader.jsというファイルがたくさんあるので、3D素材のフォーマットに合わせてLoaderファイルを選びましょう。今回はOBJおよびMTL形式のファイルを描画するので、OBJLoader.jsとMTLLoader.jsを使用します。Three.jsと一緒にhtmlファイル上に読み込みましょう。

index.html

<!doctype html>
<html>
	<head>
	  <meta charset="utf-8">
	</head>
	<body>
	<div id="js-WebGL"></div>
	<script src="./assets/lib/three.min.js"></script>
	<script src="./assets/lib/OBJLoader.js"></script>
	<script src="./assets/lib/MTLLoader.js"></script>
	<script src="./assets/js/app-dist.js"></script>
	</body>
</html>

htmlはざっとこんな感じです。ちなみに#js-WebGLは描画用のDOMを生成するためのhtml, app-dist.jsはこれから描画のためのコードを記述するためのJSファイルです。

app.js

続いてapp.jsの中身です。
※ES6で記述しています。こちらをアプリやタスクランナーなどでES5に変換したものがapp-dist.jsです。

let init = () => {

    //get window size
    let windowData = {
        width: window.innerWidth,
        height: window.innerHeight
    };

    //create scene
    let scene = new THREE.Scene();

    //create spotlight for the shadows
    let spotlight = new THREE.SpotLight(0xffffff);
    spotlight.position.set(600, 600, 600);
    spotlight.castShadow = true;
    spotlight.intensity = 2;

    scene.add(spotlight);

    //load OBJ,MTL files
    let mtlLoader = new THREE.MTLLoader();
    mtlLoader.setTexturePath( './assets/obj/city/' );
    mtlLoader.setPath( './assets/obj/city/' );
    mtlLoader.load( 'city.mtl', function( materials ) {

        materials.preload();

        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );
        objLoader.setPath( './assets/obj/city/' );
        objLoader.load( 'city.obj', function ( loadedMesh ) {

            //arrange the location
            loadedMesh.rotation.x = -0.3;
            loadedMesh.rotation.y = 0;
            loadedMesh.rotation.z = 0;
            loadedMesh.position.x = 500;
            loadedMesh.position.y = 250;
            loadedMesh.position.z = 1000;
            scene.add(loadedMesh);

        });

    });

    //create camera
    let camera = new THREE.PerspectiveCamera(
        45, windowData.width / windowData.height, 0.1, 3000
    );
    camera.position.x = 0;
    camera.position.y = 200;
    camera.position.z = 400;
    camera.lookAt(new THREE.Vector3(0, 0, 0));

    //create render
    let renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(new THREE.Color(0xEEEEEE));

    renderer.setSize(windowData.width, windowData.height);
    renderer.shadowMap.enabled = true;
    renderer.setPixelRatio(window.devicePixelRatio);

    // add the output of the renderer to the html element
    document.getElementById('js-WebGL').appendChild(renderer.domElement);

    // call the render function
    let renderScene = () => {
        requestAnimationFrame(renderScene);
        renderer.render(scene, camera);
    };
    renderScene();

};

window.onload = init();

この内、ほとんどの部分は冒頭で述べたはっちゃん氏の記事をご覧いただけばわかるかと思います。今回重要なのは20-44行目と66-71行目です。

20-44行目
//load OBJ,MTL files
    let mtlLoader = new THREE.MTLLoader();
    mtlLoader.setTexturePath( './assets/obj/city/' );
    mtlLoader.setPath( './assets/obj/city/' );
    mtlLoader.load( 'city.mtl', function( materials ) {

        materials.preload();

        let objLoader = new THREE.OBJLoader();
        objLoader.setMaterials( materials );
        objLoader.setPath( './assets/obj/city/' );
        objLoader.load( 'city.obj', function ( loadedMesh ) {

            //arrange the location
            loadedMesh.rotation.x = -0.3;
            loadedMesh.rotation.y = 0;
            loadedMesh.rotation.z = 0;
            loadedMesh.position.x = 500;
            loadedMesh.position.y = 250;
            loadedMesh.position.z = 1000;
            scene.add(loadedMesh);

        });

    });

こちらはOBJとMTLファイルを読み込むための記述です。
OBJとMTLファイルまでのパスを指定しています。
また、.rotationはモデリングの角度、.positionはモデリングの描画位置を操作しています。

66-71行目
// call the render function
    let renderScene = () => {
        requestAnimationFrame(renderScene);
        renderer.render(scene, camera);
    };
    renderScene();

こちらはレンダリング(描画)のための記述です。renderer.render(scene, camera);はどんな簡単な実装でも出てくる関数ですが、これに加えて今回はrequestAnimationFrame関数を使用し、再帰関数として使用しています。

requestAnimationFrameは「描画処理を繰り返し実行する」ためのもので、今回のWebGLのような表現以外にも、Canvasを使ったアニメーションでは欠かせない関数です。今回のデモは静的な描画しか行っていませんが、3Dモデリングを描画する際には必要になります。

まとめ

3D表現の敷居は高そうですが、フリー素材や市販の素材を描画させてちょっといじるだけであれば、Three.jsで簡単に実装することができます。3DのWeb表現を検討中の方はぜひ参考にしてみてください。

また、今回はオライリー・シャパンさんより出版されている『初めてのThree.js/Jos Dirksen著, あんどうやすし訳』を参考にさせていただきました。興味ある方はこちらもぜひ!

初めてのThree.js 第2版 ―WebGLのためのJavaScript 3Dライブラリ

初めてのThree.js 第2版 ―WebGLのためのJavaScript 3Dライブラリ

  • 著者Jos Dirksen
  • 価格¥ 4,320(2017/01/09 18:21時点)
  • 出版日2016/07/23
  • 商品ランキング106,370位
  • 単行本(ソフトカバー)416ページ
  • ISBN-104873117704
  • ISBN-139784873117706
  • 出版社オライリージャパン

LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。

Webサイト制作の実績・料金を見る

この記事のシェア数

セイト
セイト Ex-President & VPoE at LIG Philippines. / 堀口 セイト

現在はミネルバ大学院に在籍しつつ、SNS総フォロワー数11万人を誇るエンジニアコンサルタントとして活躍中。 初代ポケモンで最初に選ぶならゼニガメ。でも本当に好きなのはフシギダネ。

このメンバーの記事をもっと読む
それいけ!フロントエンド | 213 articles