playground
是前端开发者 Jesse Luo(GitHub ID: luosijie)的个人作品:一个 3D 游乐园互动场景。作者先在 Blender 中完成整体搭建与贴图烘焙,再将模型导出为 GLB,在 Three.js + TypeScript 环境中加载并赋予交互、物理与音效。
项目地址:https://github.com/luosijie/playground (opens in a new tab)
为什么值得一看
- 三维美术 + 前端一次打包:从建模、贴图、光照到浏览器端渲染的完整流水线,可作为“小团队 1 人完成 3D 项目”的参考。
- 性能友好:大量静态物体使用 Matcap 材质 与 阴影烘焙贴图,在无实时光源的情况下依旧具备质感。
- 多技术拼装:Three.js 之外,引入
cannon-es
处理刚体、howler.js
管理音效,GLSL 片段着色器做局部特效,体现了 WebGL 项目“前端工程化”思路。
核心技术要点
环节 | 关键做法 | 价值 |
---|---|---|
模型拆分 | 场景按 静态/重复/物理对象 分类导出;火车轨道、车辆单独导出 | 方便按需加载与实例化 |
Matcap 材质 | 在 Blender 烘焙光照 & 材质到单张贴图;Three.js 用 MeshMatcapMaterial | 省去实时光照,帧率更稳 |
阴影贴图 | 地面阴影独立烘焙并贴图 | 增强立体感,无需 shadowMap |
路径动画 | 列车沿自定义曲线行驶,摄像机根据玩家交互切换 | 丰富场景动感 |
物理碰撞 | cannon-es 负责车体/积木等刚体;顶点动画依旧交给 Three.js | 视觉 + 真实受力结合 |
音效 | howler.js 做 BGM 与交互音 | 进一步沉浸 |
本地运行
-
克隆仓库
git clone https://github.com/luosijie/playground.git cd playground
-
安装依赖
yarn install # 或 pnpm / npm
-
启动开发服务器
yarn dev # 默认端口 5173
浏览器访问 http://localhost:5173
即可。
核心特性
-
模块化设计 - 每个游乐设施独立开发
-
物理渲染同步 - 统一的更新循环
-
跨平台交互 - 自适应桌面/移动端控制
-
性能优化 - Matcap 材质 + SAP 宽相检测
-
开发友好 - TypeScript + 热重载 + 调试工具
分层设计
入口层 (main.ts)
↓
世界层 (World.ts) - 核心容器
↓
┌─────────────┬─────────────┬─────────────┐
│ 物理引擎层 │ 渲染系统 │ 元素系统 │
│ (Physics.ts) │ (Materials) │ (Elements/) │
└─────────────┴─────────────┴─────────────┘
↓
资源管理 (Loader.ts) + 交互控制 (JoyStick.ts)
核心代码设计
1. World 类 - 组合模式核心
class World {
// 渲染系统
scene: Scene;
renderer: WebGLRenderer;
camera: Camera;
// 物理系统
physics: Physics;
// 游乐设施元素
car: Car;
windmill: Windmill;
carousel: Carousel;
ferris: Ferris;
// ...
// 统一渲染循环
render() {
this.physics.world.fixedStep();
// 更新所有元素
// 渲染场景
}
}
2. Physics 类 - 物理引擎封装
class Physics {
world: World;
materials: Materials; // 地面、轮胎、默认材质
collideSounds: CollideSounds; // 碰撞音效映射
// 创建刚体的工厂方法
createBody(config: BodyConfig): Body;
}
3. 元素基类设计
// 统一的元素接口
interface Element {
build(resources: Resources): void;
update(): void;
}
// 示例:Car 类
class Car implements Element {
physics: Physics;
vehicle: RaycastVehicle; // Cannon.js 载具
sounds: CarSounds;
shadow: CustomShadow;
build(model: Group) {
// 创建物理刚体
// 绑定 3D 模型
// 初始化音效
}
update() {
// 处理用户输入
// 更新物理状态
// 同步渲染位置
}
}
关键技术实现
1. 自定义着色器系统
// Matcap 顶点着色器
vec3 n = mat3(modelViewMatrix) * normal;
vec2 point = vec2(dot(x, n), dot(y, n)) * 0.5 + 0.5;
vPoint = point;
// 片段着色器
gl_FragColor = texture(uTexture, vPoint);
2. 物理音效联动
// 碰撞事件监听
physics.world.addEventListener("beginContact", (event) => {
const bodyA = event.bodyA;
const bodyB = event.bodyB;
// 根据碰撞体类型播放对应音效
if (bodyA.collideSound) {
this.collideSounds[bodyA.collideSound].play();
}
});
3. 资源加载器
class Loader {
// 支持多种格式:GLTF, PLY, Texture, Video
load(resources: Resource[]) {
// 并行加载
// 进度回调
// 错误处理
}
}
设计模式应用
-
组合模式: World 统一管理所有子系统
-
工厂模式: Physics.createBody() 创建刚体
-
观察者模式: 碰撞事件 → 音效触发
-
策略模式: 不同材质和加载策略