WebGL無しiOSでも簡単軽快3D。three.jsとCSS3DRendererでDOMを3D化

Threejs

three.js CSS3D sample – periodic table

近年、各プログラミング言語による3Dライブラリの充実によりウェブの世界でも簡単に3D制作ができるようになってきましたが、肝心なコンテンツがまだ少ない印象を受けます。その原因の1つになっている(と思われる)のが、実装の難しさや手間だったりするわけですが、今回はhtmlのDOMを使って手軽に本格3D表現ができる方法をご紹介します。サンプルファイルも公開していますのですぐにお試しいただだけます。

すぐに試したい方へ
サンプルファイルとその使い方はこちらにあります。

  1. three.jsと機能拡張ライブラリ
  2. 事前に準備するもの
  3. 実装
  4. 動作確認
  5. 応用編(ページ埋込み)
  6. もっと詳しく知りたい方へ

three.jsと機能拡張ライブラリ

three.jsとは、基本的な3D操作が行えるjavascriptライブラリで、世界で最も利用者の多い3Dライブラリの1つです。また、機能拡張ライブラリも充実していてるので利用シーンも徐々に拡大しています。今回作成するサンプルでもその中のいくつか使っていきますので、まずはこれらの基本的な情報を紹介しておきたいと思います。

javascript 3Dライブラリ three.js

3Dを扱うための基本ライブラリ。本格的な3D計算のコアな部分はここが担っています。続けて紹介する機能拡張ライブラリもこれに依存しますので、インクルード必須という事になります。まずは、どんな事ができるのかざっと見てみたい!という方はオフィシャルサイトへどうぞ。(この記事でもオフシャルサイトを含めたリンク集を用意しておりますので、よろしければどうぞ)

Cameraをマウスで操作できるTrackballControls.js

three.jsでは3D表現の特徴でもあるCamera(僕は3Dの世界を覗く”穴”と解釈しています)も使えるので、オブジェクト自信の3D移動だけではなく、“視点”を移動してオブジェクト全体を見渡す事ができます。今回のサンプルではこのCameraをマウスアクションと連携させる事ができる機能拡張ライブラリTrackballControls.jsを使用します。

三軸座標の移動や回転アニメーションはTweenで

Tweenライブラリもthree.jsの機能を拡張できるライブラリで、x,y,z軸の移動と回転のアニメーションを容易に扱えるようにしてくれます。今回のサンプルではこの機能も組み込んでいきます。

「本格3D」と「DOMオブジェクト」の良いとこ取りしたCSS3DRenderer.js

three.jsとともに、今回この記事のメインを飾るのがこの拡張ライブラリです。その理由は「オイシイとこ取り」です。簡単ですが下の別枠でその背景について書いておきます。

three.jsで高速WebGLを使おう!でも…

WebGLとはウェブブラウザで3D表示させる為の標準仕様で、wikiによると2011年に初版が公開されています。これは、プラグイン不要でjavascriptを使った高速描画を可能にするもので、three.jsでも採用する等、急速に普及しつつあります。

ただし、iOS端末等、モバイル端末の多くがWebGLに未対応(2013年08月現在)なので、高速描画はほぼPCに限定されたものになります。(3D自体は扱うことができますが、WebGLに比べるとパフォーマンスが落ちてしまいます)

CSS transformならモバイルにも対応!でも…

CSS3からtransformプロパティが加わり、回転フリップ等、CSSだけでも簡単な3Dアニメーションを実現できるようになりました。更に、jQuery等のライブラリを使う事で、インタラクティブな実装も可能になり、簡単な仕様であれば、これだけで完結できるケースもあるかと思います。

ただし、先ほど紹介したCameraのような本格的な3D表現を実現できるものは少なく、多くの場合それらを制御する高度なプログラムを組む必要があるため、とたんに敷居が高くなってしまいます。

