在 Three.js 中,控制相机的裁剪和视野是3D场景渲染的重要部分。以下是主要方法和技巧:
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(); // 必须调用!
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;
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
);
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);
}
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;
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();
}
}
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); // 使用不同层
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;
}
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();
}
合理设置裁剪范围:尽量缩小远裁剪平面以提升深度缓冲精度
避免太小的近平面:可能导致深度缓冲精度问题
使用分层渲染:将UI和背景分离到不同裁剪范围
动态调整:根据场景内容动态计算合适的裁剪平面
这些技巧可以帮助你更好地控制 Three.js 相机的视野和裁剪,创建更专业、性能更好的3D应用。