Babylon加载本地gltf模型并完成一些基础的点击操作

Babylon加载本地gltf模型并完成一些基础的点击操作

加载模型

本文使用的是vue3+vite搭建的环境,在搭建好环境后安装babylon

npm install --save @babylonjs/core

然后按需引入babylon模块

import {
  Engine,
  Scene,
  Vector3,
  Vector2,
  HemisphericLight,
  ArcRotateCamera,
  SceneLoader,
  HighlightLayer,
  Color3,
  Mesh,
  StandardMaterial,
  PBRMaterial 
} from '@babylonjs/core'

当然也可以全部导入

npm install --save babylonjs
import * as BABYLON from "babylonjs";

要使用babylon加载模型到web页面上,首先要先创建一个画布

 <div id="renderCanvasContainer">
      <canvas id="renderCanvas" ref="renderCanvas"></canvas>
 </div>

然后获取这个dom元素(原生JS)

var canvas = document.getElementById('renderCanvas');

vue3也可以这么写(基于创建画布时的ref标签获取)

const renderCanvas = ref(null)
const canvas = renderCanvas.value

而后实例化一个渲染引擎,并创建场景

var engine = new BABYLON.Engine(canvas, true);
const scene = new Scene(engine)

特别的你可以让页面多一个调试面板,只需在场景创建后调用debug,但是要安装@babylonjs/inspector包,如果你是分模块导入的话,如果是全部导入就不需要了

npm install --save @babylonjs/inspector

然后在刚刚实例化的scene下加入下面这句代码就行了

scene.debugLayer.show();

在这里插入图片描述
而后我们需要创建一个相机

      const camera = new ArcRotateCamera(
		 'camera',      // 相机的名称
		  Math.PI / 6,   // 水平旋转角度
		  Math.PI / 4,   // 垂直旋转角度
		  2000,          // 相机到目标点的距离
		  new Vector3(0, 0, 0), // 目标点
		  scene          // 相机所在的场景(刚刚创建的场景)
      )

下一步将相机绑定到画布上,相当于把刚才创建的相机安装到指定位置,使得用户能够通过鼠标、键盘来控制相机。

camera.attachControl(canvas, true);//第一个参数是html元素,最开始我们创建的画布,第二个是布尔值

好了有了场景和相机后下面请需要开灯,这样场景就亮起来了

const light = new HemisphericLight(
  'light',            // 光源的名称,用于标识这个光源
  new Vector3(1, 1, 0), // 光的方向,这是一个向量,表示光从 (1, 1, 0) 方向投射到场景中
  scene               // 光源所在的场景
);
light.intensity = 0.7; // 设置光照强度为 0.7

这样一切准备就绪就可以导入我们的本地模型了,把模型放入public文件夹,最好在新建一个model,把gltf所有文件放进去

SceneLoader.ImportMesh(
          '', // 要导入的特定网格的名称,空字符串表示导入所有网格
          '/model/', // 模型文件的路径
          'yy.gltf', // 模型文件的名称
          scene, // 要将模型导入到的目标场景
          function (meshes) { // 回调函数,处理加载完成后的操作
		    // meshs是模型中的所有网格,是模型的基本组成后续要实现各种交互需要了解。
		    // 一会的鼠标监听事件就写在这里
		    console.log('模型已加载', meshes);
		  }
          )

然后启动一个渲染循环,确保你在更新场景后重新渲染场景

engine.runRenderLoop(function () {
  scene.render();
});

然后加一个窗口大小改变事件,是的浏览器窗口改变后引擎和画布大小也相应调整

window.addEventListener('resize', function () {
  engine.resize();
});

以上就完成了本地模型的加载显示

控制模型某个部分的显示与隐藏

想要对模型进行操作,比如对某一部分进行显示与隐藏,高亮或改变颜色,你首先要了解gltf的基本构成,这个大家可以从刚才打开的debug的左侧面板观察
在这里插入图片描述
就比如我这个模型是个医院场景,我想控制楼层的显示与隐藏,那么我就要先获取医院节点,然后获取医院节点下的所有子节点,你自己看下楼层(或者你想控制的mesh的name)设计一个逻辑,控制楼层的显示与隐藏,下面是我的例子

import { Scene } from '@babylonjs/core';
export default class FloorController {
  scene:Scene;          // 声明scene是个Babylon场景类型变量
  constructor(scene) {  // 这个类接受一个scene并传递给实例属性this.scene
    this.scene = scene;
  }

  chooseFloor(num) {
    this.changeFloorEnabled(num);
    this.changeOtherEnabled(!Boolean(num));
  }

  changeFloorEnabled(num) {
    const name = 'F' + num;
    const hosp = this.scene.getTransformNodeByName('hosp');

    const children = hosp?.getChildren();
    if (!num && children) {
      this.showFacade(children);
      return;
    }

    if (children) {
      for (const iterator of children) {
        iterator.setEnabled(iterator.name === name);
      }
    }
  }

