类比Three.js 学习Babylon.js
Three.js:2010 年诞生,社区庞大、生态成熟,以轻量灵活著称,适合追求自定义和极致控制的开发者。
Babylon.js:2013 年由微软推出,集成度高、工具链完善,自带很多 "开箱即用" 的功能,更像一个 "batteries-included" 的完整解决方案。
两者核心目标一致 —— 在浏览器中渲染 3D 内容,但实现方式各有侧重。
核心概念对比
实战对比
1. 环境准备:依赖安装
Three.js 通常直接引入核心库:
npm install three @react-three/fiber
Babylon.js 的安装方式类似,但核心模块划分更细致:
npm install @babylonjs/core # 核心引擎
# 如需React集成工具,可额外安装:
# npm install @babylonjs/react-native2. 基础场景搭建:从渲染器到场景
Three.js 的典型初始化:
import * as THREE from 'three';
// 创建渲染器
const renderer = new THREE.WebGLRenderer({ canvas });
renderer.setSize(width, height);
// 创建场景
const scene = new THREE.Scene();Babylon.js 的对应实现:
import { Engine, Scene } from '@babylonjs/core';
// 创建引擎(相当于Three.js的Renderer)
const engine = new Engine(canvasRef.current, true); // 第二个参数开启抗锯齿
// 创建场景(与Three.js逻辑一致)
const scene = new Scene(engine);关键差异:Babylon.js 用
Engine封装了渲染逻辑,不仅包含渲染功能,还负责管理渲染循环、窗口大小适配等底层操作,比 Three.js 的Renderer职责更全面。
3. 相机:控制 "观察视角"
Three.js 中常用轨道控制器:
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 创建相机
const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
camera.position.z = 5;
// 添加轨道控制器(需额外引入)
const controls = new OrbitControls(camera, renderer.domElement);Babylon.js 的对应实现:
import { ArcRotateCamera } from '@babylonjs/core';
// 创建ArcRotateCamera(自带轨道控制功能)
const camera = new ArcRotateCamera(
"camera", // 名称
0, // 初始方位角(弧度)
Math.PI / 3, // 初始仰角(弧度)
10, // 与目标点的距离
Vector3.Zero(), // 目标点坐标
scene // 所属场景
);
// 绑定控制器(内置功能,无需额外引入)
camera.attachControl(canvasRef.current, true);关键差异:Babylon.js 的
ArcRotateCamera内置了类似 Three.jsOrbitControls的交互功能,无需额外引入控制器库,开箱即用。
4. 光照:让物体 "可见"
Three.js 和 Babylon.js 的光照类型高度对应,但参数设计略有不同:
Three.js 中的光照示例:
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.3);
scene.add(ambientLight);
// 点光源
const pointLight = new THREE.PointLight(0xffcc99, 0.8);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);Babylon.js 的对应实现:
import { HemisphericLight, PointLight, Color3 } from '@babylonjs/core';
// 环境光(HemisphericLight比Three的AmbientLight更真实,模拟天地光)
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); // 用RGB值定义颜色(1对应255)关键差异:
Babylon.js 的
HemisphericLight比 Three.js 的AmbientLight更复杂,能模拟天空和地面的不同光照颜色,效果更自然;颜色定义方式不同:Three.js 常用十六进制(如
0xffcc99),Babylon.js 常用Color3对象(RGB 值范围 0-1)。
5. 几何体与材质:构建可见物体
Three.js 的几何体创建:
// 创建立方体
const geometry = new THREE.BoxGeometry(2, 2, 2);
const material = new THREE.MeshStandardMaterial({ color: 0xff5555 });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(-3, 1, 0);
scene.add(cube);Babylon.js 的对应实现:
import { MeshBuilder, StandardMaterial, Color3 } from '@babylonjs/core';
// 创建立方体(通过MeshBuilder工具类)
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); // 红色(RGB 0.8,0.3,0.3)
box.material = boxMaterial;关键差异:
Babylon.js 通过
MeshBuilder工具类创建几何体(如CreateBox、CreateSphere),比 Three.js 的BoxGeometry等构造函数更直观;位置、旋转等属性设置更简洁,直接赋值
Vector3对象即可,无需调用set方法。
6. 动画:让物体 "动起来"
Three.js 的动画实现(需手动更新):
function animate() {
requestAnimationFrame(animate);
// 手动更新旋转
cube.rotation.y += 0.01;
renderer.render(scene, camera);
}
animate();
Babylon.js 的动画系统(声明式关键帧):
import { Animation } from '@babylonjs/core';
// 创建动画(声明式定义关键帧)
const boxAnimation = new Animation(
"boxAnimation",
"rotation.y", // 要动画的属性
30, // 帧率
Animation.ANIMATIONTYPE_FLOAT,
Animation.ANIMATIONLOOPMODE_CYCLE // 循环模式
);
// 定义关键帧
const keyFrames = [
{ frame: 0, value: 0 },
{ frame: 120, value: 2 * Math.PI } // 120帧完成360度旋转
];
boxAnimation.setKeys(keyFrames);
box.animations = [boxAnimation];
// 播放动画(自动循环,无需手动更新)
scene.beginAnimation(box, 0, 120, true);关键差异:
Babylon.js 内置了完整的动画系统,支持关键帧定义,无需像 Three.js 那样在渲染循环中手动更新属性;
动画循环、速度控制等功能开箱即用,适合复杂动画序列。
7. 交互:响应鼠标点击
Three.js 的射线检测:
import { Raycaster } from 'three';
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 计算鼠标位置
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// 射线检测
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
// 改变颜色
intersects[0].object.material.color.set(Math.random() * 0xffffff);
}
}
window.addEventListener('click', onMouseClick);Babylon.js 的交互实现:
// 场景内置射线检测,无需手动创建射线
scene.onPointerDown = function (evt) {
const pickResult = scene.pick(scene.pointerX, scene.pointerY);
if (pickResult.hit) {
const hitMesh = pickResult.pickedMesh;
// 随机改变颜色
const material = hitMesh.material;
if (material) {
material.diffuseColor = new Color3(
Math.random(),
Math.random(),
Math.random()
);
}
}
};关键差异:Babylon.js 的
scene.pick方法封装了射线检测逻辑,无需像 Three.js 那样手动创建Raycaster和计算鼠标坐标,交互实现更简洁。
8. 渲染循环与资源清理
Three.js 的渲染循环:
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
// 清理(需手动处理)
window.removeEventListener('resize', onWindowResize);Babylon.js 的渲染循环:
// 启动渲染循环(引擎自动管理)
engine.runRenderLoop(() => {
scene.render();
});
// 窗口大小适配(内置方法)
window.addEventListener('resize', () => {
engine.resize();
});
// 清理函数(组件卸载时)
return () => {
engine.dispose(); // 一键释放所有资源
};关键差异:Babylon.js 的
Engine类自动管理渲染循环,且dispose方法会统一释放所有资源(包括场景、相机、材质等),比 Three.js 的手动清理更省心。
9. 物理引擎
一、物理引擎的集成方式
物理世界初始化
Three.js:
需先实例化第三方物理引擎的世界(如CANNON.World),手动设置重力、时间步长,且需在渲染循环中单独更新物理世界状态。
示例(Cannon.js):import * as CANNON from 'cannon-es'; // 初始化物理世界 const world = new CANNON.World(); world.gravity.set(0, -9.82, 0); // 设置重力 world.broadphase = new CANNON.NaiveBroadphase(); // 渲染循环中更新物理 function animate() { requestAnimationFrame(animate); world.step(1/60); // 手动更新物理模拟 // 同步3D模型与物理体位置(需手动写逻辑) mesh.position.copy(physicsBody.position); renderer.render(scene, camera); }Babylon.js:
直接通过PhysicsImpostor关联 3D 对象与物理属性,物理世界由引擎自动管理,无需手动初始化 “物理世界” 实例。
示例:// 启用物理引擎(仅需一次) scene.enablePhysics(new BABYLON.Vector3(0, -9.81, 0), new BABYLON.AmmoJSPlugin()); // 为物体添加物理属性 box.physicsImpostor = new BABYLON.PhysicsImpostor( box, BABYLON.PhysicsImpostor.BoxImpostor, // 碰撞体类型 { mass: 1, friction: 0.5 }, // 物理参数 scene );
2. 碰撞体类型与灵活性
Three.js:
依赖第三方库支持的碰撞体类型(如 Cannon.js 支持盒子、球体、胶囊体等基础形状,复杂形状需手动组合)。若需高精度碰撞(如使用模型顶点数据),需额外处理碰撞体生成逻辑。Babylon.js:
内置多种碰撞体类型(盒子、球体、圆柱体、胶囊体、 mesh 形状等),支持直接基于 3D 模型的顶点数据生成碰撞体(MeshImpostor),无需手动处理复杂形状的碰撞逻辑。
示例(使用模型本身作为碰撞体):
// 基于模型网格生成碰撞体
complexModel.physicsImpostor = new BABYLON.PhysicsImpostor(
complexModel,
BABYLON.PhysicsImpostor.MeshImpostor,
{ mass: 2, restitution: 0.2 },
scene
);3. 物理交互与约束
Three.js:
约束(如关节、弹簧、马达)需通过第三方库的 API 手动创建,且需手动关联到对应的物理体,代码冗余度高。例如用 Cannon.js 创建铰链约束:const hinge = new CANNON.HingeConstraint( bodyA, bodyB, { pivotA: new CANNON.Vec3(0, 1, 0) }, // 物体A的约束点 { pivotB: new CANNON.Vec3(0, -1, 0) } // 物体B的约束点 ); world.addConstraint(hinge);Babylon.js:
提供统一的约束 API(如HingeJoint、DistanceJoint、SliderJoint等),直接通过物理体实例创建,无需手动关联到物理世界,且参数配置更直观。
示例(创建铰链约束):
const hinge = new BABYLON.HingeJoint(
{
mainPivot: new BABYLON.Vector3(0, 1, 0), // 主物体约束点
connectedPivot: new BABYLON.Vector3(0, -1, 0), // 关联物体约束点
axis: new BABYLON.Vector3(1, 0, 0) // 旋转轴
},
scene
);
// 关联两个物体
hinge.attach(box.physicsImpostor, sphere.physicsImpostor);