と、それぞれ一長一短なのですが、これら両方のオイシイところを活かすべく登場したのが、拡張ライブラリCSS3DRenderer.jsです。一言で言えば「モバイルフレンドリーなCSS transformをthree.jsの高度な3D演算機能で扱ってしまえる便利な奴」です。これからサンプルのご紹介に入りますが、短いコーディング量でも見応えのある3Dコンテンツを簡単に作成できてしまうことが分かるかと思います。

ちなみに

ページ最初に出てきた元素記号が輪になっている画像は、three.jsオフィシャルサイトに掲載されているCSS3DRenderer.jsを使ったサンプルの1つで、今回の記事を書くきっかけになったものです。PCと同等と言わずともモバイルでもそこそこ軽い3Dコンテンツが作れる方法が存在する事を知ったときは、(まだ実用には耐えられないか、、と思っていただけに)ちょっとした衝撃でした。QRコードも添えておきますので、よろしければモバイルでもアクセスしてみて下さい。↓

NewImage
three.js css3d – periodic table

ということで、前置きが長くなりましたが、今回はthree.jsとCSS3DRenderer.jsを組み合わせて、DOMを3D空間に配置し、さらにボタンアクションで3Dアニメーションさせる機能とマウスアクションでカメラを操作する合わせ技をご紹介します!

なんだかんだいろいろ複雑そうですが、今回作成するのは、htmlで30行程度、javascriptでも正味80行程度(サンプルではコメント入れまくって120行くらいになっていますが)で済んでしまいますよ!

事前に準備するもの

ライブラリ

今回必要なライブラリは下の5つです。最新版が必要な方はこちらからダウンロードして下さい。(今回の記事で紹介しているソース一式はこちらからダウンロードできますので、とりあえず確実に動くサンプルが欲しい、という事であればこちらからDLする事をお勧めします。)

ブラウザ

CSS transformが使えるCSS3対応のモダンブラウザが必要になります。webox blog様サイトで対応状況が確認できるサービスの紹介記事がありましたのでご紹介させて頂きます(有用な記事をありがとうございます!)。特定ブラウザについて対応しているかどうかを知りたい際にご活用下さい。

モバイルを含む各ブラウザのHTML5やCSS3などの対応状況を確認できる「When Can I use…」 | webox blog
HTML5やCSS3の対応状況を調べるには「findmebyip」がよく使われますが、こちらではCSS3のベンダープレフィックスの有無やiOS・Androidについてまでは調べられないようです。 そこ …

テキストエディタ

テキストが編集できるエディタであればなんでも結構です。最近は、ファイル保存のタイミングでブラウザを自動的にリロードさせる環境が便利すぎて、よく使っています。設定方法については過去の記事で紹介させてもらっていますので、よろしければどうぞ。

実装

ファイル構成

今回作成するサンプルのファイル構成一覧です。
内訳は上記で紹介したライブラリが5つ。追加ファイルは画像が1つ、html,css,javascriptも1つずつです。
先ほどライブラリを個別でダウンロードされた方は、下を参考にフォルダを作成し、ファイルを配置しておいて下さい。

js/jquery-1.10.2.min.js
js/three.min.js
js/controls/TrackballControls.js
js/libs/tween.min.js
js/renderers/CSS3DRenderer.js
js/app.js(今回作成分)
css/style.css(今回作成分)
index.html(今回作成分)
logo.png(今回作成分)

続けて今回作成分の4ファイルの解説です。

使用画像 : ファイル名 logo.png

今回は、バンクーバーのうぇぶ屋様よりこちらの画像をご提供頂きましたので、ドラッグ&ドロップや右クリック等で保存しておいて下さい。↓

Logo

メインページ : ファイル名 index.html

ここでは

  • スタイルの適用
  • javascriptライブラリのインクルード
  • メインプログラムのインクルード
  • 3D描画領域の設置
  • アニメーションを起動するボタン3つの設置

を行なっています。ソース内に簡単なコメントを入れてみましたので、参考にしてみて下さい。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="css/style.css" rel="stylesheet" type="text/css" /><!-- css -->
<title>CSS3d</title>
</head>

