JavaScriptの3Dライブラリ”three.js”を使って遊んでみた

three.jsはブラウザ上で3次元のCGを表現することのできるJavaScriptライブラリで、WebGLやCanvasとの組み合わせが可能になっています。three.jsを使って遊んでみたので初歩的なことを書いておきます。

Three.jsの基礎

Three.jsに必須な6つのオブジェクト

Three.jsの主役になるのはレンダラ、シーン、カメラ、ジオメトリ、マテリアル、メッシュの6つ です。他にライト(光源)とかフォッグ(霧)とかもありますが、とにかくこの6つが必要不可欠です。それぞれの役割は次のようになっています。この役割をしっかり理解してください。

  • レンダラ

    3Dの物体を描画する領域のことです。レンダラにシーンとカメラを渡して、「描画してね」っていう命令を出すと渡されたシーンをカメラの視点から描画してくれます。またレンダラ自身がHTMLのcanvas要素をもっていて、このcanvas要素をHTMLの中に追加することで初めて3D世界がブラウザ上に描画されることになります。その意味でHTMLとJavaScriptを繋ぐ橋渡しになります。

  • シーン

    3D世界の舞台です。この中に3Dの物体や視点となるカメラを配置します。3Dの物体(メッシュ)を作成したらシーンに追加していきます。最終的にはカメラと一緒にレンダラに渡されて3D世界がブラウザ上に表示されます。

  • カメラ

    3D世界の中の視点を与えます。カメラを動かすと視点が動くので3D世界の中で動き回ることができます。カメラには「透視投影カメラ」と「平行投影カメラ」の2つがあります。このカメラに映っているものが最終的にブラウザ上に表示されます。

    1. 透視投影カメラ

      透視投影カメラというのは3次元の物体を見たとおりに2次元平面に描画するレンダリング手法です。透視投影カメラでは手前のものは大きく、奥にあるものは小さく映ります。いわゆる遠近法です。透視投影カメラでは画角やアスペクト比、手前のどこから、奥はどこまでを映すかの設定をします。

    2. 平行透視カメラ

      平行透視カメラでは近くのものも遠くのものも同じ大きさで映ります。例えば透視投影カメラで正立方体を見ると歪んで見えますが、平行透視カメラだと歪みなく正立方体だと判断できるように映ります。平行透視カメラでは前後左右をどこまで視線に入れるか、手前のどこから、奥はどこまで映すかを設定します。

  • ジオメトリ

    3Dの物体の形状です。どんな形をしているのか、例えば立方体であれば頂点の座標の情報などをジオメトリが担うことになります。

  • マテリアル

    3Dの物体の材質です。物体の表面のことだと思って大丈夫です。その表面の色や材質などなどの情報を担うのがマテリアルです。メッシュには色々あって、中には光源がないと真っ暗になるものもあります。下のコードで使ってるのは光源を必要としないメッシュです。

  • メッシュ

    形状であるジオメトリと材質であるマテリアルが合わさってメッシュになります。これが3Dの物体そのものになります。コードの中を見ると分かりますが、メッシュはジオメトリとマテリアルの2つを渡すことで作られます。作ったメッシュはシーンに追加することで3D世界の中に登場させることができます。

とりあえずなんか表示させてみる

