Babylon.js 场景基础 #

核心概念 #

在 Babylon.js 中,场景(Scene)是所有 3D 对象的容器。理解引擎(Engine)和场景(Scene)的关系是学习 Babylon.js 的第一步。

text
┌─────────────────────────────────────────────────────────────┐
│                    Babylon.js 架构层次                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────────────────────────────────────────────┐   │
│   │                    Engine                            │   │
│   │  - 渲染引擎                                          │   │
│   │  - WebGL/WebGPU 上下文                               │   │
│   │  - 渲染循环管理                                      │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│                           ▼                                 │
│   ┌─────────────────────────────────────────────────────┐   │
│   │                    Scene                             │   │
│   │  - 3D 对象容器                                       │   │
│   │  - 相机、光源、网格                                   │   │
│   │  - 材质、纹理                                        │   │
│   └─────────────────────────────────────────────────────┘   │
│                           │                                 │
│         ┌─────────────────┼─────────────────┐               │
│         ▼                 ▼                 ▼               │
│   ┌───────────┐     ┌───────────┐     ┌───────────┐        │
│   │  Camera   │     │   Light   │     │   Mesh    │        │
│   └───────────┘     └───────────┘     └───────────┘        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

引擎(Engine) #

创建引擎 #

引擎是 Babylon.js 的核心,负责 WebGL/WebGPU 上下文管理和渲染循环。

javascript
const canvas = document.getElementById('renderCanvas');

// 基础创建
const engine = new BABYLON.Engine(canvas, true);

// 带配置创建
const engine = new BABYLON.Engine(canvas, true, {
  preserveDrawingBuffer: true,  // 保留绘制缓冲区(截图需要)
  stencil: true,                // 启用模板缓冲
  antialias: true,              // 抗锯齿
  alpha: true,                  // 透明背景
  powerPreference: 'high-performance'
});

引擎配置选项 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Engine 配置选项                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  preserveDrawingBuffer                                       │
│  - 类型: boolean                                            │
│  - 默认: false                                              │
│  - 用途: 截图、后处理需要设为 true                           │
│                                                             │
│  stencil                                                    │
│  - 类型: boolean                                            │
│  - 默认: true                                               │
│  - 用途: 阴影、遮罩效果                                      │
│                                                             │
│  antialias                                                  │
│  - 类型: boolean                                            │
│  - 默认: true                                               │
│  - 用途: 边缘抗锯齿                                         │
│                                                             │
│  alpha                                                      │
│  - 类型: boolean                                            │
│  - 默认: false                                              │
│  - 用途: 透明背景                                           │
│                                                             │
│  powerPreference                                            │
│  - 类型: 'default' | 'high-performance' | 'low-power'       │
│  - 默认: 'default'                                          │
│  - 用途: GPU 性能偏好                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

引擎常用方法 #

javascript
// 获取渲染尺寸
const width = engine.getRenderWidth();
const height = engine.getRenderHeight();

// 获取硬件缩放级别
const scale = engine.getHardwareScalingLevel();

// 设置硬件缩放(性能优化)
engine.setHardwareScalingLevel(0.5);

// 获取帧率
const fps = engine.getFps();

// 强制调整尺寸
engine.resize();

// 释放资源
engine.dispose();

场景(Scene) #

创建场景 #

javascript
// 基础创建
const scene = new BABYLON.Scene(engine);

// 带选项创建
const scene = new BABYLON.Scene(engine, {
  useRightHandedSystem: false,  // 坐标系
  autoClear: true               // 自动清除
});

场景基本属性 #

javascript
// 背景颜色
scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.2, 1);

// 环境颜色
scene.ambientColor = new BABYLON.Color3(0.3, 0.3, 0.3);

// 雾效果
scene.fogMode = BABYLON.Scene.FOGMODE_EXP2;
scene.fogDensity = 0.01;
scene.fogColor = new BABYLON.Color3(0.9, 0.9, 0.85);

// 背面剔除
scene.backFaceCulling = false;

// 深度预处理
scene.useDepthPrePass = true;

坐标系 #

