Three.js——七、Group层级模型

711 阅读2分钟

关于Group

Group 层级模型

通过 THREE.Group 类创建一个组对象 group,然后通过 add 方法把网格模型 mesh1、mesh2 作为设置为组对象 group 的子对象,然后在通过执行 scene.add(group)把组对象 group 作为场景对象的 scene 的子对象。也就是说场景对象是 scene 是 group 的父对象,group 是 mesh1、mesh2 的父对象。

其实就是在场景上面追加了一层画布,可以将模型放在这个画布中,所以形成了三级结构 scene=>group=>mesh1+mesh2

const geometry = new THREE.BoxGeometry(100, 100, 100);
// 材质
material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
const group = new THREE.Group(); // 创建一个画布
const mesh1 = new THREE.Mesh(geometry, material); // 创建两个相大小和材质的正方体
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(105);
group.add(mesh1);
group.add(mesh2);
scene.add(group);

效果:请添加图片描述

group.children

可以查看 group 中存在的对象 请添加图片描述

scene.children

查看场景对象的结构 请添加图片描述

场景对象 Scene 的子对象,除了组对象 Group 之外,还可以看到环境光 AmbientLight、平行光 DirectionalLight、辅助坐标对象 AxesHelper。

.add()方法

添加多个场景时,可以简写成

group.add(mesh1, mesh2);

父级 group 旋转平移

如果我们这里让 group 这个父级平移旋转缩放等操作,那么所有的子对象也会同样的跟着改变。

group.translateY(100);

请添加图片描述

Object3D模型对象节点

其实它从某种程度上是和 group 一样的,Object3D 是表示模型节点的意思

const geometry = new THREE.BoxGeometry(100, 100, 100);
// 材质
material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
const mesh1 = new THREE.Mesh(geometry, material); // 创建两个相大小和材质的正方体
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(105);

// mesh = new THREE.Mesh(geometry, material);
const obj = new THREE.Object3D(); //作为mesh1和mesh2的父对象
obj.add(mesh1, mesh2);
obj.translateY(100);
console.log(obj);
scene.add(obj);

请添加图片描述

模型嵌套模型(mesh 中添加mesh2子对象)

const geometry = new THREE.BoxGeometry(100, 100, 100);
// 材质
material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
const mesh1 = new THREE.Mesh(geometry, material); // 创建两个相大小和材质的正方体
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(105);
mesh1.add(mesh2);
scene.add(mesh1);

请添加图片描述

遍历模型树结构、查询模型节点

比如我们可以对层级模型对象起个名字来标记这个模型是做什么的

.name属性

const geometry = new THREE.BoxGeometry(100, 100, 100);
// 材质
material = new THREE.MeshLambertMaterial({ color: 0x00ffff });
group = new THREE.Group();
group.name = "积木房";
const mesh1 = new THREE.Mesh(geometry, material);
mesh1.name = "积木房1层";
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.name = "积木房2层";
mesh2.translateY(101);
group.add(mesh1, mesh2);
scene.add(group);
console.log(group);

请添加图片描述

结构模型设置.name属性

我们在开发的时候,会优先加载外部的模型,之后要对模型上添加标签锕,标点啊等等,就需要批量给子对象设置节点名称 请添加图片描述

看个例子

const group1 = new THREE.Group();
group1.name = "一号楼";
for (let i = 0; i < 5; i++) {
  const geometry = new THREE.BoxGeometry(20, 60, 10);
  const material = new THREE.MeshBasicMaterial({ color: 0x00ffff });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.position.x = i * 30;
  group1.add(mesh);
  mesh.name = i + 1 + "号楼";
}
group1.position.y = 30;

const group2 = new THREE.Group();
group2.name = "二号楼";
for (let i = 0; i < 5; i++) {
  const geometry = new THREE.BoxGeometry(20, 30, 10);
  const material = new THREE.MeshLambertMaterial({
    color: 0x00ff90,
  });
  const mesh = new THREE.Mesh(geometry, material);
  mesh.position.x = i * 30;
  group2.add(mesh);
  mesh.name = i + 6 + "号楼";
}
group2.position.z = 50;
group2.position.y = 15;

const model = new THREE.Group();
model.name = "小区房子";
model.add(group1, group2);
model.position.set(-50, 0, -25);

console.log(model);
scene.add(model);

效果: 请添加图片描述

查看结构: 请添加图片描述

请添加图片描述