<script src="js/jquery-1.10.2.min.js"></script><!-- jquery -->
<script src="js/three.min.js"></script><!-- 3Dライブラリ -->
<script src="js/libs/tween.min.js"></script><!-- アニメーションライブラリ -->
<script src="js/controls/TrackballControls.js"></script><!-- マウスカメラコントローラ -->
<script src="js/renderers/CSS3DRenderer.js"></script><!-- CSSを使って3Dを扱うライブラリ -->

<body>
<div id="container"></div><!-- 描画領域(今回はブラウザ全画面を対象にしています) -->
<div id="webya"></div><!-- 今回アニメーションさせるオブジェクト -->

<!-- アニメーションボタン -->
<div id="menu">
<button id="a">ボタンA</button>
<button id="b">ボタンB</button>
<button id="c">ボタンC</button>
</div>

<script src="js/app.js"></script><!-- メインプログラム -->

</body>
</html>

スタイルシート : ファイル名 style.css

こちらは基本的なCSS構文なので、説明は割愛させて頂きます。

html,
body {
  height: 100%;
}
body {
  background-color: #f0f0f0;
  margin: 0;
  overflow: hidden;
}
#menu {
  position: absolute;
  left: 10px;
  bottom: 10px;
  width: 100%;
  text-align: center;
}
#webya {
  width: 918px;
  height: 184px;
  background-image: url(../logo.png);
}
button {
  font-size: 16px;
  padding: 10px;
  cursor: pointer;
}

メインプログラム : ファイル名 app.js

今回のメインパートです。コメント行を入れて120行くらいになりました。



var camera, scene, renderer;
var controls;
var object;

init();// 初期化
animate();// アニメーション開始


function init() {

    // シーン設定
    scene = new THREE.Scene();

    // カメラ設定
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 5000);
    camera.position.z = 1000;

    // #webya(うぇぶ屋ロゴ)を3Dオブジェクトにする
    object = new THREE.CSS3DObject(document.getElementById('webya'));

    // そのオブジェクトをシーンに追加
    scene.add(object);

    // CSS3Dレンダラー設定
    renderer = new THREE.CSS3DRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight); // 描画領域
    renderer.domElement.style.position = 'absolute'; // スタイル設定 {position:absolute}
    document.getElementById('container').appendChild(renderer.domElement); // #containerにレンダラーを追加

    // カメラコントローラー設定
    controls = new THREE.TrackballControls(camera, renderer.domElement);//レンダラーとカメラを関連付け
    controls.rotateSpeed = 0.5; // マウス感度設定
    controls.addEventListener('change', render); // 値が変わった(マウスで何か位置が変更された)ときに render() を呼び出す

    // 座標&回転設定 A
    var targetA = new THREE.Object3D();

    // 座標&回転設定 B
    var targetB = new THREE.Object3D();
    targetB.position.y = 300;
    targetB.rotation.x = 3.14;

    // 座標&回転設定 C
    var targetC = new THREE.Object3D();
    targetC.position.y = 100;
    targetC.position.z = 500;
    targetC.rotation.x = 1;
    targetC.rotation.y = 2;
    targetC.rotation.z = 3;

    // // #aがクリックされたら 設定A 方向にアニメーション
    $("#a").click(function () {
        transform(targetA, 1000);
    });

    // // #aがクリックされたら 設定B 方向にアニメーション
    $("#b").click(function () {
        transform(targetB, 1000);
    });

    // // #aがクリックされたら 設定C 方向にアニメーション
    $("#c").click(function () {
        transform(targetC, 1000);
    });

    //ウィンドウリサイズ時、onWindowResize()を呼び出す
    window.addEventListener('resize', onWindowResize, false);
}

function onWindowResize() {
    // カメラ設定
    camera.aspect = window.innerWidth / window.innerHeight; // カメラの縦横比を再設定
    camera.updateProjectionMatrix(); // 更新
    renderer.setSize(window.innerWidth, window.innerHeight); // 描画領域を再設定
}

