ENGINEER BLOG

ENGINEER BLOG

Babylon.jsを使って、こくりを作ってみる

エンジニアブログをご覧いただきありがとうございます。
イノベーション本部の石川です。
今回初めてエンジニアブログの記事を投稿させていただきます。

社内で実施される1on1ミーティングで、Babylon.jsという面白そうなライブラリを教えていただきましたので、触ってみたいと思います。

Babylon.jsとは

Babylon.jsとは、HTMLのcanvasを拡張してくれるJavascriptのライブラリです。
(他にもcanvasを拡張するJavascriptのライブラリをエンジニアブログで紹介しています。こちらも合わせてご覧ください!)
Babylon.jsを使うことで、簡単に3Dのモデルをブラウザに描画することができます。
また、Playgroundという簡単にBabylon.jsを体験できるページがあるのもポイントです。
詳しく知りたい方は、公式ドキュメントをご覧ください。

実際にやってみます

今回は簡単に弊社のこくりの頭部分をブラウザに3Dモデルとして表示させます。
Blender等と異なり、コードベースになるため、最初は戸惑うと思います。
最後に詰まった部分も記載しておくので、参考にしてください!

簡単なhtmlとCSSを準備

まずは最低限のhtmlとCSSを用意します。
cdnでbabylon.jspep.jsを読み込みます。
※pep.jsが無くても動作はしますが、スマホ等での動作のために読み込んでおきます。

engineerblog.html

<html>
    <head>
        <!-- babylon.jsへのリンク -->
        <script src="https://preview.babylonjs.com/babylon.js"></script>
        <!-- babylon.jsが依存しているpep.js のリンク -->
        <script src="https://code.jquery.com/pep/0.4.1/pep.js"></script>
        <!-- 今回作成するもの -->
        <script type="text/javascript" src="engineerblog.js"></script>
        <link rel="stylesheet" type="text/css" href="engineerblog.css">
    </head>
    <body>
        <canvas id="canvas"></canvas>
    </body>
</html>

engineerblog.css

html, body {
    width: 100%;
    height: 100%;
    overflow: hidden;
}
  
#canvas {
    width   : 100%;
    height  : 100%;
    touch-action: none;
}

jsを何も書かない状態だと何も表示されないと思います。

シーンを準備する

htmlとCSSが準備できたら、早速Babylon.jsに触れていきます。
最初にcanvas要素を取得し、engineの生成を行います。
その後、取得したcanvas要素とengineを使い、シーンと呼ばれるものを作成します。

function main() {
    // canvasを取得する
    const canvas = document.getElementById('canvas');
    // エンジンを生成
    const engine = new BABYLON.Engine(canvas);
    // シーンを作成
    const scene = createScene(engine, canvas);

    // このあたりにモデリング用のコード関数を記述する
    
  
    // 描画ループ関数を定義する
    engine.runRenderLoop(() => {
      scene.render();
    });
}

シーンの作成方法(createScene関数)について詳しくやっていきます。
シーンの作成の際は、カメラや照明などを設定する必要があります。

// シーンを生成(カメラ・光源・地面等を設定)
function createScene(engine, canvas) {
  // This creates a basic Babylon Scene object (non-mesh)
  let scene = new BABYLON.Scene(engine);

  // new ArcRotateCamera(name: string, alpha: number, beta: number, radius: number, target: Vector3, scene?: Scene, setActiveOnSceneIfNoneActive?: boolean)
  // alpha: 縦軸を基準とした角度位置
  // beta: 横軸を基準とした角度位置
  // radius: どれくらいズームするか(マウスホイールで操作可能)
  // target: カメラを常に向ける点
  const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 30, new BABYLON.Vector3(0, 0, 0), scene);
  camera.attachControl(canvas, true);

  // ライトを設定
  let light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
  light.intensity = 0.7;

  // 作成したシーンを返す 
  return scene;
}