traverse方法

    model.traverse(function (obj) {
      console.log("节点名称", obj.name);
      // obj.isMesh:if判断模型对象obj是不是网格模型'Mesh'
      if (obj.isMesh) {
        //判断条件也可以是obj.type === 'Mesh'
        obj.material.color.set(0xffff00);
      }
    });

请添加图片描述

查找具体的某个模型.getObjectByName()

.getObjectByName()方法可以通过name获取dom元素

    const nameNode = scene.getObjectByName('6号楼');
    console.log(nameNode);
    nameNode.material.color.set(0xfc2000)

请添加图片描述

最后附上代码:

<!-- author: Mr.J -->
<!-- description: Vue3+JS代码块模板 -->
<template>
  <div class="container" ref="container"></div>
</template>

<script setup>
import { join } from "path";
import * as THREE from "three";
// 轨道
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { ref, reactive, onMounted } from "vue";
// 三个必备的参数
let scene, camera, renderer, controls, mesh, material, group;

onMounted(() => {
  // 外层需要获取到dom元素以及浏览器宽高,来对画布设置长宽
  // clientWidth等同于container.value.clientWidth
  let container = document.querySelector(".container");
  const { clientWidth, clientHeight } = container;
  console.log(clientHeight);

  // 首先需要获取场景,这里公共方法放在init函数中
  const init = () => {
    scene = new THREE.Scene();
    // 给相机设置一个背景
    scene.background = new THREE.Color(0xaaaaaa);
    // 透视投影相机PerspectiveCamera
    // 支持的参数:fov, aspect, near, far
    camera = new THREE.PerspectiveCamera(
      60,
      clientWidth / clientHeight,
      0.001,
      6000
    );
    // 相机坐标
    camera.position.set(300, 300, 300);
    // 相机观察目标
    camera.lookAt(scene.position);
    // 渲染器
    renderer = new THREE.WebGLRenderer({
      antialias: true,
    });
    // 渲染多大的地方
    renderer.setSize(clientWidth, clientHeight);
    const axesHelper = new THREE.AxesHelper(150);
    scene.add(axesHelper);
    container.appendChild(renderer.domElement);
    addBox();
    console.log("查看当前屏幕设备像素比", window.devicePixelRatio);
  };
  init();
  function addBox() {
    const group1 = new THREE.Group();
    group1.name = "一号楼";
    for (let i = 0; i < 5; i++) {
      const geometry = new THREE.BoxGeometry(20, 60, 10);
      const material = new THREE.MeshBasicMaterial({ color: 0x00ffff });
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.x = i * 30;
      group1.add(mesh);
      mesh.name = i + 1 + "号楼";
    }
    group1.position.y = 30;

    const group2 = new THREE.Group();
    group2.name = "二号楼";
    for (let i = 0; i < 5; i++) {
      const geometry = new THREE.BoxGeometry(20, 30, 10);
      const material = new THREE.MeshLambertMaterial({
        color: 0x00ff90,
      });
      const mesh = new THREE.Mesh(geometry, material);
      mesh.position.x = i * 30;
      group2.add(mesh);
      mesh.name = i + 6 + "号楼";
    }
    group2.position.z = 50;
    group2.position.y = 15;

    const model = new THREE.Group();
    model.name = "小区房子";
    model.add(group1, group2);
    model.position.set(-50, 0, -25);
    console.log(model);
    scene.add(model);
/*     model.traverse(function (obj) {
      console.log("节点名称", obj.name);
      // obj.isMesh:if判断模型对象obj是不是网格模型'Mesh'
      if (obj.isMesh) {
        //判断条件也可以是obj.type === 'Mesh'
        obj.material.color.set(0xffff00);
      }
    }); */
    const nameNode = scene.getObjectByName('6号楼');
    console.log(nameNode);
    nameNode.material.color.set(0xfc2000);
  }

  // 相机控件
  const control = () => {
    controls = new OrbitControls(camera, renderer.domElement);
    controls.addEventListener("change", function () {
      // 浏览器控制台查看相机位置变化
      // console.log("camera.position", camera.position);
    });
  };
  control();

  // 光源
  const linght = () => {
    const pointLight = new THREE.PointLight(0xffffff, 1.0);
    pointLight.position.set(50, 50, 100);
    const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
    scene.add(pointLightHelper);
    scene.add(pointLight);
  };
  linght();
  const render = () => {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
  };
  render();
  window.addEventListener("resize", () => {
    // 更新摄像头
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  });
});
</script>

<style>
.container {
  width: 100%;
  height: 100vh;
  position: relative;
  z-index: 1;
}
</style>