5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

この記事はElixirアドベントカレンダー2025のシリーズ2、12日目の記事です

今回はBabylon.jsのチュートリアルの2章をLiveView+Babylon.jsでやっていく記事になります

11日目の続きから進めていきます

2-05: テクスチャを貼る (Add Texture)

こんな感じで素のテクスチャ読み込んで、色をセット、メッシュのテクスチャに指定
としています

const groundMat = new BABYLON.StandardMaterial("groundMat");
groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
ground.material = groundMat;

公式からDLする場合はこんな感じ

    boxMat.diffuseTexture = new BABYLON.Texture(
      "https://www.babylonjs-playground.com/textures/floor.png"
    );

最終的にこうなります

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);

    const ground = BABYLON.MeshBuilder.CreateGround("ground", {
      width: 10,
      height: 10,
    });

+   const groundMat = new BABYLON.StandardMaterial("groundMat");
+   groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
+   ground.material = groundMat;

    const box = BABYLON.MeshBuilder.CreateBox("box", {});
    box.position.y = 0.5;

+   const boxMat = new BABYLON.StandardMaterial("boxMat");
+   boxMat.diffuseTexture = new BABYLON.Texture(
+     "https://www.babylonjs-playground.com/textures/floor.png"
+   );
+
+    box.material = boxMat;

    const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
      diameter: 1.3,
      height: 1.2,
      tessellation: 3,
    });
    roof.scaling.x = 0.75;
    roof.rotation.z = Math.PI / 2;
    roof.position.y = 1.22;

+   const roofMat = new BABYLON.StandardMaterial("roofMat");
+   roofMat.diffuseTexture = new BABYLON.Texture(
+     "https://assets.babylonjs.com/environments/roof.jpg",
+     scene
+   );
+
+   roof.material = roofMat;

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

export default BabylonHook201;

スクリーンショット 2025-12-09 22.44.22.png

2-06: 家のそれぞれの面にマテリアル (Material For Each House Side)

読み込むテクスチャを変更して、UV展開します

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
  ...
+   const faceUV = [];
+   faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face
+   faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //front face
+   faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0); //right side
+   faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0); //left side

-   const box = BABYLON.MeshBuilder.CreateBox("box", {});
+   const box = BABYLON.MeshBuilder.CreateBox("box", {
+     faceUV: faceUV,
+     wrap: true,
+   });
    box.position.y = 0.5;

    const boxMat = new BABYLON.StandardMaterial("boxMat");
    boxMat.diffuseTexture = new BABYLON.Texture(
-    "https://www.babylonjs-playground.com/textures/floor.png"   
+    "https://assets.babylonjs.com/environments/cubehouse.png"
    );
    box.material = boxMat;

    ...
    
    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

export default BabylonHook201;

スクリーンショット 2025-12-09 23.09.50.png

長い家はこうなります

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
  ...
+   const faceUV = [];
+   faceUV[0] = new BABYLON.Vector4(0.6, 0.0, 1.0, 1.0); //rear face
+   faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0); //front face
+   faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //right side
+   faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //left side

-   const box = BABYLON.MeshBuilder.CreateBox("box", {});
+   const box = BABYLON.MeshBuilder.CreateBox("box", {
+     width: 2,
+     faceUV: faceUV,
+     wrap: true,
+   });
    box.position.y = 0.5;

    const boxMat = new BABYLON.StandardMaterial("boxMat");
    boxMat.diffuseTexture = new BABYLON.Texture(
-    "https://www.babylonjs-playground.com/textures/floor.png"   
+    "https://assets.babylonjs.com/environments/semihouse.png"
    );
    box.material = boxMat;

    const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
      diameter: 1.3,
      height: 1.2,
      tessellation: 3,
    });
    roof.scaling.x = 0.75;
+   roof.scaling.y = 2;
    roof.rotation.z = Math.PI / 2;
    roof.position.y = 1.22;

    
    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

export default BabylonHook201;

スクリーンショット 2025-12-09 23.14.01.png

