当前位置: 首页 > 工具软件 > Babylon.js > 使用案例 >

Babylon.js 入门 - 第 2 章 - 建造村庄(2)

徐麒
2023-12-01

Babylon.js 入门 - 第 2 章 - 建造村庄(2)

从一到多

现在我们迈出了一大步,因为我们把这个简单的盒子变成了一两个房子。由于房屋不是漂浮在空中的,所以我们需要创建一些地面,然后看看我们如何定位、旋转和重新调整房屋大小并将其放置在地面上。由于空白盒子看起来太过空白,我们将添加纹理以使其具有窗户和门的外观。为了不让雨淋到屋内,我们添加一个屋顶网格并将两个网格合并在一起。当然,几座房子并不能构成一个村庄,因此我们将了解如何根据我们的想法多次复制网格。由于建筑工人是一种工作在噪音中的工种,我们将展示如何引入声音,然后立即将它静音,因为除了您想要使用的声音,其他声音会分散注意力。

组合网格

使用合并网格组合网格

这是组合两个或多个网格的直接方式:

const combined = BABYLON.Mesh.MergeMeshes(Array_of_Meshes_to_Combine)

在我们的例子中,代码将是:

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

在你的场景中组合网格的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            const ground = buildGround();
            const box = buildBox();
            const roof = buildRoof();
            const house = BABYLON.Mesh.MergeMeshes([box, roof]);
            return scene;
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 10,
                height: 10
            });
            ground.material = groundMat;
        }
        const buildBox = () => {
            // 盒子的材质和纹理
            const boxMat = new BABYLON.StandardMaterial("boxMat");
            boxMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/cubehouse.png")
            // 用于为每一侧设置不同图像的选项参数
            const faceUV = [];
            // 后面
            faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
            // 前面
            faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
            // 右面
            faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
            // 左面
            faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            const box = BABYLON.MeshBuilder.CreateBox("box", {
                faceUV: faceUV,
                wrap: true
            });
            box.material = boxMat;
            box.position.y = 0.5;
            return box;
        }
        const buildRoof = () => {
            // 屋顶的材质和纹理
            const roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

首先我们应该注意到的是,整个房子只被一种可使用的材质覆盖了。幸运的是,这可以使用 MergeMeshesmultiMultiMaterial 参数进行纠正,不幸的是,这是一长串列表中的最后一个参数。代码现在看起来像:

const house = BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);

在此阶段,重要的是要注意第二个参数为 true 是处理原始网格,最后一个参数为 true 是允许将原始材料单独应用于与原始网格匹配的部分。

组合网格并保留材质分配的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            const ground = buildGround();
            const box = buildBox();
            const roof = buildRoof();
            const house = BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
            return scene;
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 10,
                height: 10
            });
            ground.material = groundMat;
        }
        const buildBox = () => {
            // 盒子的材质和纹理
            const boxMat = new BABYLON.StandardMaterial("roofMat");
            boxMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/cubehouse.png")
            // 用于为每一侧设置不同图像的选项参数
            const faceUV = [];
            // 后面
            faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
            // 前面
            faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
            // 右面
            faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
            // 左面
            faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            const box = BABYLON.MeshBuilder.CreateBox("box", {
                faceUV: faceUV,
                wrap: true
            });
            box.material = boxMat;
            box.position.y = 0.5;
            return box;
        }
        const buildRoof = () => {
            // 屋顶的材质和纹理
            const roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

制作副本

复制网格

复制网格的两种主要方法分别是克隆网格和创建网格的实例。克隆网格为您提供了独立的网格副本,而创建的网格实例仍然链接到原始的网格的材质。您不能更改创建的网格实例的材质。

克隆网格使用:

clonedHouse = house.clone("clonedHouse")

创建网格的实例使用:

instanceHouse = house.createInstance("instanceHouse")

因此在我们的世界中,所有房屋都将使用 createInstance 创建,来保证所有的房子都是用相同的材质。

在我们这样做之前,我们先结合房屋建造函数来分别建造宽度为 12 的独立式或半独立式房屋。

扩展房屋建造函数的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 10, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            const ground = buildGround();
            // 房子宽度1或2
            const house = buildHouse(2);
            return scene;
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 10,
                height: 10
            });
            ground.material = groundMat;
        }
        const buildHouse = (width) => {
            const box = buildBox(width);
            const roof = buildRoof(width);
            return BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
        }
        const buildBox = (width) => {
            // 盒子的材质和纹理
            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);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
            } else {
                // 后面
                faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            }
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            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 roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.scaling.y = width;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

