8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

babylon.js の使い方(随時更新)

Last updated at Posted at 2020-09-10

お仕事で babylon.js を使うことになったので、勉強したことをまとめます。

承前

babylon.js については 公式のチュートリアル が充実しているので、基本的にそれだけで事足ります。
チュートリアルを一通り読んで基礎事項を押さえれば、あとは必要に応じて HOW TOAPI を調べるだけでよい状態になれます。

この記事では、公式のチュートリアルでは少しわかりづらいと感じた箇所について、補足事項を書いていきます。
なお、この記事は随時更新予定です。

Material

Color

3D の物体表面の色は、光に含まれる各色の強度と物体表面の各色の反射率の積で計算されます。

例えば、光の RGB の各色の強度が (1,0,1) で物体の反射率が (1,0,0) だとすると、それぞれの要素の値を掛け合わせた (1,0,0) が最終的に見える物体表面の色になります。同様に、反射率が (0,1,0) の物体なら、最終的な色は (0,0,0) になります。

Material では、この計算に使用される物体の各色の反射率を定義できます。
定義は以下のように、Material オブジェクトのプロパティへの設定を通して行います。

var material = new BABYLON.StandardMaterial("material", scene);

material.diffuseColor  = new BABYLON.Color3(1, 0, 1);
material.specularColor = new BABYLON.Color3(0, 1, 0);
material.emissiveColor = new BABYLON.Color3(0, 0, 1);
material.ambientColor  = new BABYLON.Color3(1, 1, 1);

mesh.material = material;

上記のプロパティは、それぞれが特定の光に対する物体の色の反射率を表しています。
以下、それぞれのプロパティの詳細について説明します。

diffuseColor

光源からの光に対する物体の反射率を設定します。

ここでいう光源とは ~Light 系のコンストラクタを使用して生成されるオブジェクトを指します。HemisphericLightSpotLight などが該当します。
各光源には diffuse プロパティを介して光に含まれる各色の強度を設定できます。

var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.diffuse = new BABYLON.Color3(1, 1, 1);

diffuseColor に白・赤・緑を設定した Material を作成し、上記のコードで定義した光源下でどのように見えるかを確認してみます。それぞれの Material の定義は以下の通りです。

var white = new BABYLON.StandardMaterial("white", scene);
white.diffuseColor = new BABYLON.Color3(1, 1, 1);

var red = new BABYLON.StandardMaterial("red", scene);
red.diffuseColor = new BABYLON.Color3(1, 0, 0);

var green = new BABYLON.StandardMaterial("green", scene);
green.diffuseColor = new BABYLON.Color3(0, 1, 0);

結果は以下のようになります。
光源からの光が RGB の全ての成分を含んでいるので、箱がそれぞれ白・赤・緑になっているのが確認できます。

See the Pen babylon.js - diffuse sample by dmystk (@dmystk) on CodePen.

ここで光源の diffuse から緑成分を取り去ってみます。
光源の定義は以下のようになります。

var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.diffuse = new BABYLON.Color3(1, 0, 1);

結果は以下のようになります。
光源からの光に緑成分が含まれていないので、白い箱は紫に、緑の箱は黒に変化します。一方で、赤い箱は赤いままになっているのがわかります。

See the Pen babylon.js - diffuse sample 2 by dmystk (@dmystk) on CodePen.

specularColor

反射光の色を定義します。

反射光といっても、物体から反射した光が他の物体に照射されたりするわけではありません。
ここで反射光という言葉が指しているのは、物体が光に照らされた際に、物体表面が光って見えるエフェクトのことです。

言葉で説明してもわかりづらいので、例を示します。

以下の例では specularColor(1,1,1) に設定しています。
視点を動かすと、箱の上の面が光を反射して光る(艶があるように見える)のがわかると思います。

See the Pen babylon.js - specular sample by dmystk (@dmystk) on CodePen.

次の例では specularColor(0,0,0) に設定しています。
視点を動かしても、箱の上の面は特に光りません。

See the Pen babylon.js - specular sample 2 by dmystk (@dmystk) on CodePen.

上の例で見たように、specularColor では物体表面が光って見えるエフェクトを制御します。
同様に specularColor(1,0,0) に設定すれば、反射光を赤く見せることもできます。

See the Pen babylon.js - specular sample 3 by dmystk (@dmystk) on CodePen.