2-07: リファクタリング(関数化)

ベタ書きは良くない(それはそう)

汎用性もクソもない関数化ではあるがMain関数はスッキリします

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);
    const ground = buildGround(scene);
    const box = buildBox(scene, "box");
    const roof = buildRoof(scene, "roof");

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

const buildGround = (scene) => {
  const ground = BABYLON.MeshBuilder.CreateGround(
    "ground",
    {
      width: 10,
      height: 10,
    },
    scene
  );

  const groundMat = new BABYLON.StandardMaterial("groundMat");
  groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
  ground.material = groundMat;

  return ground;
};

const buildBox = (scene, name) => {
  const faceUV = [];
  faceUV[0] = new BABYLON.Vector4(0.6, 0.0, 1.0, 1.0); //rear face
  faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0); //front face
  faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //right side
  faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //left side

  const box = BABYLON.MeshBuilder.CreateBox(
    name,
    {
      width: 2,
      faceUV: faceUV,
      wrap: true,
    },
    scene
  );
  box.position.y = 0.5;

  const boxMat = new BABYLON.StandardMaterial(`${name}Mat`);
  boxMat.diffuseTexture = new BABYLON.Texture(
    "https://assets.babylonjs.com/environments/semihouse.png"
  );
  box.material = boxMat;

  return box;
};

const buildRoof = (sence, name) => {
  const roof = BABYLON.MeshBuilder.CreateCylinder(
    name,
    {
      diameter: 1.3,
      height: 1.2,
      tessellation: 3,
    },
    sence
  );
  roof.scaling.x = 0.75;
  roof.scaling.y = 2;
  roof.rotation.z = Math.PI / 2;
  roof.position.y = 1.22;

  const roofMat = new BABYLON.StandardMaterial(`${name}Mat`);
  roofMat.diffuseTexture = new BABYLON.Texture(
    "https://assets.babylonjs.com/environments/roof.jpg"
  );

  roof.material = roofMat;

  return roof;
};

export default BabylonHook201;

2-08: メッシュを結合 (Combining Meshes Using Merge Meshes)

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);
    const ground = buildGround(scene);
    const box = buildBox(scene, "box");
    const roof = buildRoof(scene, "roof");

+   const house = BABYLON.Mesh.MergeMeshes([box, roof]);

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};
...
export default BabylonHook201;

雑に結合するとこうなります

スクリーンショット 2025-12-09 23.38.01.png

MergeMeshes の multiMultiMaterial パラメータを使って修正するとちゃんとなります

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);
    const ground = buildGround(scene);
    const box = buildBox(scene, "box");
    const roof = buildRoof(scene, "roof");

+   const house = BABYLON.Mesh.MergeMeshes(
+     [box, roof],
+     true,  // disposeSource
+     false, // allow32BitsIndices
+     null,  // meshSubclass
+     false, // subdivideWithSubMeshes
+     true   // multiMultiMaterials
+    );

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};
...
export default BabylonHook201;

スクリーンショット 2025-12-09 23.40.02.png

2-09: メッシュをコピー (Copying Meshes)

家を作る関数(buildHouse)を作ります

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);
    const ground = buildGround();
    const house = buildHouse(2);

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

const buildGround = () => {
  const ground = BABYLON.MeshBuilder.CreateGround("ground", {
    width: 15,
    height: 16,
  });
  const groundMat = new BABYLON.StandardMaterial("groundMat");
  groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
  ground.material = groundMat;

  return ground;
};

const buildHouse = (width) => {
  return BABYLON.Mesh.MergeMeshes(
    [buildBox(width), buildRoof(width)],
    true,
    false,
    null,
    false,
    true
  );
};

