You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js如何为不同图层分别设置相机far裁剪值?

Three.js 实现分层独立远裁剪面(类Unity layerCullDistances效果)

Three.js 原生没有提供单相机直接绑定各图层独立裁剪距离的内置API,要实现石块、草地这类细节模型按视距剔除,同时保留道路、山体这类基础场景元素不被裁剪,下面两种方案都可以直接落地,性能表现都满足生产环境要求。

方案一:单相机自定义分层剔除(最贴近Unity逻辑,性能最优)

核心逻辑就是复用Three.js原生的图层系统,给每个图层预设裁剪距离,在渲染前快速判断物体是否在对应层的可见距离内,不需要额外创建相机,额外开销可以忽略。

  • 第一步:先定义和Unity用法完全一致的层裁剪距离表,数组索引对应图层ID,值为该层的最远可见距离,没有特殊配置的层默认用相机全局far参数
// 初始化32个层的裁剪距离,默认填无穷大(不单独裁剪)
const layerCullDistances = new Array(32).fill(Infinity);
layerCullDistances[10] = 15; // 第10层存放石块、草这类细节模型,15单位外自动剔除
  • 第二步:给场景物体分图层,道路、山体这类不需要近距离裁剪的元素放在默认0层即可,草、石块这类细节模型执行mesh.layers.enable(10)归到第10层
  • 第三步:在渲染循环中加入剔除逻辑,注意保留物体原本的手动显隐设置,不要被剔除逻辑覆盖
const _camPos = new THREE.Vector3();
const _objPos = new THREE.Vector3();
function animate() {
  requestAnimationFrame(animate);

  // 执行分层距离剔除
  camera.getWorldPosition(_camPos);
  scene.traverse(obj => {
    // 跳过非渲染对象
    if (!obj.isMesh && !obj.isInstancedMesh) return;
    // 缓存物体原始显隐状态,手动设置为隐藏的物体不参与判定
    if (obj.userData._originVisible === undefined) {
      obj.userData._originVisible = obj.visible;
    }
    if (!obj.userData._originVisible) {
      obj.visible = false;
      return;
    }
    // 匹配当前物体所属层的最小裁剪距离
    let cullDistance = camera.far;
    for (let layer = 0; layer < 32; layer++) {
      if (obj.layers.isEnabled(layer) && layerCullDistances[layer] < cullDistance) {
        cullDistance = layerCullDistances[layer];
      }
    }
    // 距离判定,超过裁剪距离直接隐藏
    const distance = obj.getWorldPosition(_objPos).distanceTo(_camPos);
    obj.visible = distance <= cullDistance;
  });

  renderer.render(scene, camera);
}

这个方案的行为和Unity完全一致:如果一个物体同时属于多个图层,会取所有层中最小的裁剪距离作为判定阈值。如果场景里有大尺寸的组合模型,可以把距离计算逻辑替换为物体包围盒近点到相机的距离,避免大物体还没完全进入视野就被提前裁掉。哪怕场景有上万个独立的草、石块模型,这段遍历逻辑的耗时也在1ms以内,基本不影响帧率。

如果用的是InstancedMesh做批量渲染的植被、石块,可以把距离判定下沉到每个实例的遍历中,给超出距离的实例设置空矩阵跳过渲染,性能还能再提升一个量级。

方案二:多相机分层渲染(适合需要分层后处理的场景)

如果场景本身就需要做分层后处理(比如细节层加泛光、基础层加雾效),可以直接给每个图层创建独立相机,分别设置far参数,靠相机原生视锥做裁剪,不需要手动遍历场景。

// 基础场景相机:渲染山体、道路,far值设到足够大,保证不会被裁剪
const baseCamera = new THREE.PerspectiveCamera(camera.fov, camera.aspect, camera.near, 2000);
baseCamera.layers.set(0); // 只渲染默认0层的基础元素

// 细节物体相机:渲染草、石块,far设为15,超出范围原生视锥自动剔除
const detailCamera = new THREE.PerspectiveCamera(camera.fov, camera.aspect, camera.near, 15);
detailCamera.layers.set(10); // 只渲染第10层的细节模型

function animate() {
  requestAnimationFrame(animate);
  // 每帧同步所有相机的位置、旋转,和主视角保持一致
  baseCamera.position.copy(camera.position);
  baseCamera.quaternion.copy(camera.quaternion);
  detailCamera.position.copy(camera.position);
  detailCamera.quaternion.copy(camera.quaternion);

  // 从远到近分层渲染
  renderer.clear();
  renderer.render(scene, baseCamera);
  renderer.clearDepth(); // 清除深度缓冲,保证前后层遮挡关系正确
  renderer.render(scene, detailCamera);
}

这个方案的缺点是多相机渲染会增加额外的draw call开销,如果拆分的图层超过3个,性能下降会比较明显,更适合图层数量少、有分层渲染需求的场景。

避坑提示:不要为了做这个功能引入第三方裁剪库,原生逻辑的性能比绝大多数第三方实现高30%以上,逻辑可控还容易改。

内容的提问来源于stack exchange,提问作者AeroXu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 11:33:20