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