three Frustum 视锥体

188 阅读3分钟

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);