text
┌─────────────────────────────────────────────────────────────┐
│                    Babylon.js 坐标系                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  默认左手坐标系:                                            │
│                                                             │
│           Y (上)                                            │
│           │                                                 │
│           │                                                 │
│           │                                                 │
│           └───────── X (右)                                 │
│          /                                                 │
│         /                                                  │
│        Z (前)                                               │
│                                                             │
│  切换右手坐标系:                                            │
│  scene.useRightHandedSystem = true;                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Vector3 向量 #

javascript
// 创建向量
const v1 = new BABYLON.Vector3(1, 2, 3);
const v2 = BABYLON.Vector3.Zero();
const v3 = BABYLON.Vector3.One();
const v4 = BABYLON.Vector3.Up();
const v5 = BABYLON.Vector3.Forward();

// 向量运算
const sum = v1.add(v2);
const diff = v1.subtract(v2);
const scaled = v1.scale(2);
const distance = BABYLON.Vector3.Distance(v1, v2);
const normalized = v1.normalize();

// 向量属性
const length = v1.length();
const lengthSq = v1.lengthSquared();

渲染循环 #

基本渲染循环 #

javascript
// 启动渲染循环
engine.runRenderLoop(function() {
  scene.render();
});

// 使用箭头函数
engine.runRenderLoop(() => {
  scene.render();
});

多场景渲染 #

javascript
const scene1 = new BABYLON.Scene(engine);
const scene2 = new BABYLON.Scene(engine);

engine.runRenderLoop(() => {
  scene1.render();
  scene2.render();
});

停止渲染循环 #

javascript
// 停止所有渲染循环
engine.stopRenderLoop();

// 使用标志控制
let isRunning = true;

engine.runRenderLoop(() => {
  if (isRunning) {
    scene.render();
  }
});

// 暂停
isRunning = false;

// 恢复
isRunning = true;

场景生命周期 #

初始化顺序 #

text
┌─────────────────────────────────────────────────────────────┐
│                    场景初始化顺序                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 创建 Engine                                             │
│     const engine = new BABYLON.Engine(canvas, true);        │
│                                                             │
│  2. 创建 Scene                                              │
│     const scene = new BABYLON.Scene(engine);                │
│                                                             │
│  3. 创建 Camera                                             │
│     const camera = new BABYLON.ArcRotateCamera(...);        │
│                                                             │
│  4. 创建 Light                                              │
│     const light = new BABYLON.HemisphericLight(...);        │
│                                                             │
│  5. 创建 Mesh                                               │
│     const box = BABYLON.MeshBuilder.CreateBox(...);         │
│                                                             │
│  6. 启动渲染循环                                            │
│     engine.runRenderLoop(() => scene.render());             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

完整示例 #

javascript
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true);

const createScene = function() {
  const scene = new BABYLON.Scene(engine);
  scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.2, 1);
  
  const camera = new BABYLON.ArcRotateCamera(
    'camera',
    Math.PI / 2,
    Math.PI / 2,
    5,
    BABYLON.Vector3.Zero(),
    scene
  );
  camera.attachControl(canvas, true);
  
  const light = new BABYLON.HemisphericLight(
    'light',
    new BABYLON.Vector3(1, 1, 0),
    scene
  );
  
  const box = BABYLON.MeshBuilder.CreateBox('box', { size: 1 }, scene);
  
  return scene;
};

const scene = createScene();

engine.runRenderLoop(function() {
  scene.render();
});

window.addEventListener('resize', function() {
  engine.resize();
});

场景管理 #

获取场景对象 #

javascript
// 获取所有网格
const meshes = scene.meshes;

// 获取所有相机
const cameras = scene.cameras;

// 获取所有光源
const lights = scene.lights;

// 获取所有材质
const materials = scene.materials;

// 按名称获取
const box = scene.getMeshByName('box');
const camera = scene.getCameraByName('camera');
const light = scene.getLightByName('light');

// 按 ID 获取
const mesh = scene.getMeshById('mesh-1');

添加和移除对象 #

javascript
// 网格已自动添加到场景
const box = BABYLON.MeshBuilder.CreateBox('box', {}, scene);