function transform(target, duration) {

    TWEEN.removeAll(); // TWEEN処理が混在しないように一旦全て中止

    /*
    下記は座標と回転の2つに分けてアニメーション情報を設定し、
    start()でアニメーションを開始しています。
    */

    // 座標アニメーション処理
    new TWEEN.Tween(object.position) // object(webyaバナー)のposition情報を使って座標アニメーションさせますよ
        .to({ x: target.position.x, y: target.position.y, z: target.position.z }, duration) // x,y,z移動先と所要時間
        .easing(TWEEN.Easing.Exponential.InOut) // アニメーションパターン
        .start(); // 設定が住んだら開始!!

    // 回転アニメーション処理
    new TWEEN.Tween(object.rotation) // object(webyaバナー)のposition情報を使って回転アニメーションさせますよ
        .to({ x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, duration) // x,y,z回転と所要時間
        .easing(TWEEN.Easing.Exponential.InOut) // アニメーションパターン
        .start(); // 設定が住んだら開始!!

    // 描画処理
    new TWEEN.Tween(this)
        .to({}, duration)
        .onUpdate(render)
        .start();
}

/* ループ
requestAnimationFrameでフレームアニメーション化して
アニメーションとカメラコントロールを繰り返し更新しています。
*/

function animate() {
    requestAnimationFrame(animate); // three.js に内包されています。これにより繰り返し処理が実現できます。
    TWEEN.update(); // tweenアニメーション更新
    controls.update(); // カメラ位置更新
}

// TWEENでアニメーションする際に呼び出される
function render() {
    renderer.render(scene, camera);
}

ソースコードと被りますが、補足を兼ねて大まかな流れを書きだしてみました。

初期設定
  • 1.シーン(3D領域)を設定(12行目)
  • 2.シーンを見るための覗き穴になるcameraを設定(
  • 3.オブジェクト(ロゴ)を作成してシーンに配置
  • 4.CSS3Dさせる為のCSS3DRendererを設定
  • 5.cameraをマウス操作するための設定
  • 6.ボタンによるアニメーションでアニメーションの目的地となる3つの情報ABCを設定
  • 7.html内の#a,#b,#cのボタンクリックでアニメーションが起動するように紐付け
  • 8.ウィンドウサイズが変更された際に、表示枠を更新するためのリスナーの設定
  • 9.繰り返し処理(↓)スタート
繰り返し処理
  • 1.マウスアクションをひたすら待つ。(ボタンクリックされれば2.へ。ドラッグされれば3.へ)
  • 2.ボタンA,B,Cで予め紐付けした目的地へアニメーションさせる。(クリック時点で既にアニメーションしていた場合は中断して再スタート)
  • 3.マウス操作によりカメラ位置をリアルタイムに変更。
86-102行目の3つのTWEENについて

今回は分かりやすさ優先で、座標アニメーションと回転アニメーションと描画処理を別々の3つのTWEENで作成しましたが、全て同じタイミング同じ時間でアニメーションするので、1つにまとめる事ができそうです。その他コードの短縮やパフォーマンスの最適化等についてもまだ検討できる部分があるかと思いますので、その辺りはご自由にカスタマイズして頂ければと思います。(不具合等があれば教えて頂けるとうれしいです!)

動作確認

ファイル一式はこちらからダウンロードできます

ライブラリを含むサンプルファイル一式を用意しておきましたので、上のソースでうまく動作しないという方、ショートカットで来られた方はこちらから圧縮ファイルをダウンロードして適当な場所に解凍してください。

表示確認

単一のウェブページなので単純にindex.htmlをブラウザ表示させるだけでOKです。以下、簡単なコメント付き画像をご覧ください。

  1. 起動直後。正面にロゴ、下にABCボタンが並んでいます。
    0 start
  2. おもむろにボタンBを押してみると、クイッと逆立ちします。
    1 b
  3. 続けて、ボタンCを押します。グリっと3Dっぽい動きが見られます。
    2 c
  4. 最後にボタンAで初期位置に戻ります。
    3 a
  5. 続いてマウスで操作してみます。まずはホイール(タップ操作なら2本指の上下スワイプ)で、拡大縮小を確認。
    4 zoom
  6. ここでボタンBをクリック。ひっくり返りますが、拡大しすぎたのではみ出しました…
    5 zoom b
  7. 最後にマウスドラッグでグリグリと自由に回転してみます。このあたりで気づかれるかと思いますが、一度マウスで回転させた後、ボタンAを押しても、最初の表示には戻りません。これは、ボタンABCによる座標や回転の変更と、マウスドラッグによってカメラ(視点)が変更される事が別であることによるものです。なので、Aを押して定位置に戻ったとしてもカメラ位置が変更されていた場合は見え方が違ってきます。
    7 random2

以上です。パラメータを増やしたり変数化する等でアニメーションのパターンを増やすこともできますし、#webyaの中に普通にhtmlを入れて拡張することもできます。Youtube動画を若干斜めに、なんて事もできますので、お時間あれば試してみて下さい。

応用編(ページ埋込み)

冒頭でも触れましたが、コンテンツとしての3Dはまだまだ発展途上で、利用シーンについては開発の余地があります。ここでは実用するケースを想定し、iframeを使って3D枠をhtmlページヘ埋込む例ご紹介します。

iframeによる埋込み

といっても特に変わったことはしていません。単純に3Dコンテンツ枠を下の1行で埋め込むだけOKです。また、描画範囲の指定等、ソースを一部変更する必要はありますが、divを使って任意の場所に埋め込むこと可能です。


↓動きます

【注意】ドラッグできるコンテンツは1つだけにしておくのが無難

iframeでもdivを使う方法でも、複数配置する事で複数の3D枠を設置できるのですが、2つのiframeを使って埋め込んでiPadで表示させてみたところ、片方(html順で先に記述したオブジェクト)だけマウスドラッグに反応し、2つめは無反応でした。

これについてはまだ十分に調べきれていませんが、恐らく、trackballControls.jsのドラッグ系処理がお互い干渉しあっていると思われます。(違ってたらスミマセン。。)、より良い回避策を検討する余地はありますが、とりあえず、タッチデバイス対応で3D枠を複数設置する場合はドラッグ系アクションでコントロールできるものはページ内で1つだけにしておいた方が無難でしょう。

このあたりは明らかになり次第、紹介していければと思います。

いかがでしたでしょうか

3Dは読ませるよりも見せるコンテンツとして使われることが多いので、エンタテインメント系やインフォグラフィック等の分野で開拓していく事が今後のニーズを捉える意味でベターなのではないでしょうか。どこから始めて良いか分からずとっつきにくい3Dですが、この記事で少しでもその敷居が低くなれば幸いです。

今後テットリとしてもチャンスを見つけてサンプルもアップしていければと思います。

もっと詳しく知りたい方へ

3Dの分野は独自の専門用語が多い為、勉強すべき要素もいろいろありますが、ライブラリなどもうまく利用しながら自分に合った活用法を見つけ出してみてください。以下、参考になりそうな関連サイトを集めてみました。

本家サイト。ひと通りサンプルに目を通すだけでも勉強になります

three.js – JavaScript 3D library
Three.js is a lightweight cross-browser JavaScript library/API used to create and display animated 3D computer graphics on a Web browser.

three.js作者のサイト。有名なコンテンツもいくつか見つけられると思います。

Mr.doob | Obsidian

本家サイトを補足できそうな良サンプルがあります。

Three.js – examples
The goal of this collection is to provide a set of basic and instructive examples that introduce the …

three.jsのwikiがありました。日本語です。

three.js wiki – トップページ
three.jsについて分からんなりにまとめたりするページ。 最新リビジョンは r59。 three.jsとは JavaScriptで3DCGが簡単に動かせるやつ。 GPUに3DCGさせるやつ(O …


投稿日

カテゴリー:

投稿者:

タグ: