🐙

Three.js(WebGL)で一年以上学習した成果と便利なクラス/ライブラリを紹介

2021/12/04に公開

こんにちは、うえむーです。

今回はThree.js(WebGL)で一年以上勉強した成果と便利なクラス/ライブラリを紹介したいと思います。

Three.js(WebGL)とは?

Three.jsは、少しでも簡単にWebサイト上に3Dのコンテンツを表示するライブラリです。
ライブラリなしでカメラ・影・ライト・メッシュ実装する場合はそれなりの高度な技術が必要になりJavascriptのコード量が膨大になっていきます。
Three.jsのようなライブラリを導入することで、Javascriptの基礎と、Three.jsのクラスを利用することで気軽に3Dのコンテンツを表示することができます。

当初は、Three.jsを勉強したいけどまずどこから手をつければいいかわからず、公式ドキュメントを見ると英文で何をどうすればいいのかわからないかと思います。

Three.jsをこれから勉強するには、以下のサイトを見ることをお勧めします。初学者さんにもわかりやく説明してくれます。

https://ics.media/tutorial-three/

ある程度、Three.jsを学んだら「公式ドキュメント」を見て学習した方がいいかと思います。

https://threejs.org/

公式ドキュメントはサンプルがいくつか掲載されており、これはどのどうに実装したのかすごくワクワクします。

制作物

去年からThree.jsを学習していくつかの制作物を紹介していきたいと思います。

地球・月

こちらは、一番最初にcodepenで公開した制作物です。チュートリアルを参考して制作・公開しました。
公開してから1日経過した所、閲覧数が1000pvも超えました。

お月見

こちらは9/21にcodepenで公開した制作物です。地球・月の次にバズリPV数が700を超えました。

フォント

こちらは10月初旬にcodepenで公開した制作物です。書体を読み込ませて実装しました。Three.jsのどのクラスを利用したのかは後ほど説明します。

ハロウィン

こちらが10/31にcodepenで公開した成果物です。Javascriptのクラス構文を利用していくつかのパーツを分けて実装しております。これを完成するのに40H以上はかかりました。

Three.jsの基礎

実際に利用したクラス/ライブラリを紹介をする前にThree.jsの基礎をお話ししていきたいと思います。
最低限は以下を抑える必要があります。

・HTMLファイル作成、CDNを導入
・レンダラーを作成
・シーンを作成
・カメラを作成
・メッシュを作成
・光源設定

HTMLファイル作成、CDNを導入

まずは、HTMLファイル作成しCDNを導入する必要があります。head内にthree.jsのプラグインとjsの外部ファイルを準備してhtml内に埋め込んでください。
また、レンダリングするために必要なcanvas属性をbody内に埋め込んでください。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
    <canvas id="myCanvas"></canvas>
  </body>
</html>

レンダラーを作成

最初に、レンダリングするためにレンダラーを作成する必要があります。
クラスに「canvas」変数と3Dのコンテンツを出力したい要素を設定します。
次には、setPixelRatioというクラスを利用して解像度を設定します。
最後にレンダラーの幅・高さを設定します。今回は幅は950,高さ500とします。

const width  = 950
    , height = 500;