// 手动添加
scene.addMesh(box);

// 移除网格
scene.removeMesh(box);

// 销毁网格
box.dispose();

// 清空场景
scene.clearColor = new BABYLON.Color4(0, 0, 0, 1);

场景事件 #

javascript
// 渲染前事件
scene.onBeforeRenderObservable.add(() => {
  // 每帧渲染前执行
});

// 渲染后事件
scene.onAfterRenderObservable.add(() => {
  // 每帧渲染后执行
});

// 相机渲染前
scene.onBeforeCameraRenderObservable.add((camera) => {
  // 相机渲染前
});

// 相机渲染后
scene.onAfterCameraRenderObservable.add((camera) => {
  // 相机渲染后
});

场景优化 #

自动优化 #

javascript
// 自动优化场景
scene.autoClear = false;           // 禁用自动清除
scene.autoClearDepthAndStencil = false;

// 自动批量处理
scene.autoAnimate = true;

// 禁用不必要的功能
scene.blockfreeActiveMeshesAndRenderingGroups = true;

手动优化 #

javascript
// 冻结材质
material.freeze();

// 冻结网格
box.freezeWorldMatrix();

// 解冻
box.unfreezeWorldMatrix();

// 批量冻结
scene.freezeActiveMeshes();

LOD(细节层次) #

javascript
// 创建 LOD
const box = BABYLON.MeshBuilder.CreateBox('box', { size: 2 }, scene);

// 添加 LOD 层级
const lod1 = BABYLON.MeshBuilder.CreateBox('lod1', { size: 1.8 }, scene);
const lod2 = BABYLON.MeshBuilder.CreateBox('lod2', { size: 1.5 }, scene);

box.addLODLevel(10, lod1);
box.addLODLevel(20, lod2);
box.addLODLevel(30, null);  // 超过距离不渲染

场景拾取 #

射线拾取 #

javascript
// 点击拾取
scene.onPointerDown = function(evt, pickResult) {
  if (pickResult.hit) {
    console.log('点击了:', pickResult.pickedMesh.name);
    console.log('点击位置:', pickResult.pickedPoint);
  }
};

// 手动射线检测
const ray = scene.createPickingRay(
  scene.pointerX,
  scene.pointerY,
  BABYLON.Matrix.Identity(),
  camera
);

const hit = scene.pickWithRay(ray);
if (hit.hit) {
  console.log('射线命中:', hit.pickedMesh.name);
}

拾取信息 #

javascript
scene.onPointerDown = function(evt, pickResult) {
  if (pickResult.hit) {
    // 命中的网格
    const mesh = pickResult.pickedMesh;
    
    // 命中点世界坐标
    const point = pickResult.pickedPoint;
    
    // 命中点法线
    const normal = pickResult.getNormal();
    
    // 命中距离
    const distance = pickResult.distance;
    
    // 命中面的索引
    const faceId = pickResult.faceId;
    
    // UV 坐标
    const uv = pickResult.getTextureCoordinates();
  }
};

场景调试 #

Inspector #

javascript
// 显示调试面板
scene.debugLayer.show();

// 带选项显示
scene.debugLayer.show({
  overlay: true,
  showExplorer: true,
  showInspector: true,
  embedMode: true
});

// 隐藏调试面板
scene.debugLayer.hide();

// 切换显示
if (scene.debugLayer.isVisible()) {
  scene.debugLayer.hide();
} else {
  scene.debugLayer.show();
}

性能监控 #

javascript
// 显示性能统计
scene.debugLayer.show();

// 获取性能数据
const fps = engine.getFps();
const drawCalls = scene.getActiveMeshes().length;

// 性能分析
scene.instrumentation = new BABYLON.SceneInstrumentation(scene);
console.log('绘制调用:', scene.instrumentation.drawCallsCounter.current);

场景序列化 #

导出场景 #

javascript
// 导出为 JSON
const serializedScene = BABYLON.SceneSerializer.Serialize(scene);
const json = JSON.stringify(serializedScene);