カメラオブジェクトを生成する際に、カメラの位置を設定します。
今回は、中心から手前に斜め上方向にカメラを置いています。
ライトについても、引数でライトオブジェクトをどこに設置するかを設定する必要があります。
迷ったらPlaygroundのコードをコピペして、動かして見るのがよいと思います。
このシーン上に作成した3Dモデルが表示されます。

モデルを作成する

シーンが作成できたら、いよいよモデリングです。
作成するこくりの頭の構成要素を簡単に分けると以下のようになります。

  • 直方体×1
  • 半球×2

最初に直方体の部分を作っていきます。
目等のパーツをモデリングするのが大変なので、以下の画像を直方体に貼り付けようと思います。

    // マテリアルを定義して、画像を設定する
    let mat = new BABYLON.StandardMaterial("mat", scene);
    let textureImage = new BABYLON.Texture("cocreTexture.png", scene);
    mat.diffuseTexture = textureImage;

    // 貼り付けたい画像を分割する
    const columns = 10;
    const rows = 1;
    let cocreTexture = new Array(6);
    cocreTexture[0] = new BABYLON.Vector4(3 / columns, 0, 5 / columns, 1 / rows);
    cocreTexture[1] = new BABYLON.Vector4(1 / columns, 0, 3 / columns, 1 / rows);
    cocreTexture[2] = new BABYLON.Vector4(0 / columns, 0, 1 / columns, 1 / rows);
    cocreTexture[3] = new BABYLON.Vector4(9 / columns, 0, 10 / columns, 1 / rows);
    cocreTexture[4] = new BABYLON.Vector4(7 / columns, 0, 9 / columns, 1 / rows);
    cocreTexture[5] = new BABYLON.Vector4(5 / columns, 0, 7 / columns, 1 / rows);

    // 直方体を生成
    let cocre = BABYLON.MeshBuilder.CreateBox("box", {height: 3, width: 6, depth: 3, faceUV: cocreTexture, wrap: true}, scene);
    // 直方体にマテリアルを付ける
    cocre.material = mat;
    // 中心から少し上に上げる
    cocre.position = new BABYLON.Vector3(0, 3, 0);

faceUVプロパティを使って、直方体に画像を割り振ります。
このfaceUVプロパティの配列は、それぞれの要素にどの面に貼るかが割り振られています。

配列のインデックス 貼られる場所
0 背面
1 前面
2 右面
3 左面
4 上面
5 下面

また、画像のどの位置を割り当てるかはBABYLON.Vector4(x,y,z,w)で指定します。
下の画像の通り、4点を指定することで割り当てられます。

縦・横の長さは全体で1になっていることに注意してください!

ここまでできるとこくりの頭の一部ができます。

最後に耳(?)の部分を作ります。
ここでは簡単に球体を作って、耳にします。
半球にしたいので、一部は直方体の部分に埋まるように位置を調整します。

    // 耳用の緑のマテリアルを設定
    let earMaterial = new BABYLON.StandardMaterial("earMaterial", scene);
    earMaterial.diffuseColor = new BABYLON.Color3(0, 1, 0);

    // 耳のモデルを作成
    let leftEar = BABYLON.MeshBuilder.CreateSphere("leftEar", {diameter: 2, diameterX: 2}, scene);
    leftEar.position = new BABYLON.Vector3(-3, 3, 0);
    leftEar.material = earMaterial;
    let rightEar = BABYLON.MeshBuilder.CreateSphere("rightEar", {diameter: 2, diameterX: 2}, scene);
    rightEar.position = new BABYLON.Vector3(3, 3, 0);
    rightEar.material = earMaterial;

これでこくりの頭の完成です!

コードの全体を記載しておきます。
engineerblog.js

function main() {
    // canvasを取得する
    const canvas = document.getElementById('canvas');
  
    // エンジンを生成(必須)
    const engine = new BABYLON.Engine(canvas);
    // シーンを生成する
    const scene = createScene(engine, canvas);

    // モデリング
    createCocre(scene);
  
    // 描画ループ関数を定義する
    engine.runRenderLoop(() => {
      scene.render();
    });
}

