Frustum(视锥体)API 的主要用途是 提升性能 和 检测可见性。它提供了对场景中哪些物体位于相机视野内的判断,从而避免渲染不可见的物体。这个 API 在 3D 图形和游戏开发中非常重要,尤其在处理复杂场景和高性能需求时,Frustum可以帮助显著减少不必要的计算。
Frustum 有一个属性 九个方法
一些使用示例
Frustum(p0 : Plane, p1 : Plane, p2 : Plane, p3 : Plane, p4 : Plane, p5 : Plane)
p0 - (可选参数) Plane.
p1 - (可选参数) Plane.planes : Array
p2 - (可选参数) Plane.
p3 - (可选参数) Plane.
p4 - (可选参数) Plane.
p5 - (可选参数) Plane.
// 渲染优化: 在一个大型 3D 游戏或虚拟场景中,场景中有数百个物体,但是玩家(通过相机)一次只能看到一部分。如果没有 Frustum 裁剪,渲染引擎仍然会处理所有物体,即使这些物体根本不在屏幕上。使用 Frustum 裁剪可以显著减少 GPU 和 CPU 的负载。
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.updateMatrixWorld();
const frustumMatrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
frustum.setFromProjectionMatrix(frustumMatrix);
scene.children.forEach(object => {
if (frustum.intersectsObject(object)) {
// 物体在视锥体内,渲染它
object.visible = true;
} else {
// 物体在视锥体外,隐藏它,避免渲染
object.visible = false;
}
});
// 触发事件: 在某些游戏中,当一个物体进入视野时,可能需要触发一些事件。例如: 玩家角色看到敌人后,敌人开始追逐玩家。 当物体进入视锥体时加载纹理或者其他资源,减少不必要的加载。
const frustum = new THREE.Frustum();
const enemy = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshBasicMaterial());
const isEnemyVisible = frustum.intersectsObject(enemy);
if (isEnemyVisible) {
// 敌人进入视野,触发追逐行为
enemy.startChasingPlayer();
}
// **可见性优化 (Level of Detail)** : 在复杂的场景中,如果一个物体离相机很远,我们不需要渲染其所有的细节。相反,可以使用一个简化模型,节省资源。`Frustum` 可以判断物体是否可见,并根据物体与相机的距离切换不同细节层次的模型。
const object = new THREE.Mesh(highDetailGeometry, highDetailMaterial);
if (frustum.intersectsObject(object)) {
if (object.distanceToCamera > 50) {
// 使用简化的几何模型
object.geometry = lowDetailGeometry;
} else {
// 使用高细节的几何模型
object.geometry = highDetailGeometry;
}
}
属性
- planes : Array 包含6个平面 planes 的数组。
方法
- clone () : Frustum 返回一个与当前对象有相同参数的视锥体。
- containsPoint ( point : Vector3 ) : Boolean point - Vector3 被检测的点。
- copy ( frustum : Frustum ) : this frustum - 用于拷贝的视锥体。
- intersectsBox ( box : Box3 ) : Boolean box - Box3 用于检测是否要交的包围盒。 返回 true 如果该 box 与视锥体相交。
- intersectsObject ( object : Object3D ) : Boolean 检测 object 的包围球 bounding sphere 是否与视锥体相交。 注意:该对象必须有一个 BufferGeometry ,因为这样才能计算出包围球。
- intersectsSphere ( sphere : Sphere ) : Boolean sphere - Sphere 用于检查是否相交。 返回true 如果球sphere与视锥体相交。
- intersectsSprite ( sprite : Sprite ) : Boolean 检查精灵sprite是否与截锥体相交。
- set ( p0 : Plane, p1 : Plane, p2 : Plane, p3 : Plane, p4 : Plane, p5 : Plane ) : this 从传入的平面设置当前视锥体。没有隐式的顺序。 请注意,此方法仅复制给定对象的值。
- setFromProjectionMatrix ( matrix : Matrix4 ) : this matrix - 投影矩阵 Matrix4 会被用来设置该视椎体的 planes 根据投影矩阵 matrix 来设置当前视椎体的六个面。
// `setFromProjectionMatrix` 方法是将一个投影矩阵应用到视锥体(Frustum)上,从而根据这个矩阵设置视锥体的 6 个裁剪平面(近、远、左、右、上、下)。这些平面共同定义了相机的可见区域,任何位于这个区域之外的物体将被认为是不可见的。
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
// 更新相机的世界矩阵(确保投影矩阵是最新的)
camera.updateMatrixWorld();
// 创建一个 Frustum 实例
const frustum = new THREE.Frustum();
// 计算投影-视图矩阵 (projectionMatrix * matrixWorldInverse)
const projectionViewMatrix = new THREE.Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
// 使用投影-视图矩阵设置视锥体
frustum.setFromProjectionMatrix(projectionViewMatrix);
// 创建一个立方体
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 判断立方体是否在视锥体内
const isVisible = frustum.intersectsObject(cube);
console.log('立方体是否可见:', isVisible);