什么是 Babylon.js?

Babylon.js 是由微软开发并维护的一款高性能、开源的 3D 引擎,专为 Web 打造。它基于 WebGL 技术,允许开发者在浏览器中创建丰富的 3D 游戏、可视化效果和交互式体验,而无需用户安装任何插件。

其主要优势包括:

  • 强大的渲染能力,支持复杂材质和光影效果

  • 丰富的几何体和粒子系统

  • 完善的物理引擎集成

  • 良好的跨浏览器兼容性

  • 活跃的社区和详尽的文档

  • 与主流前端框架(React、Vue 等)良好兼容

实战

  • 在 React 项目中使用 Babylon.js 创建一个包含基础几何体、动画和交互的 3D 场景。

环境准备

安装必要的依赖:

npm install @babylonjs/core @babylonjs/react-native

核心代码解析

  • 下面是一个完整的 React 组件,它创建了一个包含多种几何体、动画和交互的 3D 场景:

1. 基础设置与组件状态

import React, { useEffect, useRef, useState } from 'react';
import {
  Engine,
  Scene,
  Vector3,
  HemisphericLight,
  MeshBuilder,
  StandardMaterial,
  Color3,
  ArcRotateCamera,
  PointLight,
  SpotLight,
  Animation
} from '@babylonjs/core';

function App() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  
  //Todo
}
  • 使用useRef获取 canvas 元素的引用,这是 Babylon.js 渲染 3D 场景的基础。

2. 初始化 Babylon 核心组件

  • Babylon.js 的核心是Engine(引擎)和Scene(场景),让我们看看如何初始化它们:

useEffect(() => {
  if ( !canvasRef.current) return;

  // 创建引擎和场景
  const engine = new Engine(canvasRef.current, true);
  const scene = new Scene(engine);
  
  // 后续代码将在这里添加
}, []);
  • Engine是连接 canvas 和场景的桥梁,负责处理渲染循环和低级 WebGL 操作。Scene则是所有 3D 元素的容器。

3. 添加相机

  • 没有相机,就无法 "看到"3D 场景。这里我们使用ArcRotateCamera,围绕目标旋转观察:

// 创建相机
const camera = new ArcRotateCamera(
  "camera",          // 名称
  0,                 // 初始方位角(弧度)
  Math.PI / 3,       // 初始仰角(弧度)
  10,                // 与目标的距离
  Vector3.Zero(),    // 目标点坐标
  scene              // 所属场景
);
camera.attachControl(canvasRef.current, true);
camera.lowerRadiusLimit = 5;  // 最小缩放限制
camera.upperRadiusLimit = 20; // 最大缩放限制
  • attachControl方法让相机响应鼠标 / 触摸输入,实现旋转和缩放功能。

4. 添加光源

  • 3D 场景中的光照至关重要,它决定了物体的视觉表现。我们添加了三种不同类型的光源:

// 环境光 - 均匀照亮所有物体
const hemisphericLight = new HemisphericLight(
  "hemisphericLight",
  new Vector3(0, 1, 0),  // 光源方向
  scene
);
hemisphericLight.intensity = 0.3;

// 点光源 - 从一点向各个方向发射光线
const pointLight = new PointLight(
  "pointLight",
  new Vector3(0, 5, 0),  // 光源位置
  scene
);
pointLight.intensity = 0.8;
pointLight.diffuse = new Color3(1, 0.8, 0.6);  // 暖黄色

// 聚光灯 - 类似手电筒,有方向和锥形范围
const spotLight = new SpotLight(
  "spotLight",
  new Vector3(0, 10, 0),   // 光源位置
  new Vector3(0, -1, 0),   // 光照方向
  Math.PI / 3,             // 锥角
  2,                       // 衰减指数
  scene
);
spotLight.intensity = 0.5;
spotLight.diffuse = new Color3(0.6, 0.8, 1);  // 冷蓝色
  • 不同类型的光源可以创造出丰富的光影效果,组合使用能让场景更有层次感。

5. 创建几何体与材质

  • 创建基础几何体,应用材质:

// 创建地面
const ground = MeshBuilder.CreateGround("ground", {
  width: 20,
  height: 20
}, scene);
const groundMaterial = new StandardMaterial("groundMaterial", scene);
groundMaterial.diffuseColor = new Color3(0.2, 0.2, 0.2); // 深灰色
groundMaterial.specularColor = new Color3(0.1, 0.1, 0.1); // 弱高光
ground.material = groundMaterial;

// 创建立方体
const box = MeshBuilder.CreateBox("box", { size: 2 }, scene);
box.position = new Vector3(-3, 1, 0); // 设置位置
const boxMaterial = new StandardMaterial("boxMaterial", scene);
boxMaterial.diffuseColor = new Color3(0.8, 0.3, 0.3); // 红色
box.material = boxMaterial;

// 创建球体、圆柱体和圆环(代码类似,略)
  • MeshBuilder提供了多种创建基础几何体的方法,如CreateBoxCreateSphere等。StandardMaterial则用于定义物体的外观属性。

6. 添加动画

  • 为物体添加一些动画:

// 立方体旋转动画
const boxAnimation = new Animation(
  "boxAnimation",
  "rotation.y",  // 要动画的属性
  30,            // 帧率
  Animation.ANIMATIONTYPE_FLOAT,
  Animation.ANIMATIONLOOPMODE_CYCLE  // 循环模式
);

// 定义关键帧
const keyFrames = [];
keyFrames.push({ frame: 0, value: 0 });
keyFrames.push({ frame: 120, value: 2 * Math.PI }); // 2π即360度

boxAnimation.setKeys(keyFrames);
box.animations = [boxAnimation];

// 播放动画
scene.beginAnimation(box, 0, 120, true);

// 球体弹跳动画(类似,略)
  • 通过定义关键帧,我们可以控制物体在不同时间点的状态,Babylon.js 会自动计算中间状态,实现平滑过渡。

7. 交互功能

  • 让我们添加点击交互,让用户可以与 3D 物体互动:

// 添加鼠标点击事件
scene.onPointerDown = function (evt) {
  const pickResult = scene.pick(scene.pointerX, scene.pointerY);
  if (pickResult.hit) {
    const hitMesh = pickResult.pickedMesh;
    if (hitMesh) {
      // 随机改变颜色
      const material = hitMesh.material as StandardMaterial;
      if (material) {
        material.diffuseColor = new Color3(
          Math.random(),
          Math.random(),
          Math.random()
        );
      }
    }
  }
};
  • scene.pick方法通过射线检测,判断鼠标点击位置是否有物体。

8. 渲染循环与清理

  • 启动渲染循环,并在组件卸载时进行清理:

// 渲染循环
engine.runRenderLoop(() => {
  scene.render();
});

// 处理窗口大小变化
window.addEventListener('resize', () => {
  engine.resize();
});

// 清理函数
return () => {
  engine.dispose();
  window.removeEventListener('resize', () => {});
};
  • engine.runRenderLoop会不断渲染场景,确保动画和交互的流畅性。组件卸载时,engine.dispose会释放资源,避免内存泄漏。

9. 组件渲染

  • 最后,我们需要在 JSX 中渲染 canvas 元素:

return (
  <div className="App" style={{ width: '100vw', height: '100vh', overflow: 'hidden' }}>
    {showAdvanced ? (
      <AdvancedBabylonExample onSwitchExample={() => setShowAdvanced(false)} />
    ) : (
      <div style={{ position: 'relative', width: '100%', height: '100%' }}>
        <canvas
          ref={canvasRef}
          style={{
            width: '100%',
            height: '100%',
            outline: 'none',
            background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
          }}
        />
        
        {/* 悬浮切换按钮 */}
        <div className="floating-switch-btn" onClick={() => setShowAdvanced(true)}>
          <span>高级示例</span>
        </div>
      </div>
    )}
  </div>
);

运行效果

  • 一个 3D 场景,包含地面和四种不同的几何体

  • 一个可以用鼠标控制的相机(拖动旋转,滚轮缩放)

  • 自动旋转的立方体和上下弹跳的球体

  • 点击任何物体时,它会随机改变颜色

  • 右上角有一个按钮,可以切换到更高级的示例