const renderer = new THREE.WebGLRenderer({
  canvas: document.querySelector('#myCanvas')
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);

シーンを作成

次に、メッシュをセットするためのシーン(ステージ)を用意します。
シーンは以下のように埋め込んでください。

// シーンを作成
const scene = new THREE.Scene();

カメラを作成

次に、カメラを作成します。カメラを作成しないとフロントには反映されません。
カメラを作成するためのコードは以下になります。

const camera = new THREE.PerspectiveCamera(画角,アスペクト比,ニア(near),ファー(far);

画角・・・視点からの角度
アスペクト比・・・縦横の比率
ニア(near)・・・区間の開始距離
ファー(far)・・・区間の終了距離

カメラの種類は2種類あり、先ほど紹介した「THREE.PerspectiveCamera」(遠近感)と他に「THREE.OrthographicCamera」(平行投影)があるみたいです。
自分は「THREE.OrthographicCamera」は利用したことがないので後ほど実装してみたいと思います。

メッシュを作成

次にメッシュを作成していきます。メッシュを設定するには大きく分けて3つあります。

ジオメトリ作成

・ジオメトリ(形状)を設定する(箱型、三角錐、円型、ドーナッツなど)
・ジオメトリ(形状)の幅・奥行き・高さを設定する

マテリアル作成

・マテリアル(質感)を設定する
・マテリアルのハイライト・カラー色を設定する

メッシュを設定・シーンに加える。

・設定したジオメトリ・マテリアルをメッシュのクラスに引数として渡す。
・設定したメッシュをシーンに渡す。

コードを記載すると以下のようになります。

const geometry = new THREE.BoxGeometry(400, 400, 400);
const material = new THREE.MeshNormalMaterial({color: 0xFFFFFF});
const box = new THREE.Mesh(geometry, material);
scene.add(box);

上記のようにコードを設定するとメッシュが反映されるようになります。

光源設定

最後は光源設定です。
影がなし1色で塗りつぶした「MeshNormalMaterial」や、ノーマルのカラーをRGBで可視化した「MeshBasicMaterial」の様なマテリアルは光源設定が不要ですが、光沢感が出る「MeshPhongMaterial」や「MeshStandardMaterial」には光源設定の設定が必要になってきます。光源設定をしないと、メッシュがシルエット状になります。

光源設定はいくつか種類があります。
環境光の「AmbientLight」、点光源の「PointLight」、方向光源の「DirectionalLight」など、いろんな種類のライトを目的によって使い分けます。
点光源の「PointLight」、方向光源の「DirectionalLight」でコードを書くと以下の様になります。


const directionalLight = new THREE.DirectionalLight(0xffffff);
scene.add(directionalLight);

const pointLight = new THREE.PointLight(0xffffff, 1, 10000);
scene.add(pointLight);

Three.jsの基礎を学習すれば簡単な3D形状のものが反映される様になります。

Three.jsの便利なクラス/ライブラリを紹介

実際に利用した便利なクラス/ライブラリを紹介していきたいと思います。これを利用することでワクワク感と利便性が上がるかと思います。

公式ドキュメントを確認すると、プラグイン・クラスが数え切れないくらい存在しており利用してないものが沢山あります。

クラス
https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene

プラグイン
https://github.com/mrdoob/three.js/tree/dev/examples/js

その中から気になったクラスを利用してみました。

OrbitControls

OrbitControlsとは?

OrbitControlsというのは、要素・属性を指定するだけでカメラをグリグリ動かせることができる便利なクラスです。
OrbitControls.jsは、Three.jsライブラリの本体に含まれていないので注意が必要です。CDNで利用するときは、以下のようにscript要素で読み込む必要があります。

<script src="https://unpkg.com/three@0.131.3/examples/js/controls/OrbitControls.js"></script>
OrbitControlsを利用して見る

OrbitControlsを利用して見ます。このコントローラーを利用するにはカメラ作成する必要があります。

const camera = new THREE.PerspectiveCamera(...); // カメラを作成

カメラを作成した後は、「OrbitControls」コントローラーを作成していきます。
「OrbitControls」の第二引数「要素の指定」ですが、カメラを操作する要素を指定します。

const controls = new THREE.OrbitControls(camera, 要素の指定); // コントローラーを作成

例:body要素・ID属性をそれぞれ指定してカメラ操作したい場合

//body要素を指定する
const controls = new THREE.OrbitControls(camera, document.body); 

// ID属性id属性を指定してカメラ操作したい場合
const controls = new THREE.OrbitControls(camera, document.querySelector('#myCanvas')); 

OrbitControlsを利用することで、以下のようなことが操作ができます。

・オービット(回転): 左ボタンでドラッグ
・ズーム(ピンチアウト・ピンチイン): マウスホイール
・パン: 右ボタンでドラッグ

FontLoader

FontLoaderとは?

FontLoaderとはフォントを読み込むクラスであり、FontLoaderにフォントの素材データを格納し、格納先を指定すればフォントを出力する事ができます。

FontLoaderを利用して実装して見る

実際にFontLoaderを利用してテキストを出力してみます。まずはFontの素材を格納して格納先を指定する必要があります。
three.jsのGithub状にフォントの素材が格納されているのでそちらからでも利用できます。

https://github.com/mrdoob/three.js/tree/master/examples/fonts

    const loader = new THREE.FontLoader();
    loader.load( 'https://threejs-plactice.vercel.app/fontloader/fonts/helvetiker_regular.typeface.json', function ( font ) {   
          const matLite = new THREE.MeshBasicMaterial( { color: 0x800080, transparent: true, opacity: 1, side: THREE.DoubleSide } ), // マテリアルを設定
                message = 'Happy',  // 出力する文字
                shapes = font.generateShapes( message, 100 ), // 文字の大きさを設定
                text_geometry = new THREE.ShapeGeometry( shapes ), // ジオメトリを設定
                text = new THREE.Mesh( text_geometry, matLite ); // textという変数を作成し設定したジオメトリ・マテリアルにメッシュのクラスに引数として渡す
          scene.add( text ); // シーンに作成したtextを引数として渡す。
        }
     );

そうすると、シーンにHappyという文字列が出力される様になります。

CubeTextureLoader

CubeTextureLoaderとは?

CubeTextureLoaderというのは、以下のような上下左右の計6枚の画像を用意し、CubeTextureLoaderのクラスを利用することでキューブ状な3Dコンテンツができたり、応用化としてガラス玉のようなものができます。

CubeTextureLoaderを利用して実装してみる。

まず、CubeTextureLoaderを実装する際に立方体の展開図の様な画像を計6枚用意する必要があります。パノラマ画像からキューブ画像に変換するツールがあるのでそちらを利用してキューブ画像に変換させます。

https://christinayan01.jp/architecture/archives/14067

変換した計6枚をcubeTextureLoaderに読み込んだものをtextureCubeという変数を作成し、
マテリアルに先ほど作成したtextureCubeを引数として渡します。
メッシュのクラスに先ほど作成したマテリアルとジオメトリを引数として渡してシーンに加えます。

        const cubeTextureLoader = new THREE.CubeTextureLoader()
        let textureCube = cubeTextureLoader.load( [
          './img/px.png', './img/nx.png',
          './img/py.png', './img/ny.png',
          './img/pz.png', './img/nz.png'          
        ] );
        const material = new THREE.MeshStandardMaterial( {
            color: 0xffffff,
            roughness: 0,
            metalness: 1,
            envMap: textureCube
        } );
        const sphere = new THREE.Mesh(
                        new THREE.SphereGeometry( 4, 160, 160 ),
                        material
                     );
        scene.add( sphere );

すると、以下のような実行結果になります。

dat.GUI

dat.GUIとは?

dat.GUIはJavascriptリアルタイムでパラメーター調整するライブラリです。
このプラグインを読み込んでパラメータの設定を行うだけで数値入力ができるフォーム画面ができあがります。
パラメーターの値の変化に応じて処理を組み込むことができるため、フォームで設定したパラメータの値に応じて画面を変化させることができます。
光源設定・影・コントラストの設定を態々コードを変更・フロント確認といった手間のかかる作業をしなくて済みます。

data.GUIを実装してみる。

data.GUIを利用して実装してみます。まず最初に、head内にthree.jsのライブラリと、dat.gui.min.jsのライブラリを導入します。


<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.6/dat.gui.min.js"></script>
    <script src="js/index.js"></script>
  </head>
  <body>
  </body>
</html>

htmlでライブラリ導入完了したらjsでシーン作成をして、GUIパラメータを加えます。

window.addEventListener('load', init);

function degree(degrees) {
  return degrees * (Math.PI / 180);
}

function init() {

  var scene = new THREE.Scene();

  var renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

  var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight);
  camera.position.set(0, 0, 50);
  scene.add(camera);

  light = new THREE.DirectionalLight(0xffffff,1);
  scene.add(light);

  light.position = new THREE.Vector3(0, 0, 0);
  ambient = new THREE.AmbientLight(0xffffff);
  scene.add(ambient);

  var geometry = new THREE.BoxGeometry(5,5,5);
  var material = new THREE.MeshStandardMaterial({color: '#a42c2c'});
  var box = new THREE.Mesh( geometry, material);
  box.position.set(0,0,0);
  box.rotation.set(degree(0),degree(0),degree(0));
  scene.add(box);

  // GUIパラメータ
  var guiCtrl = function(){
    this.position_x = 0;
    this.position_y = 0;
    this.position_z = 50;
    this.rotation_x = 0;
    this.rotation_y = 0;
    this.rotation_z = 0;
  };

  gui = new dat.GUI();
  guiObj = new guiCtrl();
  var folder = gui.addFolder('Folder');
  folder.add( guiObj, 'position_x', 0, 100 ).onChange(setCameraPosition);
  folder.add( guiObj, 'position_y', 0, 100 ).onChange(setCameraPosition);
  folder.add( guiObj, 'position_z', 0, 100 ).onChange(setCameraPosition);
  folder.add( guiObj, 'rotation_x', 0, 180 ).onChange(setCameraRotation);
  folder.add( guiObj, 'rotation_y', 0, 180 ).onChange(setCameraRotation);
  folder.add( guiObj, 'rotation_z', 0, 180 ).onChange(setCameraRotation);
  folder.open();

  function setCameraRotation(){
    box.rotation.set(
      degree(guiObj.rotation_x),
      degree(guiObj.rotation_y),
      degree(guiObj.rotation_z)
    );
  }

  function setCameraPosition(){
    box.position.set(
      guiObj.position_x,
      guiObj.position_y,
      guiObj.position_z
    );
  }

  // レンダリング
  var render = function() {
    requestAnimationFrame(render);
    renderer.render(scene,camera);
  }
  render();

}

これで、以下の様にGUIパラメータが表示され座標の設定ができる様になりました。

最後に

Three.jsのライブラリを導入し参考記事に参照すると簡単に実装できました。
公式ドキュメントを見ると3桁以上のクラス・ライブラリが存在しているので、気になるのがあったらどんどん触ってみたいと思います。
WebGL導入してるサイトはまだまだ少なく、自分もこの業界をやって8年以上ですが、WebGLの実装は一度もありません。いつかは現場でWebGLを実装していきたいと思います。

参考記事

https://threejsfundamentals.org/threejs/lessons/ja/

https://ics.media/tutorial-three/

Discussion