なお diffuse と同様、こちらも specular プロパティを通して、光源が放つ specularColor に作用する光の色を設定できます。

var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
light.specular = new BABYLON.Color3(1, 1, 1);

emissiveColor

物体表面それ自体が光っている状態を表現できます。
光源がない環境下でも明るく見えます。

以下の例では emissiveColor(1,0,0) を設定しています。
光源は生成していませんが、箱が真っ赤に表示されているのがわかると思います。

See the Pen babylon.js - emissive sample by dmystk (@dmystk) on CodePen.

なお、光源によって emissiveColor が設定された面が照らされた場合、光源の光の色(厳密には diffusediffuseColor によって生じる色)と emissiveColor の色が混色されます。
上の例に青色 (0,0,1) の光を放つ光源を追加した例を以下に示します。

See the Pen babylon.js - emissive sample 2 by dmystk (@dmystk) on CodePen.

ambientColor

環境光に対する物体の色の反射率を定義します。

babylon.js の scene には環境光という特別な光が設定されています。
環境光には、以下のように ambientColor プロパティを通して色の設定を行います。

scene.ambientColor = new BABYLON.Color3(1, 1, 1);

Material の ambientColor の初期値は (0,0,0) のようです。
そのため ambientColor プロパティに明示的に値を設定しない限り、環境光は効果を発揮しません。

以下の例では環境光を (1,1,1) に設定していますが、Material には何も設定していません。

See the Pen babylon.js - ambient sample by dmystk (@dmystk) on CodePen.

一方、Material の ambientColor(1,0,0) を設定した場合は以下のようになります。

See the Pen babylon.js - ambient sample 2 by dmystk (@dmystk) on CodePen.

ShaderMaterial

ShaderMaterial を利用することでオブジェクトに自作シェーダーを適用することができます。
シェーダーについては以下の babylon.js の公式ページに詳しく書いてあります。

全部を説明すると大変な文量になってしまうため、ここではさわりだけ説明します。

ShaderMaterial 経由で自作シェーダーを使用するための必要最低限のコードは以下の通りです:

Effect.ShadersStore["customVertexShader"] = `
  // type your vertex shader here
`;
Effect.ShadersStore["customFragmentShader"] = `
  // type your fragment shader here
`;
var shaderPath = {
  vertex: "custom",
  fragment: "custom",
};
var option = {
  attributes: ["position", "normal", "uv"],
  uniforms: ["world", "worldView", "worldViewProjection", "view", "projection"],
}
var shaderMaterial = new BABYLON.ShaderMaterial("shader", scene, shaderPath, option);

大まかには shaderPath"custom" を指定して Effect.ShadersStore にカスタムシェーダーのコードを記述するという構成です。シェーダーのコードは GLSL で記述します。

GLSL は Babylon.js の裏にある WebGL の世界でコンパイルされて動作します。この際、Babylon.js は GLSL のコードと一緒に必要なデータを WebGL の世界に引き渡します。

option で指定している attributesuniforms は Babylon.js から WebGL の世界に引き渡す変数を定義しています。変数には Babylon.js のエンジンが勝手に用意してくれるものとプログラマが自分で宣言するものの2種類あるのですが、ここでは前者のみが対象です。後者については ShaderMaterialsetFloatsetTexture を使用して引き渡します。

文面だけではわかりづらいと思うので例を示します。以下はカスタムシェーダを使ってランダムな色のテクスチャを持つ Box を作成するサンプルです。

See the Pen babylon.js - shader material by dmystk (@dmystk) on CodePen.

以下は Vertex シェーダーを記述している箇所の抜粋です。Vertex シェーダーと Fragment シェーダーについては、先に掲載した Introduction to Shaders を参照ください。

  BABYLON.Effect.ShadersStore["customVertexShader"] = `
    precision highp float;
    // Attributes
    attribute vec2 uv;
    attribute vec3 position;
    // Uniforms
    uniform mat4 worldViewProjection;
    // Varying
    varying vec2 vUV;
    void main(void) {
      gl_Position = worldViewProjection * vec4(position, 1.0);
      vUV = uv;
    }
  `;

attributeuniform として宣言されているものが Babylon.js から渡される変数群です。
ここで宣言している uvpositionworldViewProjection は、いずれも先ほど option で指定されていたことに注意してください。

