Three.js案例
play

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 与交互音进一步沉浸

本地运行

  1. 克隆仓库

    git clone https://github.com/luosijie/playground.git
    cd playground
  2. 安装依赖

    yarn install   # 或 pnpm / npm
  3. 启动开发服务器

    yarn dev       # 默认端口 5173

浏览器访问 http://localhost:5173 即可。

核心特性

  1. 模块化设计 - 每个游乐设施独立开发

  2. 物理渲染同步 - 统一的更新循环

  3. 跨平台交互 - 自适应桌面/移动端控制

  4. 性能优化 - Matcap 材质 + SAP 宽相检测

  5. 开发友好 - 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() 创建刚体

  • 观察者模式: 碰撞事件 → 音效触发

  • 策略模式: 不同材质和加载策略