我们现在扩大地面并稍微增加相机半径以适应多个房子,并使摄像机能够观察到它们。首先,我们为每种类型建造一个房屋并定位它们。之后,我们将为剩余的房屋创建实例。在确定其他房屋的类型、位置和方向后,我们将使用循环来创建它们。

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];
}

创建多个房屋实例的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            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;
            // 每项都是一个数组[房屋类型,旋转,x,z]
            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];
            }
            return scene;
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 15,
                height: 16
            });
            ground.material = groundMat;
        }
        const buildHouse = (width) => {
            const box = buildBox(width);
            const roof = buildRoof(width);
            return BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
        }
        const buildBox = (width) => {
            // 盒子的材质和纹理
            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);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
            } else {
                // 后面
                faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            }
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            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 roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.scaling.y = width;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

和以前一样,方便后续开发,我们将把这些房屋的建造代码放入一个函数中。

将实例包装到函数中的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            buildDwellings();
            return scene;
        }
        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;
            // 每项都是一个数组[房屋类型,旋转,x,z]
            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];
            }
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 15,
                height: 16
            });
            ground.material = groundMat;
        }
        const buildHouse = (width) => {
            const box = buildBox(width);
            const roof = buildRoof(width);
            return BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
        }
        const buildBox = (width) => {
            // 盒子的材质和纹理
            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);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
            } else {
                // 后面
                faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            }
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            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 roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.scaling.y = width;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

目前,我们正在构建的世界稍微复杂了,让我们将村庄保存为一个文件,然后重新访问,将其作为我们想要增强的网站的一部分进行查看。

Village 作为 .glb 文件导入的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Babylon.js sample code</title>
    <!-- Babylon.js -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/ammo.js"></script>
    <script src="https://preview.babylonjs.com/cannon.js"></script>
    <script src="https://preview.babylonjs.com/Oimo.js"></script>
    <script src="https://preview.babylonjs.com/earcut.min.js"></script>
    <script src="https://preview.babylonjs.com/babylon.js"></script>
    <script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
    <script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.js"></script>
    <script src="https://preview.babylonjs.com/serializers/babylonjs.serializers.min.js"></script>
    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
    <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }
    </style>
</head>

<body>
    <canvas id="renderCanvas"></canvas>
    <script>
        var canvas = document.getElementById("renderCanvas");
        var startRenderLoop = function (engine, canvas) {
            engine.runRenderLoop(function () {
                if (sceneToRender && sceneToRender.activeCamera) {
                    sceneToRender.render();
                }
            });
        }
        var engine = null;
        var scene = null;
        var sceneToRender = null;
        var createDefaultEngine = function () {
            return new BABYLON.Engine(canvas, true, {
                preserveDrawingBuffer: true,
                stencil: true,
                disableWebGL2Support: false
            });
        };
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "village.glb");
            return scene;
        };
        window.initFunction = async function () {
            var asyncEngineCreation = async function () {
                try {
                    return createDefaultEngine();
                } catch (e) {
                    console.log(
                        "the available createEngine function failed. Creating the default engine instead"
                    );
                    return createDefaultEngine();
                }
            }
            window.engine = await asyncEngineCreation();
            if (!engine) throw 'engine should not be null.';
            startRenderLoop(engine, canvas);
            window.scene = createScene();
        };
        initFunction().then(() => {
            sceneToRender = scene
        });
        // Resize
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

查看器

更改查看器的相机

当我们将村庄作为模型放入查看器时会发生什么?

使用默认查看器的村庄的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Viewer 1 Example</title>
    <script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </meta>
    <style>
        body {
            height: 600px;
        }

        #header {
            font-size: 6em;
            padding: 5px;
        }

        .cell {
            width: 30%;
            height: 40%;
            margin: 1%;
            float: left;
            padding: 3px;
            background-color: #BBBBBB;
        }

        @media screen and (max-width: 900px) {
            .cell {
                width: unset;
                padding: 0;
                font-size: 18px;
            }

            .babylon {
                width: 100%;
                margin: 0;
                padding: 8px;
                box-sizing: border-box;
                background: unset;
            }
        }
    </style>
</head>

<body>
    <div id="header">
        Viewer News
    </div>
    <div class="cell babylon">
        <babylon model="https://assets.babylonjs.com/meshes/village.glb"></babylon>
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
</body>

</html>

我们看到地面在闪烁。这是为什么?这是因为默认情况下,查看器已经添加了一个地面,造成它们相互重叠并争夺最高地位。

我们如何克服这一点? 我们在 <babylon> 元素中使用 extends 属性并将其设置为 minimal