説明はこれくらいにして、なんか表示させてみましょう。下のコードをindex.htmlとかって名前のファイルにコピペして保存してブラウザで開いてみてください。球体が表示されると思います。コードの中に説明が書いてあるので一体何をしているのかは中を読んでもらうのが早いと思います。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>球体を表示</title>
</head>
<body>
<!-- three.jsライブラリを読み込む -->
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r68/three.min.js"></script>
<script>
// ページをすべて読み込んだあとに関数の中を実行
window.addEventListener("load",function() {
 
    /*-------------------------------------------------------
      ここから変数の宣言と定義
    -------------------------------------------------------*/
 
    // レンダラ、シーン、カメラ、ジオメトリ、マテリアル、メッシュ用の変数を宣言
    var renderer, scene, camera, geometry, material, mesh;
 
    // 描画領域をHTMLに確保するためのコンテナ用変数
    var container;
 
    // 描画領域の幅と高さをウィンドウの幅と高さに設定
    var canvasWidth  = window.innerWidth;
    var canvasHeight = window.innerHeight;
 
 
    /*-------------------------------------------------------
      ここからレンダラ(描画領域)の生成と設定
    -------------------------------------------------------*/
 
    // レンダラの生成
    renderer = new THREE.WebGLRenderer( { alpha: true, antialias: true } )
    // WebGLをサポートしていない場合(何も表示されない場合)は上の行をコメントアウトして下の行のコメントアウトを外してください
    //renderer = new THREE.CanvasRenderer({ alpha: true } );
 
    // レンダラの大きさをウィンドウの幅と高さに設定
    renderer.setSize( canvasWidth, canvasHeight );
 
    // レンダラの背景色を白に、透明度を100%に設定
    renderer.setClearColor( 0xffffff, 0 );
 
    // div要素をつくってHTMLのbody要素内に追加し、その中にレンダラがもつDOM要素を追加
    container = document.createElement( 'div' );
    document.body.appendChild( container );
    container.appendChild( renderer.domElement );
 
    /*-------------------------------------------------------
      ここからシーンの生成
    -------------------------------------------------------*/
 
    // シーンの生成
    scene = new THREE.Scene();
 
    /*-------------------------------------------------------
      ここからカメラの生成と設定
    -------------------------------------------------------*/
 
    // 画角20度、画面アスペクト比はウィンドウのアスペクト比と同様、手前1、後方10000までを描画できる
    // 投資投影カメラ(遠近法的に映る)を生成
    camera = new THREE.PerspectiveCamera( 20, canvasWidth / canvasHeight, 1 , 10000 );
 
    // カメラの位置を座標(x,y,z)=(0,500,1000)に設定
    camera.position.set( 0, 500, 1000 );
 
    // カメラの向きを座標の原点(x,y,z)=(0,0,0)に設定
    camera.lookAt( { x:0, y:0, z:0 } );
 
    /*-------------------------------------------------------
      ここからジオメトリの生成
    -------------------------------------------------------*/
 
    // 半径150、経度分割数と緯度分割数が32の球体ジオメトリを生成
    geometry = new THREE.SphereGeometry( 150, 32, 32 );
 
    /*-------------------------------------------------------
      ここからマテリアルの生成
    -------------------------------------------------------*/
 
    // hexコードff00ffの色をもち、ワイヤーフレームを有効にしたマテリアルの生成
    // BasicMaterialは光源を必要としない
    material = new THREE.MeshBasicMaterial( { color: 0xff00ff, wireframe: true } );
 
    /*-------------------------------------------------------
      ここからメッシュの生成
    -------------------------------------------------------*/
 
    // ジオメトリとマテリアルを渡してメッシュを生成
    mesh = new THREE.Mesh( geometry, material );
 
    /*-------------------------------------------------------
      ここから描画
    -------------------------------------------------------*/
 
    // メッシュをシーンに追加
    scene.add( mesh );
 
    // レンダラにシーンとカメラを渡してブラウザの画面に描画
    renderer.render( scene, camera );
 
});
</script>
</body>
</html>

回転させてみる

表示させただけだと面白くありません。ということで回転させてみましょう。回転させるためには2つの方法があります。一つは球体そのものを回転させる方法です。物体を回転させるんだから回転します。当たり前です。もう一つは物体を固定したままでカメラを回転させる方法です。視点が変わるので「回転しているように見える」というわけです。シーンに追加するオブジェクトが多くなるとそれ全部を回転させるとなるとコードを書くのが大変ですが、カメラを回転させると数行で済みます。

ということでここではカメラを回転させてみましょう。ちなみにカメラを固定して球体自体を回転させるときは、camera.position.setの行とcamera.lookAtの行をmesh.rotation.y = theta;に書き換えてやるとOKです。

    // 回転角のための変数を宣言
    var theta = 0;
    // 回転アニメーション用の関数
    var rotationAnimation = function() {
        // カメラの位置をx-z平面で回転するように設定
        camera.position.set( 1000 * Math.sin( theta ), 500, 1000 * Math.cos( theta ) );
        // カメラを座標の原点に向き直させるように設定
        camera.lookAt( { x:0, y:0, z:0 } );
        // レンダラにシーンとカメラを渡してブラウザの画面に描画
        renderer.render( scene, camera );
        // 回転角を増やす
        theta += 0.01;
        // 回転角が360度を超えたらまたゼロに戻す
        theta %= 2 * Math.PI;
        // アニメーション用の関数を渡す
        // これで1秒間に60回描画される
        requestAnimationFrame( rotationAnimation );
    };
    // アニメーションを実行
    rotationAnimation();

このコードをコピーしてさっきのコードのrenderer.render( scene, camera );の後ろ、100行目に貼付けてください。下のような感じで回転すると思います。

コードの実行結果

参考ページ