Babylon.js 相机系统 #
相机概述 #
相机决定了场景的观察视角。Babylon.js 提供了多种相机类型,适用于不同的应用场景。
text
┌─────────────────────────────────────────────────────────────┐
│ Babylon.js 相机类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ArcRotateCamera ─── 围绕目标旋转观察 │
│ - 适合:产品展示、模型查看 │
│ │
│ FreeCamera ──────── 自由移动相机 │
│ - 适合:第一人称游戏、漫游 │
│ │
│ UniversalCamera ─── 通用相机(FreeCamera 升级版) │
│ - 适合:需要多种输入方式的场景 │
│ │
│ FollowCamera ────── 跟随相机 │
│ - 适合:第三人称游戏、跟随角色 │
│ │
│ AnaglyphCamera ──── 立体相机 │
│ - 适合:VR/3D 立体效果 │
│ │
│ DeviceOrientationCamera ─ 设备方向相机 │
│ - 适合:移动端陀螺仪控制 │
│ │
│ WebXRCamera ─────── WebXR 相机 │
│ - 适合:VR/AR 应用 │
│ │
└─────────────────────────────────────────────────────────────┘
ArcRotateCamera #
ArcRotateCamera 是最常用的相机类型,围绕一个目标点旋转观察。
基本创建 #
javascript
const camera = new BABYLON.ArcRotateCamera(
'camera', // 名称
Math.PI / 2, // alpha - 水平旋转角度
Math.PI / 2, // beta - 垂直旋转角度
10, // radius - 与目标的距离
BABYLON.Vector3.Zero(), // target - 目标点
scene
);
// 附加控制
camera.attachControl(canvas, true);
参数详解 #
text
┌─────────────────────────────────────────────────────────────┐
│ ArcRotateCamera 参数 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 目标点 (Target) │
│ ● │
│ /│\ │
│ / │ \ │
│ / │ \ radius (半径) │
│ / │ \ │
│ / │ \ │
│ / beta│ \ │
│ / │ \ │
│ ●────────┼────────● │
│ 相机位置 │ alpha (水平角) │
│ │
│ alpha: 水平旋转角度(绕 Y 轴) │
│ 0 = 正前方,π/2 = 右侧,π = 后方 │
│ │
│ beta: 垂直旋转角度 │
│ 0 = 正上方,π/2 = 水平,π = 正下方 │
│ │
│ radius: 相机到目标的距离 │
│ │
└─────────────────────────────────────────────────────────────┘
常用属性 #
javascript
// 限制缩放距离
camera.lowerRadiusLimit = 2; // 最小距离
camera.upperRadiusLimit = 50; // 最大距离
// 限制垂直角度
camera.lowerBetaLimit = 0.1; // 最小角度(接近顶部)
camera.upperBetaLimit = Math.PI / 2; // 最大角度(水平)
// 限制水平角度
camera.lowerAlphaLimit = 0;
camera.upperAlphaLimit = Math.PI * 2;
// 平滑移动
camera.inertia = 0.9; // 惯性系数(0-1)
// 缩放速度
camera.wheelPrecision = 50; // 鼠标滚轮精度
camera.panningSensibility = 100; // 平移灵敏度
// 自动旋转
camera.useAutoRotationBehavior = true;
控制方式 #
javascript
// 默认控制
// 左键拖动:旋转
// 右键拖动:平移
// 滚轮:缩放
// 禁用特定控制
camera.panningSensibility = 0; // 禁用平移
// 键盘控制
camera.keysUp.push(87); // W
camera.keysDown.push(83); // S
camera.keysLeft.push(65); // A
camera.keysRight.push(68); // D
相机动画 #
javascript
// 平滑移动到目标位置
camera.alpha = 0;
camera.beta = Math.PI / 3;
camera.radius = 15;
// 使用动画
BABYLON.Animation.CreateAndStartAnimation(
'cameraMove',
camera,
'alpha',
30,
60,
camera.alpha,
Math.PI,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
// 使用过渡
const easingFunction = new BABYLON.QuadraticEase();
easingFunction.setEasingMode(BABYLON.EasingFunction.EASINGMODE_EASEINOUT);
BABYLON.Animation.CreateAndStartAnimation(
'cameraZoom',
camera,
'radius',
30,
30,
camera.radius,
5,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT,
easingFunction
);
FreeCamera #
FreeCamera 允许自由移动,适合第一人称视角。
基本创建 #
javascript
const camera = new BABYLON.FreeCamera(
'camera',
new BABYLON.Vector3(0, 5, -10), // 初始位置
scene
);
// 设置目标
camera.setTarget(BABYLON.Vector3.Zero());
// 附加控制
camera.attachControl(canvas, true);
控制方式 #
javascript
// 键盘控制
camera.keysUp.push(87); // W - 前进
camera.keysDown.push(83); // S - 后退
camera.keysLeft.push(65); // A - 左移
camera.keysRight.push(68); // D - 右移
// 鼠标控制
// 移动鼠标:旋转视角
// 移动速度
camera.speed = 1;
camera.angularSensibility = 2000; // 鼠标灵敏度
// 惯性
camera.inertia = 0.9;
碰撞检测 #
javascript
// 启用碰撞
camera.checkCollisions = true;
// 应用重力
camera.applyGravity = true;
// 设置碰撞范围(椭圆体)
camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
// 地面碰撞
ground.checkCollisions = true;
鼠标锁定 #
javascript
// 锁定鼠标指针
camera.minZ = 0.1;
// 点击后锁定
scene.onPointerDown = function() {
canvas.requestPointerLock();
};
UniversalCamera #
UniversalCamera 是 FreeCamera 的升级版,支持更多输入设备。
javascript
const camera = new BABYLON.UniversalCamera(
'camera',
new BABYLON.Vector3(0, 5, -10),
scene
);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
// 支持触摸、手柄等多种输入
FollowCamera #
FollowCamera 会自动跟随目标物体。
基本创建 #
javascript
const camera = new BABYLON.FollowCamera(
'followCam',
new BABYLON.Vector3(0, 10, -20),
scene
);
// 设置跟随目标
camera.lockedTarget = targetMesh;
// 跟随参数
camera.radius = 30; // 与目标的距离
camera.heightOffset = 10; // 高度偏移
camera.rotationOffset = 0; // 旋转偏移
camera.cameraAcceleration = 0.05; // 加速度
camera.maxCameraSpeed = 10; // 最大速度
camera.attachControl(canvas, true);
动态跟随 #
javascript
// 创建跟随目标
const target = BABYLON.MeshBuilder.CreateSphere('target', { diameter: 1 }, scene);
// 设置相机跟随
const followCamera = new BABYLON.FollowCamera('followCam', new BABYLON.Vector3(0, 10, -20), scene);
followCamera.lockedTarget = target;
// 移动目标,相机自动跟随
scene.onBeforeRenderObservable.add(() => {
target.position.x += 0.1;
});
DeviceOrientationCamera #
用于移动端陀螺仪控制。
javascript
const camera = new BABYLON.DeviceOrientationCamera(
'deviceCam',
new BABYLON.Vector3(0, 0, 0),
scene
);
camera.setTarget(BABYLON.Vector3.Zero());
// 检查设备方向支持
if (window.DeviceOrientationEvent) {
camera.attachControl(canvas, true);
}
相机切换 #
多相机管理 #
javascript
const scene = new BABYLON.Scene(engine);
// 创建多个相机
const arcCamera = new BABYLON.ArcRotateCamera('arc', Math.PI / 2, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene);
const freeCamera = new BABYLON.FreeCamera('free', new BABYLON.Vector3(0, 5, -10), scene);
// 设置活动相机
scene.activeCamera = arcCamera;
arcCamera.attachControl(canvas, true);
// 切换相机
function switchToFreeCamera() {
arcCamera.detachControl(canvas);
scene.activeCamera = freeCamera;
freeCamera.attachControl(canvas, true);
}
function switchToArcCamera() {
freeCamera.detachControl(canvas);
scene.activeCamera = arcCamera;
arcCamera.attachControl(canvas, true);
}
平滑切换 #
javascript
async function switchCameraSmooth(newCamera) {
const oldCamera = scene.activeCamera;
// 获取位置和目标
const startPos = oldCamera.position.clone();
const endPos = newCamera.position.clone();
// 动画过渡
const animation = new BABYLON.Animation(
'cameraSwitch',
'position',
30,
BABYLON.Animation.ANIMATIONTYPE_VECTOR3,
BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT
);
animation.setKeys([
{ frame: 0, value: startPos },
{ frame: 30, value: endPos }
]);
oldCamera.animations.push(animation);
await scene.beginAnimation(oldCamera, 0, 30, false).waitAsync();
// 切换到新相机
oldCamera.detachControl(canvas);
scene.activeCamera = newCamera;
newCamera.attachControl(canvas, true);
}
相机输入管理 #
输入类型 #
text
┌─────────────────────────────────────────────────────────────┐
│ 相机输入类型 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ArcRotateCamera 输入: │
│ - mouse: 鼠标控制 │
│ - touch: 触摸控制 │
│ - keyboard: 键盘控制 │
│ - mousewheel: 鼠标滚轮 │
│ - pointers: 指针输入 │
│ │
│ FreeCamera 输入: │
│ - mouse: 鼠标控制 │
│ - keyboard: 键盘控制 │
│ - touch: 触摸控制 │
│ - gamepad: 游戏手柄 │
│ │
└─────────────────────────────────────────────────────────────┘
管理输入 #
javascript
// 获取输入管理器
const inputs = camera.inputs;
// 禁用鼠标输入
inputs.removeByType('ArcRotateCameraMouseInput');
// 添加自定义输入
inputs.add(new MyCustomInput());
// 清除所有输入
inputs.clear();
// 重新添加默认输入
inputs.addMouseWheel();
inputs.addPointers();
inputs.addKeyboard();
自定义输入 #
javascript
class MyCustomInput {
constructor() {
this.keys = { left: 0, right: 0, up: 0, down: 0 };
}
attachControl(element) {
element.addEventListener('keydown', (e) => {
if (e.key === 'w') this.keys.up = 1;
if (e.key === 's') this.keys.down = 1;
if (e.key === 'a') this.keys.left = 1;
if (e.key === 'd') this.keys.right = 1;
});
element.addEventListener('keyup', (e) => {
if (e.key === 'w') this.keys.up = 0;
if (e.key === 's') this.keys.down = 0;
if (e.key === 'a') this.keys.left = 0;
if (e.key === 'd') this.keys.right = 0;
});
}
detachControl(element) {
element.removeEventListener('keydown', null);
element.removeEventListener('keyup', null);
}
checkInputs() {
const camera = this.camera;
if (this.keys.up) camera.alpha -= 0.01;
if (this.keys.down) camera.alpha += 0.01;
if (this.keys.left) camera.beta -= 0.01;
if (this.keys.right) camera.beta += 0.01;
}
getSimpleName() {
return 'customInput';
}
}
相机视口 #
多视口渲染 #
javascript
// 创建两个相机
const camera1 = new BABYLON.ArcRotateCamera('cam1', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
const camera2 = new BABYLON.ArcRotateCamera('cam2', Math.PI, 0, 10, BABYLON.Vector3.Zero(), scene);
// 设置视口
camera1.viewport = new BABYLON.Viewport(0, 0, 0.5, 1); // 左半边
camera2.viewport = new BABYLON.Viewport(0.5, 0, 0.5, 1); // 右半边
// 添加到活动相机列表
scene.activeCameras.push(camera1);
scene.activeCameras.push(camera2);
小地图视口 #
javascript
// 主相机
const mainCamera = new BABYLON.ArcRotateCamera('main', Math.PI / 2, Math.PI / 3, 20, BABYLON.Vector3.Zero(), scene);
mainCamera.viewport = new BABYLON.Viewport(0, 0, 1, 1);
// 小地图相机
const minimapCamera = new BABYLON.FreeCamera('minimap', new BABYLON.Vector3(0, 50, 0), scene);
minimapCamera.setTarget(BABYLON.Vector3.Zero());
minimapCamera.viewport = new BABYLON.Viewport(0.75, 0.75, 0.25, 0.25);
// 添加相机
scene.activeCameras.push(mainCamera);
scene.activeCameras.push(minimapCamera);
相机效果 #
后处理 #
javascript
// 添加后处理效果
const pipeline = new BABYLON.DefaultRenderingPipeline(
'defaultPipeline',
true,
scene,
[camera]
);
// 景深效果
pipeline.depthOfFieldEnabled = true;
pipeline.depthOfFieldBlurLevel = BABYLON.DepthOfFieldEffectBlurLevel.Medium;
pipeline.depthOfField.focusDistance = 2000;
pipeline.depthOfField.focalLength = 50;
pipeline.depthOfField.fStop = 2.8;
// 泛光效果
pipeline.bloomEnabled = true;
pipeline.bloomThreshold = 0.8;
pipeline.bloomWeight = 0.3;
pipeline.bloomKernel = 64;
pipeline.bloomScale = 0.5;
// 色差效果
pipeline.chromaticAberrationEnabled = true;
pipeline.chromaticAberration.aberrationAmount = 30;
// 胶片效果
pipeline.grainEnabled = true;
pipeline.grain.intensity = 10;
镜头抖动 #
javascript
function shakeCamera(camera, intensity, duration) {
const originalPosition = camera.position.clone();
const startTime = Date.now();
const shake = () => {
const elapsed = Date.now() - startTime;
if (elapsed < duration) {
const factor = 1 - elapsed / duration;
camera.position.x = originalPosition.x + (Math.random() - 0.5) * intensity * factor;
camera.position.y = originalPosition.y + (Math.random() - 0.5) * intensity * factor;
camera.position.z = originalPosition.z + (Math.random() - 0.5) * intensity * factor;
requestAnimationFrame(shake);
} else {
camera.position = originalPosition;
}
};
shake();
}
// 使用
shakeCamera(camera, 0.5, 500);
相机事件 #
javascript
// 相机视图矩阵变化
camera.onViewMatrixChangedObservable.add(() => {
console.log('相机视图变化');
});
// 相机投影矩阵变化
camera.onProjectionMatrixChangedObservable.add(() => {
console.log('投影矩阵变化');
});
// 相机激活
camera.onRestore = () => {
console.log('相机恢复');
};
实战:相机控制器 #
javascript
class CameraController {
constructor(scene, canvas) {
this.scene = scene;
this.canvas = canvas;
this.cameras = {};
this.activeCamera = null;
this.init();
}
init() {
this.createArcCamera();
this.createFreeCamera();
this.setActiveCamera('arc');
}
createArcCamera() {
const camera = new BABYLON.ArcRotateCamera(
'arcCamera',
Math.PI / 2,
Math.PI / 3,
15,
BABYLON.Vector3.Zero(),
this.scene
);
camera.lowerRadiusLimit = 3;
camera.upperRadiusLimit = 50;
camera.wheelPrecision = 50;
camera.panningSensibility = 100;
this.cameras.arc = camera;
}
createFreeCamera() {
const camera = new BABYLON.FreeCamera(
'freeCamera',
new BABYLON.Vector3(0, 5, -15),
this.scene
);
camera.setTarget(BABYLON.Vector3.Zero());
camera.speed = 0.5;
camera.angularSensibility = 2000;
camera.keysUp = [87]; // W
camera.keysDown = [83]; // S
camera.keysLeft = [65]; // A
camera.keysRight = [68]; // D
this.cameras.free = camera;
}
setActiveCamera(name) {
if (this.activeCamera) {
this.activeCamera.detachControl(this.canvas);
}
this.activeCamera = this.cameras[name];
this.scene.activeCamera = this.activeCamera;
this.activeCamera.attachControl(this.canvas, true);
}
focusOnMesh(mesh) {
if (this.activeCamera === this.cameras.arc) {
this.activeCamera.setTarget(mesh.position);
}
}
resetView() {
if (this.activeCamera === this.cameras.arc) {
this.activeCamera.alpha = Math.PI / 2;
this.activeCamera.beta = Math.PI / 3;
this.activeCamera.radius = 15;
this.activeCamera.setTarget(BABYLON.Vector3.Zero());
}
}
}
// 使用
const controller = new CameraController(scene, canvas);
// 切换相机
document.getElementById('arcBtn').onclick = () => controller.setActiveCamera('arc');
document.getElementById('freeBtn').onclick = () => controller.setActiveCamera('free');
下一步 #
现在你已经掌握了相机系统,接下来学习 光照系统,了解如何为场景添加光照效果!
最后更新:2026-03-29