<babylon extends="minimal" model="path to model file"></babylon>

使用最小查看器的村庄的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Viewer 1 Example</title>
    <script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </meta>
    <style>
        body {
            height: 600px;
        }

        #header {
            font-size: 6em;
            padding: 5px;
        }

        .cell {
            width: 30%;
            height: 40%;
            margin: 1%;
            float: left;
            padding: 3px;
            background-color: #BBBBBB;
        }

        @media screen and (max-width: 900px) {
            .cell {
                width: unset;
                padding: 0;
                font-size: 18px;
            }

            .babylon {
                width: 100%;
                margin: 0;
                padding: 8px;
                box-sizing: border-box;
                background: unset;
            }
        }
    </style>
</head>

<body>
    <div id="header">
        Viewer News
    </div>
    <div class="cell babylon">
        <babylon extends="minimal" model="https://assets.babylonjs.com/meshes/village.glb"></babylon>
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
</body>

</html>

通过移除默认地面闪烁已经停止了,但是默认查看器会计算模型的范围并相应地调整相机。通过使用最小的相机也只是默认靠近模型村的中心。

当你想让相机离得更远时,你必须再写一些代码,当然你可以根据需要复制和粘贴。

要移动相机,我们必须调整它的半径属性。这必须在加载模型之前完成。一旦模型加载到查看器中,属性就无法更改。我们需要从 <babylon> 元素中移除 model 属性,以防止在更改相机半径之前加载模型。<babylon> 元素还必须有一个 id,该 id 由将改变相机属性的脚本引用。

<babylon id="myViewer" extends="minimal"></babylon>

以下代码设置相机半径(在本例中为俯角),然后加载模型:

BabylonViewer.viewerManager.getViewerPromiseById('myViewer').then((viewer) => {
    viewer.onSceneInitObservable.add(() => {
        // 设置相机半径
        viewer.sceneManager.camera.radius = 15;
        // 设置相机俯角
        viewer.sceneManager.camera.beta = Math.PI / 2.2;
    });
    viewer.onEngineInitObservable.add((scene) => {
        viewer.loadModel({
            url: "path to model file"
        });
    });
});

村庄调整相机的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Viewer 1 Example</title>
    <script src="https://cdn.babylonjs.com/viewer/babylon.viewer.js"></script>
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.min.css" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </meta>
    <script>
        BabylonViewer.viewerManager.getViewerPromiseById('myViewer').then((viewer) => {
            viewer.onSceneInitObservable.add(() => {
                // 设置相机半径
                viewer.sceneManager.camera.radius = 15;
                // 设置相机俯角
                viewer.sceneManager.camera.beta = Math.PI / 2.2;
            });
            viewer.onEngineInitObservable.add((scene) => {
                viewer.loadModel({
                    url: "https://assets.babylonjs.com/meshes/village.glb"
                });
            });
        });
    </script>
    <style>
        body {
            height: 600px;
        }

        #header {
            font-size: 6em;
            padding: 5px;
        }

        .cell {
            width: 30%;
            height: 40%;
            margin: 1%;
            float: left;
            padding: 3px;
            background-color: #BBBBBB;
        }

        @media screen and (max-width: 900px) {
            body {
                height: unset;
            }

            .cell {
                width: unset;
                padding: 0;
                font-size: 18px;
            }

            .babylon {
                width: 100%;
                margin: 0;
                padding: 8px;
                box-sizing: border-box;
                background: unset;
            }
        }
    </style>
</head>

<body>
    <div id="header">
        Viewer News
    </div>
    <div class="cell babylon">
        <babylon id="myViewer" extends="minimal"></babylon>
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
    <div class="cell">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
        magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
        consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
        pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
        laborum.
    </div>
</body>

</html>

当您开发网页游戏或应用程序时,您可能需要比 Viewer 所能提供的更大的灵活性。让我们再看看如何使用 HTML 模板。

Web 应用布局

改变 Web 应用程序布局

虽然您可能希望您的游戏或应用程序占据大部分页面,但您可能需要一些空间来作为示例说明。只需将 <canvas> 元素放在 <div> 中,然后根据需要排列元素。

<div id="holder">
    <!-- touch-action="none" 以获得 PEP 的最佳结果 -->
    <canvas id="renderCanvas" touch-action="none"></canvas>
</div>
<div id="instructions">
    <br />
    <h2>Instructions</h2>
    <br />
    Instructions Instructions Instructions Instructions Instructions
    Instructions Instructions Instructions Instructions Instructions
</div>

附加样式:

<style>
    #holder {
        width: 80%;
        height: 100%;
        float: left;
    }

    #instructions {
        width: 20%;
        height: 100%;
        float: left;
        background-color: grey;
    }
</style>

导入模型村的示例的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </meta>
    <title>Babylon Template</title>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }

        #holder {
            width: 80%;
            height: 100%;
            float: left;
        }

        #instructions {
            width: 20%;
            height: 100%;
            float: left;
            background-color: grey;
        }
    </style>
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
</head>

<body>
    <div id="holder">
        <!-- touch-action="none" 以获得 PEP 的最佳结果 -->
        <canvas id="renderCanvas" touch-action="none"></canvas>
    </div>
    <div id="instructions">
        <br>
        <h2>Instructions</h2>
        <br>
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
    </div>
    <script>
        // 获取画布元素
        const canvas = document.getElementById("renderCanvas");
        // 生成 BABYLON 3D 引擎
        const engine = new BABYLON.Engine(canvas, true);
        const createScene = function () {
            const scene = new BABYLON.Scene(engine);
            BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/", "village.glb");
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            return scene;
        };
        // 调用 createScene 函数
        const scene = createScene();
        // 注册渲染循环以重复渲染场景
        engine.runRenderLoop(function () {
            scene.render();
        });
        // 监视浏览器/画布调整大小事件
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>

你当然仍然可以完全用代码构建你的场景。

从代码构建村庄的示例:

示例链接

示例代码:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    </meta>
    <title>Babylon Template</title>
    <style>
        html,
        body {
            overflow: hidden;
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
        }

        #renderCanvas {
            width: 100%;
            height: 100%;
            touch-action: none;
        }

        #holder {
            width: 80%;
            height: 100%;
            float: left;
        }

        #instructions {
            width: 20%;
            height: 100%;
            float: left;
            background-color: grey;
        }
    </style>
    <script src="https://cdn.babylonjs.com/babylon.js"></script>
    <script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
    <script src="https://code.jquery.com/pep/0.4.3/pep.js"></script>
</head>

<body>
    <div id="holder">
        <!-- touch-action="none" 以获得 PEP 的最佳结果 -->
        <canvas id="renderCanvas" touch-action="none"></canvas>
    </div>
    <div id="instructions">
        <br>
        <h2>Instructions</h2>
        <br>
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
        Instructions Instructions Instructions Instructions Instructions
    </div>
    <script>
        // 获取画布元素
        const canvas = document.getElementById("renderCanvas");
        // 生成 BABYLON 3D 引擎
        const engine = new BABYLON.Engine(canvas, true);
        const createScene = () => {
            const scene = new BABYLON.Scene(engine);
            buildDwellings();
            // 设置摄像机和光源
            const camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2.5, 15, new BABYLON
                .Vector3(0, 0, 0));
            camera.attachControl(canvas, true);
            const light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(1, 1, 0));
            return scene;
        }
        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;
            // 每一项都是一个数组[房屋类型,旋转,x,z]
            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];
            }
        }
        const buildGround = () => {
            // 地面材质
            const groundMat = new BABYLON.StandardMaterial("groundMat");
            groundMat.diffuseColor = new BABYLON.Color3(0, 1, 0);
            const ground = BABYLON.MeshBuilder.CreateGround("ground", {
                width: 15,
                height: 16
            });
            ground.material = groundMat;
        }
        const buildHouse = (width) => {
            const box = buildBox(width);
            const roof = buildRoof(width);
            return BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
        }
        const buildBox = (width) => {
            // 盒子材质和纹理
            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);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.4, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.4, 0, 0.6, 1.0);
            } else {
                // 后面
                faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0);
                // 前面
                faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0);
                // 右面
                faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0);
                // 左面
                faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0);
            }
            // 顶部 4 和底部 5 看不到所以没有设置
            /**** World Objects *****/
            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 roofMat = new BABYLON.StandardMaterial("roofMat");
            roofMat.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg");
            const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {
                diameter: 1.3,
                height: 1.2,
                tessellation: 3
            });
            roof.material = roofMat;
            roof.scaling.x = 0.75;
            roof.scaling.y = width;
            roof.rotation.z = Math.PI / 2;
            roof.position.y = 1.22;
            return roof;
        }
        // 调用 createScene 函数
        const scene = createScene();
        // 注册渲染循环以重复渲染场景
        engine.runRenderLoop(function () {
            scene.render();
        });
        // 监视浏览器/画布调整大小事件
        window.addEventListener("resize", function () {
            engine.resize();
        });
    </script>
</body>

</html>
 类似资料: