场景层级基础概念

(一)场景(Scene)对象

在 Three.js 中,THREE.Scene是整个 3D 场景的容器,它是场景层级结构的根节点。可以将场景想象成一个空的舞台,所有的 3D 元素(如几何体、光源、相机等)都要被添加到这个舞台上才能在最终渲染中呈现。

创建一个场景非常简单,只需如下代码:

js 复制代码
const scene = new THREE.Scene();

这个scene对象将作为所有后续添加到场景中的对象的父级。

(二)对象 3D(Object3D)基类

THREE.Object3D是 Three.js 中几乎所有对象的基类,包括THREE.Scene、THREE.Mesh(网格,用于表示 3D 物体)、THREE.Group(组,用于将多个对象组合在一起)、THREE.Light(光源)等。这意味着这些对象都继承了Object3D的属性和方法,其中与场景层级密切相关的是add、remove方法以及children属性。

  1. add 方法:用于将一个或多个子对象添加到当前对象中。例如,如果我们有一个场景对象scene和一个网格对象mesh,可以通过以下方式将mesh添加到scene中:
js 复制代码
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
  1. remove 方法:与add方法相反,用于从当前对象中移除一个或多个子对象。假设我们要从scene中移除刚才添加的mesh,可以这样做:
js 复制代码
scene.remove(mesh);
  1. children 属性:该属性是一个数组,包含了当前对象的所有直接子对象。通过访问children属性,可以遍历和操作当前对象的子对象。例如,要遍历scene中的所有子对象,可以使用以下代码:
js 复制代码
scene.children.forEach((child) => {
    // 在这里对每个子对象进行操作
    console.log(child);
});

(三)组(Group)对象

THREE.Group对象是Object3D的一个子类,它的主要作用是将多个对象组合成一个逻辑单元。通过将相关的对象组合到一个组中,可以方便地对这些对象进行统一的变换(如移动、旋转、缩放)和管理。

例如,我们要创建一个由多个部分组成的复杂物体(如一辆汽车,包含车身、车轮等),可以将每个部分作为一个独立的Mesh对象,然后将这些Mesh对象添加到一个Group对象中。

js 复制代码
// 创建车身网格
const bodyGeometry = new THREE.BoxGeometry(1, 0.5, 2);
const bodyMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const bodyMesh = new THREE.Mesh(bodyGeometry, bodyMaterial);
// 创建车轮网格
const wheelGeometry = new THREE.CylinderGeometry(0.1, 0.1, 0.2, 32);
const wheelMaterial = new THREE.MeshStandardMaterial({ color: 0x000000 });
const wheelMesh1 = new THREE.Mesh(wheelGeometry, wheelMaterial);
const wheelMesh2 = new THREE.Mesh(wheelGeometry, wheelMaterial);
const wheelMesh3 = new THREE.Mesh(wheelGeometry, wheelMaterial);
const wheelMesh4 = new THREE.Mesh(wheelGeometry, wheelMaterial);
// 将车轮网格放置在合适位置
wheelMesh1.position.set(-0.8, -0.2, 0.8);
wheelMesh2.position.set(0.8, -0.2, 0.8);
wheelMesh3.position.set(-0.8, -0.2, -0.8);
wheelMesh4.position.set(0.8, -0.2, -0.8);
// 创建组对象并添加车身和车轮
const carGroup = new THREE.Group();
carGroup.add(bodyMesh);
carGroup.add(wheelMesh1);
carGroup.add(wheelMesh2);
carGroup.add(wheelMesh3);
carGroup.add(wheelMesh4);
// 最后将组对象添加到场景中
scene.add(carGroup);

这样,当我们对carGroup进行移动、旋转或缩放操作时,组内的所有对象(车身和车轮)都会相应地发生变化。

二、场景层级中的坐标系统

(一)世界坐标(World Coordinates)

世界坐标是整个场景的全局坐标系统。场景中的每个对象都有一个相对于世界坐标系原点(0, 0, 0)的位置。当对象直接添加到Scene对象下时,它的位置就是以世界坐标来表示的。

例如,创建一个简单的立方体并将其添加到场景中:

js 复制代码
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
cubeMesh.position.set(2, 1, 3); // 在世界坐标中设置位置
scene.add(cubeMesh);

这里cubeMesh的位置(2, 1, 3)就是相对于世界坐标系原点的位置。

(二)局部坐标(Local Coordinates)

局部坐标是相对于对象自身的坐标系统。每个对象都有自己的局部坐标系,其原点位于对象自身的中心(对于大多数几何体而言)。当一个对象作为另一个对象的子对象时,它的位置、旋转和缩放都是相对于其父对象的局部坐标系来定义的。

回到前面汽车的例子,车轮作为carGroup的子对象,它们的位置(如wheelMesh1.position.set(-0.8, -0.2, 0.8))就是相对于carGroup的局部坐标系的。这意味着当carGroup在世界坐标系中移动、旋转时,车轮会基于它们在carGroup局部坐标系中的位置相应地移动和旋转,而无需重新计算它们在世界坐标系中的绝对位置。

理解世界坐标和局部坐标的转换关系非常重要。Three.js 提供了一些方法来进行坐标转换,例如localToWorld方法可以将局部坐标转换为世界坐标,worldToLocal方法则相反。假设我们有一个对象obj,要将其局部坐标(1, 1, 1)转换为世界坐标,可以这样做:

js 复制代码
const localVector = new THREE.Vector3(1, 1, 1);
const worldVector = localVector.clone();
obj.localToWorld(worldVector);
console.log(worldVector); // 输出转换后的世界坐标

三、场景层级与渲染

(一)渲染顺序

Three.js 在渲染场景时,会从场景层级结构的根节点(即Scene对象)开始,递归地遍历所有子对象,并按照它们在层级结构中的顺序进行渲染。这意味着先添加到场景中的对象会先被渲染,后添加的对象会在上面进行渲染。

例如,如果我们先添加一个蓝色的立方体,然后在相同位置添加一个红色的球体,那么球体将会覆盖在立方体之上显示,因为球体是后被渲染的。

js 复制代码
// 先添加蓝色立方体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0x0000ff });
const cubeMesh = new THREE.Mesh(cubeGeometry, cubeMaterial);
scene.add(cubeMesh);
// 后添加红色球体
const sphereGeometry = new THREE.SphereGeometry(0.5, 32, 32);
const sphereMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const sphereMesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphereMesh.position.set(0, 0, 0);
scene.add(sphereMesh);

在这个例子中,球体由于后添加到场景中,所以会显示在立方体之上。

(二)可见性与剔除

场景层级结构对于控制对象的可见性和剔除(culling)非常有用。每个Object3D对象都有一个visible属性,通过设置该属性可以控制对象是否在渲染中显示。当一个对象的visible属性设置为false时,它及其所有子对象都不会被渲染。

js 复制代码
const group = new THREE.Group();
const mesh1 = new THREE.Mesh(geometry1, material1);
const mesh2 = new THREE.Mesh(geometry2, material2);
group.add(mesh1);
group.add(mesh2);
scene.add(group);
// 设置组不可见,组内的mesh1和mesh2都不会被渲染
group.visible = false;

此外,Three.js 的渲染器会自动进行一些剔除操作,以提高渲染效率。例如,对于背对摄像机的面(默认情况下,Three.js 使用背面剔除),渲染器不会对其进行渲染。在复杂场景中,合理利用场景层级结构和对象的可见性设置,可以进一步优化渲染性能,减少不必要的渲染计算。

四、场景层级的遍历与查找

(一)遍历场景层级

在开发过程中,有时需要遍历整个场景层级结构,对每个对象进行特定的操作。由于场景层级是一个树状结构,我们可以使用递归算法来实现遍历。

下面是一个简单的递归函数,用于遍历场景中的所有对象并打印它们的名称(假设每个对象都有name属性):

js 复制代码
function traverseScene(node) {
    console.log(node.name);
    node.children.forEach((child) => {
        traverseScene(child);
    });
}
// 从场景根节点开始遍历
traverseScene(scene);

这个函数首先打印当前节点的名称,然后递归地调用自身来遍历当前节点的所有子节点。通过这种方式,可以访问到场景中的每一个对象。

(二)查找特定对象

在复杂的场景中,可能需要根据特定的条件查找某个对象。例如,根据对象的名称查找对象。我们可以在遍历场景层级的基础上实现对象查找功能。

js 复制代码
function findObjectByName(node, name) {
    if (node.name === name) {
        return node;
    }
    for (let i = 0; i < node.children.length; i++) {
        const found = findObjectByName(node.children[i], name);
        if (found) {
            return found;
        }
    }
    return null;
}
// 查找名称为"specificMesh"的对象
const specificMesh = findObjectByName(scene, "specificMesh");
if (specificMesh) {
    // 对找到的对象进行操作
    specificMesh.material.color.set(0xff00ff);
}

这个函数在遍历场景层级的过程中,检查每个对象的名称是否与目标名称匹配。如果匹配,则返回该对象;如果在当前节点及其子节点中都没有找到匹配的对象,则返回null。

通过对 Three.js 场景层级的深入理解,包括场景层级的基础构成、坐标系统、渲染相关以及遍历查找等方面,开发者能够更加高效地构建、管理和优化复杂的 3D 场景,实现丰富的 3D 交互体验。

相关推荐
七月丶2 分钟前
🧼 为什么我开始在项目里禁用 CSS 文件?
前端·javascript·后端
心系2 分钟前
vue项目引入marvinJS
前端·vue.js
李剑一3 分钟前
兄弟们,2025年了!求求你优化一下图片加载吧
前端·vue.js
好柿会發生3 分钟前
关于chartjs的简单使用,各位大佬有知道什么办法通过js设置图表的宽高嘛
javascript·vue.js
20264 分钟前
7.抽奖功能lucky-canvas总结
前端
jserTang5 分钟前
前端构建工具漫谈:Webpack、Vite、Turbopack 与 Rspack 的终极对决
前端·webpack·vite
飞飞鱼在摸鱼5 分钟前
document.getElementById("chart") 和 this.$refs.chart的区别
前端
海底火旺6 分钟前
【灵魂拷问】你的JS类型检测真的正确吗?Object.prototype.toString.call才是永远的神!
前端·javascript·面试
前端太佬7 分钟前
Vue3异步数据加载的陷阱与最佳实践:从内存泄漏到优雅实现
前端·javascript·vue.js
小old弟8 分钟前
📊📈数据可视化大屏 —— 基于 Vue 3 和 Echarts 5📊📈
前端