1. 相机裁剪与视野控制

在 Three.js 中,控制相机的裁剪和视野是3D场景渲染的重要部分。以下是主要方法和技巧:

1.1. 相机裁剪(Clipping)

1.1.1. 近平面和远平面裁剪

javascript

const camera = new THREE.PerspectiveCamera(
  75, // 视野角度
  window.innerWidth / window.innerHeight, // 宽高比
  0.1, // 近裁剪平面(near)
  1000  // 远裁剪平面(far)
);

// 动态调整裁剪平面
camera.near = 0.01; // 非常近的对象也能看到
camera.far = 5000;  // 更远的对象也能看到
camera.updateProjectionMatrix(); // 必须调用!

1.1.2. 自定义裁剪平面(Clipping Planes)

javascript

// 创建自定义裁剪平面
const localPlane = new THREE.Plane(new THREE.Vector3(0, -1, 0), 0.5);
camera.clippingPlanes = [localPlane];

// 启用裁剪
renderer.localClippingEnabled = true;

// 多个裁剪平面
const planes = [
  new THREE.Plane(new THREE.Vector3(-1, 0, 0), 1), // 左裁剪
  new THREE.Plane(new THREE.Vector3(0, -1, 0), 1), // 下裁剪
  new THREE.Plane(new THREE.Vector3(0, 0, -1), 1), // 近裁剪
];
renderer.clippingPlanes = planes;

1.2. 视野控制(FOV)

1.2.1. 基础FOV设置

javascript

// 透视相机(PerspectiveCamera)
const camera = new THREE.PerspectiveCamera(
  45, // FOV角度(垂直方向)
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);

// 正交相机(OrthographicCamera)
const aspect = window.innerWidth / window.innerHeight;
const orthoCamera = new THREE.OrthographicCamera(
  -10 * aspect, // left
  10 * aspect,  // right
  10,           // top
  -10,          // bottom
  0.1,
  1000
);

1.2.2. 动态调整FOV

javascript

// 响应窗口大小变化
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);
});

// 动画中调整FOV
function animate() {
  // 模拟变焦效果
  camera.fov = 45 + Math.sin(Date.now() * 0.001) * 10;
  camera.updateProjectionMatrix();

  requestAnimationFrame(animate);
}

1.3. 相机视图控制

1.3.1. 使用相机控制器

javascript

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

const controls = new OrbitControls(camera, renderer.domElement);

// 限制视野范围
controls.minDistance = 2; // 最小缩放距离
controls.maxDistance = 50; // 最大缩放距离
controls.minPolarAngle = 0; // 最小垂直角度(弧度)
controls.maxPolarAngle = Math.PI; // 最大垂直角度

// 限制平移
controls.enablePan = true;
controls.minAzimuthAngle = -Infinity;
controls.maxAzimuthAngle = Infinity;

1.3.2. 自定义视图约束

javascript

class CameraConstraints {
  constructor(camera) {
    this.camera = camera;
    this.minFOV = 20;
    this.maxFOV = 100;
    this.targetPosition = new THREE.Vector3(0, 0, 0);
  }

  applyConstraints() {
    // 约束FOV
    this.camera.fov = THREE.MathUtils.clamp(
      this.camera.fov,
      this.minFOV,
      this.maxFOV
    );

    // 约束相机位置(防止穿墙)
    const maxDistance = 50;
    const distance = this.camera.position.distanceTo(this.targetPosition);
    if (distance > maxDistance) {
      this.camera.position.sub(this.targetPosition)
        .normalize()
        .multiplyScalar(maxDistance)
        .add(this.targetPosition);
    }

    this.camera.updateProjectionMatrix();
  }
}

1.4. 高级技巧

1.4.1. 分层裁剪(用于UI等)

javascript

// 多个渲染通道,不同裁剪设置
const sceneMain = new THREE.Scene();
const sceneUI = new THREE.Scene();

// 主场景使用标准裁剪
cameraMain.near = 0.1;
cameraMain.far = 1000;

// UI场景使用特殊裁剪
cameraUI.near = 900;
cameraUI.far = 1000;
cameraUI.layers.set(1); // 使用不同层

1.4.2. 视锥体可视化(调试)

javascript

function createFrustumHelper(camera) {
  const frustum = new THREE.Frustum();
  const matrix = new THREE.Matrix4()
    .multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
  frustum.setFromProjectionMatrix(matrix);

  // 创建线框显示视锥体
  const helper = new THREE.FrustumHelper(frustum, 0xffff00);
  scene.add(helper);
  return helper;
}

1.4.3. 自适应裁剪平面

javascript

function autoAdjustClipping(scene, camera) {
  const boundingBox = new THREE.Box3();

  // 计算场景边界
  scene.traverse(object => {
    if (object.isMesh) {
      boundingBox.expandByObject(object);
    }
  });

  const size = boundingBox.getSize(new THREE.Vector3());
  const maxDim = Math.max(size.x, size.y, size.z);
  const center = boundingBox.getCenter(new THREE.Vector3());

  // 根据场景大小自动设置裁剪
  camera.near = maxDim / 1000;
  camera.far = maxDim * 10;

  // 调整相机位置
  camera.position.copy(center);
  camera.position.z += maxDim * 2;
  camera.lookAt(center);

  camera.updateProjectionMatrix();
}

1.5. 性能优化提示

  1. 合理设置裁剪范围:尽量缩小远裁剪平面以提升深度缓冲精度

  2. 避免太小的近平面:可能导致深度缓冲精度问题

  3. 使用分层渲染:将UI和背景分离到不同裁剪范围

  4. 动态调整:根据场景内容动态计算合适的裁剪平面

这些技巧可以帮助你更好地控制 Three.js 相机的视野和裁剪,创建更专业、性能更好的3D应用。