// 下载场景文件
function downloadScene(scene, filename) {
  const serialized = BABYLON.SceneSerializer.Serialize(scene);
  const json = JSON.stringify(serialized);
  const blob = new Blob([json], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  
  const a = document.createElement('a');
  a.href = url;
  a.download = filename || 'scene.babylon';
  a.click();
  
  URL.revokeObjectURL(url);
}

导入场景 #

javascript
// 加载 .babylon 文件
BABYLON.SceneLoader.Load(
  '/scenes/',
  'scene.babylon',
  engine,
  function(scene) {
    scene.createDefaultCameraOrLight(true, true, true);
    scene.createDefaultEnvironment();
    
    engine.runRenderLoop(() => {
      scene.render();
    });
  }
);

实战:创建完整场景 #

javascript
const canvas = document.getElementById('renderCanvas');
const engine = new BABYLON.Engine(canvas, true);

const createScene = function() {
  const scene = new BABYLON.Scene(engine);
  scene.clearColor = new BABYLON.Color4(0.1, 0.1, 0.15, 1);
  
  // 创建相机
  const camera = new BABYLON.ArcRotateCamera(
    'camera',
    Math.PI / 4,
    Math.PI / 3,
    10,
    BABYLON.Vector3.Zero(),
    scene
  );
  camera.attachControl(canvas, true);
  camera.lowerRadiusLimit = 3;
  camera.upperRadiusLimit = 20;
  
  // 创建光源
  const light = new BABYLON.HemisphericLight(
    'hemiLight',
    new BABYLON.Vector3(0, 1, 0),
    scene
  );
  light.intensity = 0.7;
  
  const pointLight = new BABYLON.PointLight(
    'pointLight',
    new BABYLON.Vector3(2, 3, 2),
    scene
  );
  pointLight.intensity = 0.5;
  
  // 创建地面
  const ground = BABYLON.MeshBuilder.CreateGround(
    'ground',
    { width: 10, height: 10 },
    scene
  );
  
  const groundMat = new BABYLON.StandardMaterial('groundMat', scene);
  groundMat.diffuseColor = new BABYLON.Color3(0.3, 0.3, 0.35);
  ground.material = groundMat;
  
  // 创建多个物体
  const box = BABYLON.MeshBuilder.CreateBox('box', { size: 1 }, scene);
  box.position = new BABYLON.Vector3(-2, 0.5, 0);
  
  const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', { diameter: 1 }, scene);
  sphere.position = new BABYLON.Vector3(0, 0.5, 0);
  
  const cylinder = BABYLON.MeshBuilder.CreateCylinder('cylinder', { height: 1, diameter: 0.8 }, scene);
  cylinder.position = new BABYLON.Vector3(2, 0.5, 0);
  
  // 材质
  const redMat = new BABYLON.StandardMaterial('redMat', scene);
  redMat.diffuseColor = new BABYLON.Color3(1, 0.2, 0.2);
  box.material = redMat;
  
  const greenMat = new BABYLON.StandardMaterial('greenMat', scene);
  greenMat.diffuseColor = new BABYLON.Color3(0.2, 1, 0.2);
  sphere.material = greenMat;
  
  const blueMat = new BABYLON.StandardMaterial('blueMat', scene);
  blueMat.diffuseColor = new BABYLON.Color3(0.2, 0.2, 1);
  cylinder.material = blueMat;
  
  // 点击交互
  scene.onPointerDown = function(evt, pickResult) {
    if (pickResult.hit && pickResult.pickedMesh) {
      const mesh = pickResult.pickedMesh;
      mesh.scaling = mesh.scaling.scale(1.1);
    }
  };
  
  // 动画
  scene.onBeforeRenderObservable.add(() => {
    box.rotation.y += 0.01;
    sphere.position.y = 0.5 + Math.sin(Date.now() * 0.002) * 0.2;
    cylinder.rotation.x += 0.01;
  });
  
  return scene;
};

const scene = createScene();

engine.runRenderLoop(() => {
  scene.render();
});

window.addEventListener('resize', () => {
  engine.resize();
});

下一步 #

现在你已经掌握了场景的基础知识,接下来学习 相机系统,了解如何控制视角和交互!

最后更新:2026-03-29