varying は Vertex シェーダーから Fragment シェーダーに値を渡す際に使用する変数です。これは以下の Fragment シェーダーのコードを見るとわかりやすいです。

  BABYLON.Effect.ShadersStore["customFragmentShader"] = `
    precision highp float;
    // Uniforms
    uniform sampler2D uTextureSampler;
    // Varying
    varying vec2 vUV;
    void main(void) {
      gl_FragColor = texture2D(uTextureSampler, vUV);
    }
  `;

Vertex シェーダーの中で varying として宣言していた vUV と同名の変数が宣言されています。この vUV には Vertex シェーダーから渡された値が格納されます。

Fragment シェーダーの中でも uniform が宣言されていますが、ここで宣言されている uTextureSampler は先ほどの option の中には含まれていない変数(=プログラマの自作変数)です。uTextureSampler の値は以下の箇所で設定されています。

  let material = new BABYLON.ShaderMaterial("smat", this.scene, {vertex: "custom", fragment: "custom"}, {
    attributes: ["position", "normal", "uv"],
    uniforms: ["world", "worldView", "worldViewProjection", "view", "projection"],
  });

  // 中略

  material.setTexture("uTextureSampler", texture);

今回は GLSL の sampler2D 型の値を設定したいので ShaderMaterialsetTexture をコールして値を設定しています。

Camera

Multi Canvases

EngineregisterView() を使うことで、1つの Babylon.js インスタンスに対して、複数の canvas を割り当てることができます。
それぞれの canvas で異なるカメラを使用することも可能です。

See the Pen babylon.js - multi canvases sample by dmystk (@dmystk) on CodePen.

Safari だと表示が崩れるようです。
Chrome では正常に表示されることを確認済みです。

Orthographic Camera

通常、我々が babylon.js の世界でカメラを作成した場合、そのカメラは透視投影を使用したカメラです。透視投影では、1点を中心に放射状に光線を投影するため、遠くのものが小さく、近くのものが大きく画面に表示されます。

一方で、掲題の垂直投影カメラ(Orthographic Camera)は光線を平行に投影します。光線が平行に投影されるため、遠くのものも近くのものも一定の寸法で画面に表示されます。

例えば、カメラのごく近くに箱があり、その遥か向こう側にその箱よりも少しだけ大きな箱がある場合、透視投影では奥の箱は手前の箱に隠れてしまいますが、垂直投影では両方の箱が見える状態となります。

文面ではわかりづらいので、こちら にある画像を見ていただくのがよいかもしれません。

babylon.js では作成したカメラの modeCamera.ORTHOGRAPHIC_CAMERA を設定することで垂直投影カメラに切り替えることができます。

var camera = new ArcRotateCamera("camera", 0, 0, 20, new Vector3.Zero(), this.scene);
camera.mode = Camera.ORTHOGRAPHIC_CAMERA;

注意点として、光線を平行に投影する形式に変更されるため、投影面の範囲を自分で設定する必要があります。これは各種 orthoXxx に値を設定することで行えます。画面のアスペクト比も考慮する必要がありますが、これは Engine から取得することができます。

var scale = 5;  // スケールの適切な選び方はよくわかっていないので誰か教えてほしい ...
var aspect = scene.getEngine().getAspectRatio(camera);
camera.orthoTop = scale;
camera.orthoBottom = -scale;
camera.orthoLeft = -scale * aspect;
camera.orthoRight = scale * aspect;

以下に Multi Canvases を使用して透視投影と垂直投影の2つのカメラを比較するサンプルを示します。左が透視投影、右が垂直投影です。初期状態では表面に凹凸のある地形(リボン)を上から俯瞰していますが、見え方が大きく異なることがわかると思います。

画面のアスペクト比がうまく反映できていない感じがしますが、CodePen のソースコードペインを折り畳むとそれらしい表示になるようです。リサイズイベント等を適切に拾うことで解消できるのかもしれませんが、ここでは対応していません。

See the Pen babylon.js - multi canvases sample by dmystk (@dmystk) on CodePen.

ちなみに左のキャンバスのカメラをマウス操作すると、連動して右のキャンバスのカメラも動くようになっているので、ご活用ください。なお、連動するのは回転のみで、拡大縮小には対応できていません。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?