const buildBox = (width) => {
  //texture
  const boxMat = new BABYLON.StandardMaterial("boxMat");
  if (width == 2) {
    boxMat.diffuseTexture = new BABYLON.Texture(
      "https://assets.babylonjs.com/environments/semihouse.png"
    );
  } else {
    boxMat.diffuseTexture = new BABYLON.Texture(
      "https://assets.babylonjs.com/environments/cubehouse.png"
    );
  }

  const faceUV = [];
  if (width == 2) {
    faceUV[0] = new BABYLON.Vector4(0.6, 0.0, 1.0, 1.0); //rear face
    faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0); //front face
    faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //right side
    faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0); //left side
  } else {
    faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face
    faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //front face
    faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0); //right side
    faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0); //left side
  }

  const box = BABYLON.MeshBuilder.CreateBox("box", {
    width: width,
    faceUV: faceUV,
    wrap: true,
  });
  box.material = boxMat;
  box.position.y = 0.5;

  return box;
};

const buildRoof = (width) => {
  const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
    diameter: 1.3,
    height: 1.2,
    tessellation: 3,
  });
  roof.scaling.x = 0.75;
  roof.scaling.y = width;
  roof.rotation.z = Math.PI / 2;
  roof.position.y = 1.22;

  const roofMat = new BABYLON.StandardMaterial("roofMat");
  roofMat.diffuseTexture = new BABYLON.Texture(
    "https://assets.babylonjs.com/environments/roof.jpg"
  );
  roof.material = roofMat;

  return roof;
};

export default BabylonHook201;

最終的に村化します
ベースのdetached_houseとsemi_houseを作って、
芸術的な位置取りで新しいインスタンスを作成しています

import { createAttachment } from "./util";

const BabylonHook201 = {
  mounted() {
    const canvas = document.getElementById("renderCanvas");
    const engine = new BABYLON.Engine(canvas, true);
    const scene = new BABYLON.Scene(engine);
    createAttachment(scene, canvas);

    buildDwellings();

    engine.runRenderLoop(() => {
      scene.render();
    });
  },
};

const buildDwellings = () => {
  const ground = buildGround();

  const detached_house = buildHouse(1);
  detached_house.rotation.y = -Math.PI / 16;
  detached_house.position.x = -6.8;
  detached_house.position.z = 2.5;

  const semi_house = buildHouse(2);
  semi_house.rotation.y = -Math.PI / 16;
  semi_house.position.x = -4.5;
  semi_house.position.z = 3;

  const places = [];
  places.push([1, -Math.PI / 16, -6.8, 2.5]);
  places.push([2, -Math.PI / 16, -4.5, 3]);
  places.push([2, -Math.PI / 16, -1.5, 4]);
  places.push([2, -Math.PI / 3, 1.5, 6]);
  places.push([2, (15 * Math.PI) / 16, -6.4, -1.5]);
  places.push([1, (15 * Math.PI) / 16, -4.1, -1]);
  places.push([2, (15 * Math.PI) / 16, -2.1, -0.5]);
  places.push([1, (5 * Math.PI) / 4, 0, -1]);
  places.push([1, Math.PI + Math.PI / 2.5, 0.5, -3]);
  places.push([2, Math.PI + Math.PI / 2.1, 0.75, -5]);
  places.push([1, Math.PI + Math.PI / 2.25, 0.75, -7]);
  places.push([2, Math.PI / 1.9, 4.75, -1]);
  places.push([1, Math.PI / 1.95, 4.5, -3]);
  places.push([2, Math.PI / 1.9, 4.75, -5]);
  places.push([1, Math.PI / 1.9, 4.75, -7]);
  places.push([2, -Math.PI / 3, 5.25, 2]);
  places.push([1, -Math.PI / 3, 6, 4]);

  const houses = [];
  for (let i = 0; i < places.length; i++) {
    if (places[i][0] === 1) {
      houses[i] = detached_house.createInstance("house" + i);
    } else {
      houses[i] = semi_house.createInstance("house" + i);
    }
    houses[i].rotation.y = places[i][1];
    houses[i].position.x = places[i][2];
    houses[i].position.z = places[i][3];
  }
};

...

export default BabylonHook201;

スクリーンショット 2025-12-10 0.21.14.png

最後に

2章は次で最後になります
本記事は以上になりますありがとうございました

5
0
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
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?