はじめに
この記事は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;
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;
長い家はこうなります
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;
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;
雑に結合するとこうなります
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;
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;
最後に
2章は次で最後になります
本記事は以上になりますありがとうございました