// こくりを生成する
function createCocre(scene) {
    // マテリアルを定義
    let mat = new BABYLON.StandardMaterial("mat", scene);
    let textureImage = new BABYLON.Texture("cocreTexture.png", scene);
    mat.diffuseTexture = textureImage;

    // texture用の画像は、正方形と長方形の組み合わせのため、10分割していい感じに分ける
    const columns = 10;
    const rows = 1;
    let cocreTexture = new Array(6);
    cocreTexture[0] = new BABYLON.Vector4(3 / columns, 0, 5 / columns, 1 / rows);
    cocreTexture[1] = new BABYLON.Vector4(1 / columns, 0, 3 / columns, 1 / rows);
    cocreTexture[2] = new BABYLON.Vector4(0 / columns, 0, 1 / columns, 1 / rows);
    cocreTexture[3] = new BABYLON.Vector4(9 / columns, 0, 10 / columns, 1 / rows);
    cocreTexture[4] = new BABYLON.Vector4(7 / columns, 0, 9 / columns, 1 / rows);
    cocreTexture[5] = new BABYLON.Vector4(5 / columns, 0, 7 / columns, 1 / rows);

    let cocre = BABYLON.MeshBuilder.CreateBox("box", {height: 3, width: 6, depth: 3, faceUV: cocreTexture, wrap: true}, scene);
    cocre.material = mat;
    cocre.position = new BABYLON.Vector3(0, 3, 0);

    // 耳用の緑のマテリアルを設定
    let earMaterial = new BABYLON.StandardMaterial("earMaterial", scene);
    earMaterial.diffuseColor = new BABYLON.Color3(0, 1, 0);

    // 耳のモデルを作成
    let leftEar = BABYLON.MeshBuilder.CreateSphere("leftEar", {diameter: 2, diameterX: 2}, scene);
    leftEar.position = new BABYLON.Vector3(-3, 3, 0);
    leftEar.material = earMaterial;
    var rightEar = BABYLON.MeshBuilder.CreateSphere("rightEar", {diameter: 2, diameterX: 2}, scene);
    rightEar.position = new BABYLON.Vector3(3, 3, 0);
    rightEar.material = earMaterial;
}

// シーンを生成(カメラ・光源・地面等を設定)
function createScene(engine, canvas) {
  let scene = new BABYLON.Scene(engine);
  const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 30, new BABYLON.Vector3(0, 0, 0), scene);
  camera.attachControl(canvas, true);


  // ライトを設定
  let light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
  light.intensity = 0.7;

  return scene;
}

// 読み込み終わってからmainを始める
window.addEventListener('DOMContentLoaded', main);

詰まったところと解決方法

ここでは、ここまでの実装で詰まったところと解決策を書いていきます。

  1. モデルの下部分が真っ黒
    実は上のコードのままですと、こくりの下面が真っ黒になっています。

    これはライトが上しか照らしてないことが原因です。(だと思います…)
    なので、下にもう一つlightを追加します。

      let light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
      light.intensity = 0.7;
     // 追加のライト
      let light1 = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, -1, 0), scene);
      light1.intensity = 0.5;
    

    すると下面までしっかり見ることができます。

  2. モデルに画像が貼られない
    ただ作成したhtmlファイルをローカルで開くと、直方体に画像が貼り付けられず、デフォルトの画像(?)が貼り付けられています。

    開発者ツールで開くと以下のエラーがでています。

    rom origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.
    

    CORSエラーが発生してました。
    このエラーはローカルサーバーを建てることで、回避ができます。
    (他にもchromeの起動オプション等でも回避できます)
    ローカルサーバーで閲覧すると、正常に画像が貼られているのが確認できると思います。

まとめ

今回はBabylon.jsを使って簡単にモデリングをしてみました。
弊社のイベントソリューションにも活かせるかも!?と期待が膨らみました。

初めてのエンジニアブログということで、拙い内容になってしまいましたが、最後までご覧いただきありがとうございました。