  showFacade(list) {
    if (list) {
      for (const iterator of list) {
        iterator.setEnabled(true);
      }
    }
  }

  changeOtherEnabled(bool) {
    const circumstances = this.scene.getTransformNodeByName('circumstances');
    if (circumstances) {
      circumstances.setEnabled(bool);// 控制节点的显示与交互
    }
  }
}

  changeOtherEnabled(bool) {
    const circumstances = scene.getTransformNodeByName('circumstances');
    if (circumstances) {
      circumstances.setEnabled(bool);// 控制节点的显示与交互
    }
  }

然后你可以设计一个点击事件来执行这个操作,比如设计一个按钮列表控制楼层的显示和隐藏

          <div id="buttonContainer">
            <el-button
              v-for="floor in Array.from({ length: 15 }, (v, k) => k + 1).reverse()"
              :key="floor"
              type="primary"
              @click="handleFloorClick(floor)"
              class="floor-button"
            >
              {{ floor }}</el-button>
          </div>

然后是这个点击函数,不过之前我们把显示和隐藏的逻辑写到了另一个ts文件中,这里使用需要先引入

import FloorController from '../scenes/hospitalController' // 根据实际路径调整


    const handleFloorClick = (floor) => {
      console.log(`Clicked ${floor}`)
      if (floorController.value) {
        floorController.value.chooseFloor(floor)  // 分别调用changeFloorEnabled和changeOtherEnabled设置选中的楼层可见和其他场景不可见
      }
    }

添加模型的点击事件

假如我们想获取我们点击模型那部分网格的属性信息,并且改变他的颜色,我们可以在引入模型时添加监听器,还记得刚刚导入模型时的回调函数吗,具体的实现逻辑就写在这里面

		SceneLoader.ImportMesh(
          '', // 要导入的特定网格的名称,空字符串表示导入所有网格
          '/model/', // 模型文件的路径
          'yy.gltf', // 模型文件的名称
          scene, // 要将模型导入到的目标场景
          (meshes) => { // 导入完成后的回调函数
            // 添加点击事件监听器
            canvas.addEventListener('pointerdown', (evt) => {
              // 使用 scene.pick 获取鼠标点击的网格
              const pickResult = scene.pick(scene.pointerX, scene.pointerY);

              // 检查是否拾取到网格
              if (pickResult.hit) {
                const pickedMesh = pickResult.pickedMesh;
                console.log('Picked mesh:', pickedMesh);

                // 打印网格的所有属性
                console.log('Mesh name:', pickedMesh.name);
                console.log('Mesh position:', pickedMesh.position);
                console.log('Mesh rotation:', pickedMesh.rotation);
                console.log('Mesh scaling:', pickedMesh.scaling);
                console.log('Mesh material:', pickedMesh.material);

                // 如果之前有选中的网格,恢复其原始颜色
                if (selectedMesh && selectedMesh !== pickedMesh) {
                        if (selectedMesh.material) {
                          if (selectedMesh.material instanceof PBRMaterial) {
                            selectedMesh.material.albedoColor = originalColors.get(selectedMesh).clone();
                          } else if (selectedMesh.material instanceof StandardMaterial) {
                            selectedMesh.material.diffuseColor = originalColors.get(selectedMesh).clone();
                          }
                        }
                      }

                      // 记录当前选中的网格及其原始颜色
                      selectedMesh = pickedMesh;
                      if (!originalColors.has(pickedMesh)) {
                        if (pickedMesh.material) {
                          if (pickedMesh.material instanceof PBRMaterial) {
                            originalColors.set(pickedMesh, pickedMesh.material.albedoColor.clone());
                          } else if (pickedMesh.material instanceof StandardMaterial) {
                            originalColors.set(pickedMesh, pickedMesh.material.diffuseColor.clone());
                          }
                        }
                      }

                      // 修改选中网格的颜色
                      if (pickedMesh.material) {
                        if (pickedMesh.material instanceof PBRMaterial) {
                          pickedMesh.material.albedoColor = new Color3(0, 1, 0); // 绿色
                        } else if (pickedMesh.material instanceof StandardMaterial) {
                          pickedMesh.material.diffuseColor = new Color3(0, 1, 0); // 绿色
                        }
                      } else {
                        // 如果没有材质,创建一个新的 StandardMaterial 并应用
                        const material = new StandardMaterial('newMaterial', scene);
                        material.diffuseColor = new Color3(0, 1, 0); // 绿色
                        pickedMesh.material = material;
                        originalColors.set(pickedMesh, material.diffuseColor.clone());
                      }

              }
            });
          }
        );

当然你需要定义一些变量

    const originalColors = new Map();   // 存储训中mesh的颜色
    let selectedMesh = null;   // 存储选中的mesh

这样应该就没问题了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值