Web 3D 的觉醒
第二十一本做完 2D 手游后,老板盯上了「免下载」的玩法——发个链接就能玩的 3D 网页游戏!小哲问:浏览器里做 3D?那不是要装插件吗?周师傅说:WebGL 让浏览器原生支持 3D,Three.js 让 3D 开发像搭积木。这一本,从 WebGL 原理到 Three.js 实战,把 Web 3D 技术栈完整走一遍。
点开即玩的 3D 世界
Web 3D 全景:WebGL → Three.js → 游戏引擎 → 上线师傅!老板看了别人的 3D 网页游戏——免下载、点开就玩,转发还方便!「我们也做一个!」可浏览器里做 3D,不是要装 Flash 插件吗?
Flash 是上古时代了——现在浏览器原生支持 WebGL(把 GPU 能力开放给网页),不用装任何东西。Web 3D 技术栈分四层:
- 底层:WebGL(浏览器 3D API——像浏览器的「OpenGL」)。
- 引擎:Three.js(最流行的 Web 3D 库——搭积木式开发)。
- 框架:Babylon.js / PlayCanvas / React Three Fiber(游戏级/工程级)。
- 平台:微信小游戏 / H5 页面——分发与上线。
来,十站路线——
记住一句话:Web 3D = WebGL(GPU 接口)+ Three.js(引擎)+ 模型资产 + 性能优化——第二十一本的渲染知识在这里换个平台重演。走,第一站,看浏览器怎么「长」出 GPU。
WebGL:浏览器的 3D 能力
WebGL · Canvas · GLSL · 渲染管线 · 着色器WebGL 是浏览器给网页开的「GPU 后门」——网页代码能直接指挥显卡画 3D。虽然日常开发不直接写它,但懂它才能懂 Three.js 在干什么。
WebGL 三件事:
- Canvas + WebGL 上下文:网页的「画布」上拿 GPU 绘制上下文——`canvas.getContext('webgl')`。
- GLSL 着色器:GPU 上跑的着色语言——顶点着色器(位置变换)+ 片元着色器(像素颜色)——第二十一本渲染管线的浏览器版。
- 渲染管线:顶点 → 光栅化 → 片元 ——和桌面 3D 完全同一条流水线(第 2 站渲染管线老朋友)。
关键认知:WebGL 是「底层 API」,写起来非常原始(几百行才能画个三角形)——所以没人直接写,都用 Three.js 封装。WebGL2 是升级版(支持更多特性)。
【画一个三角形需要……】
① 创建着色器程序(GLSL 代码 30 行)
② 准备顶点数据(缓冲)
③ 写渲染循环(requestAnimationFrame)
④ 处理矩阵(数学 50 行)
→ 300 行代码画个三角形 —— 所以用 Three.js!
【GLSL 着色器(了解语法)】
// 顶点着色器:决定顶点画在哪
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
// 片元着色器:决定像素什么颜色
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
【WebGL vs WebGPU(未来)】
WebGL:当前标准(Three.js 主力支持)
WebGPU:新一代 API(更现代、性能更强)——未来方向
→ 学 Three.js 不用关心底层,引擎两个都兼容
【浏览器兼容】
WebGL 支持率 98%+(所有现代浏览器)
→ 「免安装点开即玩」的硬件基础就是它
【技术栈全景(四层)】
底层:WebGL / WebGPU
引擎:Three.js(最流行)
框架:Babylon.js / PlayCanvas / React Three Fiber
平台:H5 / 微信小游戏 / 小程序
WebGL 术语
- WebGL:浏览器 GPU 接口——网页 3D 的地基。
- Canvas:网页画布——GPU 绘制的载体。
- GLSL:GPU 着色语言——顶点/片元着色器。
- 渲染管线:顶点→光栅→片元——第二十一本同款。
- WebGL2 / WebGPU:升级版 / 下一代——未来方向。
- 兼容性:98%+ 浏览器支持——免安装基础。
- 别直接写:太原始——用 Three.js 封装。
本站收获:WebGL = 浏览器给网页的 GPU 后门(Canvas + GLSL + 渲染管线)。懂原理但别直接写——它太原始,这正是 Three.js 存在的意义。WebGPU 是未来方向。
Three.js:引擎入门
Scene/Camera/Renderer · Geometry/Material/Mesh · LightThree.js 是 Web 3D 的事实标准(80% 以上项目用它)。核心就六个对象:场景、相机、渲染器、几何体、材质、网格——记住它们,你就入门了。
Three.js 六大核心对象:
- Scene(场景):装所有物体的「世界容器」。
- Camera(相机):观察视角(PerspectiveCamera 透视——近大远小,游戏标配)。
- Renderer(渲染器):WebGLRenderer——把场景画到 Canvas。
- Geometry(几何体):形状——BoxGeometry(立方体)、SphereGeometry(球)。
- Material(材质):表面属性——颜色、金属度、粗糙度。
- Mesh(网格):几何体 + 材质 = 一个「可见物体」。
再加 Light(灯光)——没光的世界是黑的(第二十一本光照老朋友)。
import * as THREE from "three";
// ① 场景
const scene = new THREE.Scene();
// ② 相机(透视:75° 视角,近远裁剪)
const camera = new THREE.PerspectiveCamera(75, innerWidth/innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5); // 放在(0,2,5)
// ③ 渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);
// ④ 物体:几何体 + 材质 = 网格
const geometry = new THREE.BoxGeometry(1, 1, 1); // 立方体
const material = new THREE.MeshStandardMaterial({ color: 0x2dd4bf });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube); // 放进场景
// ⑤ 灯光(没光 = 全黑)
scene.add(new THREE.AmbientLight(0xffffff, 0.5)); // 环境光
const dir = new THREE.DirectionalLight(0xffffff, 1); // 方向光
dir.position.set(3, 5, 3);
scene.add(dir);
// ⑥ 动画循环(第二十一本游戏循环!)
function animate() {
requestAnimationFrame(animate);
cube.rotation.y += 0.01; // 每帧转一点
renderer.render(scene, camera);
}
animate();
【六大对象一句话】
Scene 装世界 / Camera 看世界 / Renderer 画世界
Geometry 形状 / Material 表面 / Mesh 可见物
→ 万事万物都是「几何+材质」组合进场景
【Hello 之后学什么】
官网示例库(threejs.org/examples)——最好的教材
常用:导入模型、动画、粒子、后处理特效
Three.js 术语
- Scene / Camera / Renderer:世界 / 视角 / 画笔——三件套。
- Geometry / Material / Mesh:形状 / 表面 / 可见物体。
- Light:灯光——没光全黑。
- PerspectiveCamera:透视相机——近大远小。
- requestAnimationFrame:浏览器动画循环——游戏循环的 Web 版。
- MeshStandardMaterial:PBR 标准材质——物理真实。
- scene.add:物体入场景——世界构建器。
本站收获:Three.js = Scene(世界)+ Camera(视角)+ Renderer(画笔)+ Mesh(几何+材质)+ Light(光)。30 行代码一个会转的 3D 立方体——引擎把 WebGL 的 300 行封装成了 6 个概念。
场景搭建:材质与光照
场景图 · PBR 材质 · 灯光类型 · 贴图纹理场景不止一个立方体——要地面、要房子、要氛围。场景图组织层次、PBR 材质让表面真实、灯光决定氛围。这一站,把「世界」搭出来。
场景搭建三件事:
- 场景图(Scene Graph):物体的「父子树」——爸爸移动,孩子跟着动(角色=身体节点,武器=子节点;和游戏引擎的场景树同款)。
- PBR 材质:物理真实渲染——金属度/粗糙度/贴图,让物体「像真的」(MeshStandardMaterial)。
- 灯光类型:Ambient(环境光,全局补光)、Directional(方向光,太阳)、Point(点光,灯泡)、Spot(聚光灯,舞台)——组合打光出氛围。
贴图(Texture):颜色图/法线图/粗糙度图——细节不用建模,贴图来补(第二十一本贴图老朋友)。
// 场景图:父子关系(爸爸动,孩子跟着动)
const player = new THREE.Group(); // 空组(身体)
const head = new THREE.Mesh(sphereGeo, skinMat);
head.position.y = 1.7; // 相对身体的位置
player.add(head); // 头是身体的子节点
scene.add(player); // 身体进场景
player.position.x += 1; // 移动身体 → 头跟着走!
// PBR 材质:物理真实
const metal = new THREE.MeshStandardMaterial({
color: 0xc0c0c0,
metalness: 0.9, // 金属度:高 = 金属
roughness: 0.1, // 粗糙度:低 = 光滑反光
});
// 贴图:细节靠贴图补(不用建模)
const woodTex = new THREE.TextureLoader().load("wood.jpg");
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(20, 20),
new THREE.MeshStandardMaterial({ map: woodTex }) // 贴图材质
);
floor.rotation.x = -Math.PI / 2; // 地面躺平
scene.add(floor);
// 灯光组合(氛围)
scene.add(new THREE.AmbientLight(0xffffff, 0.3)); // 全局补光
const sun = new THREE.DirectionalLight(0xffeedd, 1); // 太阳
sun.position.set(10, 20, 5);
scene.add(sun);
const lamp = new THREE.PointLight(0xff8800, 1, 10); // 暖灯泡
lamp.position.set(2, 3, 2);
scene.add(lamp);
【打光三原则】
全局环境光:保证不黑
主光(方向光):立体感来源(侧上 45°)
点缀光(点/聚光):氛围与焦点
→ 灯光数量少而精(每盏灯都是渲染成本)
场景术语
- 场景图(Scene Graph):父子树——爸爸动孩子跟。
- Group:空容器——组织物体层级。
- PBR 材质:物理真实——金属度/粗糙度。
- 灯光类型:环境/方向/点/聚光——组合出氛围。
- 贴图(Texture):颜色/法线/粗糙度——细节靠贴图。
- 打光三原则:环境 + 主光 + 点缀——少而精。
- 每灯成本:灯越多渲染越贵——灯光要节制。
本站收获:场景搭建 = 场景图(层级)+ PBR(真实)+ 灯光(氛围)+ 贴图(细节)。材质和光照决定「质感」——同样的模型,打光不同,感觉完全不同。
相机与交互:玩家视角
透视/正交 · 轨道控制器 · Raycaster · 点击拾取玩家怎么看 3D 世界?相机怎么控制?点击怎么「捡起」物体?——相机 + 射线拾取是 Web 3D 交互的两大核心。
交互两件事:
- 相机控制:透视相机(近大远小——游戏/真实感)vs 正交相机(平行投影——2.5D/建筑);轨道控制器(OrbitControls):鼠标拖拽旋转/滚轮缩放——「3D 查看器」标配。
- 点击拾取(Raycaster 射线):鼠标点击 → 从相机发一条射线 → 检测和哪些物体相交 → 拾取点击的物体——「点选」交互的灵魂。
游戏视角:第一人称(相机在角色眼睛)、第三人称(相机跟在角色后面)——相机是「玩家的眼睛」。
// 轨道控制器(鼠标旋转/缩放——3D 查看器标配)
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 惯性手感
// Raycaster:点击拾取(点选交互的灵魂)
const raycaster = new THREE.Raycaster();
const pointer = new THREE.Vector2();
renderer.domElement.addEventListener("click", (e) => {
// 屏幕坐标 → 归一化设备坐标
pointer.x = (e.clientX / innerWidth) * 2 - 1;
pointer.y = -(e.clientY / innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera); // 从相机发射线
const hits = raycaster.intersectObjects(scene.children, true);
if (hits.length > 0) {
console.log("点到了:", hits[0].object.name);
highlight(hits[0].object); // 高亮被点中的
}
});
【相机两种选型】
透视(PerspectiveCamera):近大远小——真实感(游戏)
正交(OrthographicCamera):平行投影——2.5D/俯视策略
【游戏视角】
第一人称:相机 = 角色眼睛(射击/FPS)
第三人称:相机跟在角色后(动作/RPG)
→ 相机跟随:每帧 lerp 平滑跟随角色位置
【Raycaster 应用】
点击拾取(选物体/捡装备)
悬停高亮(鼠标指哪亮哪)
拖拽移动(抓住物体跟着走)
→ 所有「鼠标和 3D 世界互动」都是它
交互术语
- 透视 vs 正交相机:近大远小 vs 平行投影。
- OrbitControls:轨道控制器——拖拽旋转滚轮缩放。
- Raycaster:射线检测——点击拾取灵魂。
- 归一化坐标:屏幕坐标转 NDC——射线计算前提。
- 第一/第三人称:相机位置决定视角。
- 相机跟随:lerp 平滑跟随——手感的细节。
- 悬停高亮:射线 + 材质变色——交互反馈。
本站收获:交互 = 相机(玩家眼睛)+ Raycaster(点击射线)。OrbitControls 让 3D 可浏览、射线让 3D 可操作——「看得见 + 点得中」就是 3D 交互的全部。
动画与物理:世界动起来
requestAnimationFrame · 关键帧/骨骼动画 · 物理引擎 · 碰撞3D 世界要「活」——物体动起来靠动画,落体弹跳靠物理。这一站,给静止的场景注入生命(第二十一本游戏循环的 Web 版)。
动起来三件事:
- 动画循环(requestAnimationFrame):浏览器每帧回调(约 60 次/秒)——就是 Web 版的游戏循环(第 2 站见过,这里展开)。
- 动画类型:关键帧动画(补间:位置/旋转插值)、骨骼动画(模型自带动作——导入的角色模型通常带骨骼动画,GLTFLoader 加载后播放)。
- 物理引擎:cannon-es(轻量 3D 物理)/ Ammo.js(Bullet 移植,重量级)——刚体、重力、碰撞自动算(第二十一本物理老朋友)。
// ① 动画循环(游戏循环的 Web 版)
let last = 0;
function animate(time) {
requestAnimationFrame(animate);
const dt = (time - last) / 1000; // deltaTime!(第二十一本)
last = time;
// 关键帧动画:补间插值
cube.position.y = 1 + Math.sin(time / 500) * 0.5; // 上下浮动
cube.rotation.y += dt * 0.5; // 匀速旋转
controls.update();
renderer.render(scene, camera);
}
animate();
// ② 骨骼动画(导入角色后播放)
gltf.animations; // 模型自带动作列表
mixer = new THREE.AnimationMixer(gltf.scene);
const walk = mixer.clipAction(gltf.animations.find(a => a.name === "Walk"));
walk.play(); // 播放走路动画
// ③ 物理引擎(cannon-es:刚体 + 重力 + 碰撞)
import * as CANNON from "cannon-es";
const world = new CANNON.World({ gravity: new CANNON.Vec3(0, -9.82, 0) });
const ballBody = new CANNON.Body({ mass: 1 }); // 刚体
ballBody.addShape(new CANNON.Sphere(0.5));
world.addBody(ballBody);
function animate(time) {
world.step(1 / 60); // 物理步进
ballMesh.position.copy(ballBody.position); // 物理→渲染同步
renderer.render(scene, camera);
}
【动画 vs 物理分工】
动画:播放预设动作(走路/攻击——美术做好的)
物理:实时计算(落体/碰撞/弹跳——引擎算的)
→ 角色走路用动画,石头下落用物理
【物理同步模式】
物理引擎算位置 → 每帧同步给 Three 网格
→ 「物理世界」和「渲染世界」两个世界对齐
动画物理术语
- requestAnimationFrame:浏览器动画循环——Web 游戏循环。
- deltaTime:帧间隔——速度一致性(第二十一本)。
- 关键帧动画:位置/旋转插值补间。
- 骨骼动画:模型自带动作——AnimationMixer 播放。
- cannon-es / Ammo.js:轻量 / 重量级物理引擎。
- 刚体 + 重力:物理世界的基础。
- 物理→渲染同步:两个世界的对齐。
本站收获:动起来 = requestAnimationFrame(循环)+ 动画(关键帧/骨骼)+ 物理(cannon 刚体重力)。动画是「美术编排」,物理是「引擎实时算」——两者配合,世界才活。
模型与资产:内容管线
glTF/GLB · Blender 导出 · Draco/KTX2 · 资产加载3D 内容从哪来?——Blender 建模 → glTF 格式 → 网页加载。模型格式、压缩、加载流程:3D 网页游戏的「内容管线」。
内容管线三件事:
- glTF(GLB):Web 3D 的标准格式(「3D 的 JPEG」)——几何+材质+动画+场景一包装。GLB 是二进制版(体积小、加载快)——Three.js 官方推荐格式。
- 建模工具(Blender):免费开源 3D 建模——建模 → 材质 → 绑定骨骼 → 导出 glTF/GLB。
- 压缩:Draco(几何压缩——顶点数据压 80%)、KTX2(纹理压缩——GPU 直接读)、Meshopt(更现代的几何压缩)——Web 3D 加载快的秘诀。
// 加载 glTF 模型(GLTFLoader)
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
import { DRACOLoader } from "three/addons/loaders/DRACOLoader.js";
// Draco 压缩:几何数据先解压
const draco = new DRACOLoader();
draco.setDecoderPath("https://www.gstatic.com/draco/versioned/decoders/1.4.1/");
const loader = new GLTFLoader();
loader.setDRACOLoader(draco);
loader.load("models/robot.glb", (gltf) => {
scene.add(gltf.scene); // 模型入场景
// gltf.animations 模型自带动画(第5站播放)
}, (xhr) => {
console.log("加载进度:", xhr.loaded / xhr.total * 100 + "%");
});
【glTF 是什么】
Web 3D 的标准格式(“3D 世界的 JPEG”)
一个 .glb 文件 = 几何 + 材质 + 贴图 + 动画 + 场景
→ Three.js / Babylon / Unity WebGL 都支持
【资产压缩三件套】
Draco:几何压缩(顶点位置/法线压 80%)
KTX2 :纹理压缩(GPU 直接读,省显存省加载)
Meshopt:现代几何压缩(更小更快)
→ 首屏加载快的幕后功臣(第10站展开)
【内容管线全流程】
Blender 建模/绑定 → 导出 .glb → Draco/KTX2 压缩
→ 上传 CDN(第10站)→ Three.js 加载 → 场景中使用
【模型来源】
自建:Blender(免费)/ 建模师外包
现成:Sketchfab(模型市场)/ Quixel(素材库)
→ 个人开发先用现成模型,练手不卡美术
【坑:格式与兼容】
老格式(OBJ/DAE):能用但不推荐(无压缩无动画)
FBX:美术常用但大——转 glTF 再上网页
→ 统一走 glTF/GLB 是 Web 3D 的规矩
资产术语
- glTF / GLB:Web 3D 标准格式——「3D 的 JPEG」。
- Blender:免费开源建模工具。
- Draco:几何压缩——顶点压 80%。
- KTX2:纹理压缩——GPU 直接读。
- GLTFLoader:模型加载器——官方标准。
- DRACOLoader:解压器——先解压再加载。
- 内容管线:建模→导出→压缩→上传→加载。
本站收获:内容管线 = Blender 建模 → glTF/GLB 导出 → Draco/KTX2 压缩 → CDN 加载。统一走 glTF + 压缩——「模型是 3D 世界的食材,格式是保鲜盒」。
游戏引擎:高层框架
Babylon.js · PlayCanvas · React Three Fiber · Cocos/Unity WebGLThree.js 是「3D 库」,要做完整的游戏还需要游戏引擎功能(场景管理/输入/UI/物理集成)——这一站,看高层框架怎么选。
框架地图:
- Three.js:最流行——灵活的 3D 库(渲染为主,其他自己拼)。
- Babylon.js:游戏引擎级——场景/物理/UI/音频全内置(微软出品,Web 3D 游戏选它更像「引擎」)。
- React Three Fiber:React 生态的 Three.js——用组件写 3D(「声明式 3D」,React 团队的项目)。
- PlayCanvas:在线编辑器 + 引擎——像「Web 版 Unity」(可视化拖拽)。
- Cocos Creator:国内小游戏主力——导出微信小游戏(3D 也支持)。
- Unity WebGL:Unity 项目导出网页版——适合已有 Unity 项目(包体大)。
【框架对比】
框架 定位 适合
Three.js 3D 库(灵活) 3D 展示/轻游戏
Babylon.js 游戏引擎 正经 Web 3D 游戏
R3F (React) 组件化 3D React 团队/数据可视化
PlayCanvas 在线引擎 可视化拖拽
Cocos Creator 小游戏引擎 微信小游戏(3D 也行)
Unity WebGL Unity 导出 已有 Unity 项目
【React Three Fiber(组件化 3D)】
<Canvas>
<ambientLight />
<mesh rotation={[0, angle, 0]}>
<boxGeometry />
<meshStandardMaterial color="teal" />
</mesh>
</Canvas>
→ 用 JSX 组件描述 3D 场景(声明式!——第十九本 SwiftUI 同思想)
【选型口诀】
3D 展示/学习 → Three.js
正经 Web 游戏 → Babylon.js(引擎功能全)
React 团队 → React Three Fiber
微信小游戏 → Cocos Creator(最成熟路径)
已有 Unity → Unity WebGL 导出
【游戏引擎 vs 3D 库】
3D 库:只管渲染(Three.js)——其他自己拼
游戏引擎:渲染+场景+物理+UI+输入+资源管理一体化
→ 做「游戏」选引擎,做「展示」选库
框架术语
- Babylon.js:Web 3D 游戏引擎——全内置。
- React Three Fiber:React 组件化 3D——声明式。
- PlayCanvas:在线可视化引擎——Web 版 Unity。
- Cocos Creator:小游戏引擎——微信小游戏主力。
- Unity WebGL:Unity 导出网页——已有项目用。
- 3D 库 vs 引擎:渲染 vs 一体化——按目标选。
- 选型口诀:展示 Three / 游戏 Babylon / 小游戏 Cocos。
本站收获:框架 = Three.js(灵活库)+ Babylon(游戏引擎)+ R3F(React 组件)+ Cocos(小游戏)。做展示用库、做游戏用引擎、上小游戏用 Cocos——「选型跟着目标走」。
交互与 UI:游戏操作
输入 · HUD · 点击拾取 · WebXR 初识游戏要「玩」起来——键盘/鼠标/触屏输入、HUD 界面、和 3D 世界的操作。这一站,把第 4 站的交互扩展成「游戏操作」。
游戏操作四件事:
- 输入:键盘(WASD 移动)、鼠标(视角/点击)、触屏(虚拟摇杆/手势)——手机游戏触屏优先(第二十一本输入老朋友)。
- HUD:叠加的 HTML/CSS 界面(血条/分数/按钮)——3D 场景 + 2D HTML UI 两层叠加(第二十一本 HUD)。
- 点击拾取:Raycaster(第 4 站)——点击 3D 物体捡装备/选单位。
- WebXR(VR/AR):浏览器原生 VR/AR——未来的 3D 交互(VR 眼镜看 3D 场景、AR 把 3D 放现实里)。
// ① 键盘输入(WASD 移动——第二十一本轮询)
const keys = {};
addEventListener("keydown", e => keys[e.code] = true);
addEventListener("keyup", e => keys[e.code] = false);
// 每帧查状态(Update 里轮询)
function update(dt) {
const speed = 3 * dt; // deltaTime 速度一致
if (keys["KeyW"]) player.position.z -= speed;
if (keys["KeyA"]) player.position.x -= speed;
if (keys["KeyD"]) player.position.x += speed;
}
// ② 触屏(手机——虚拟摇杆)
// 触屏坐标 → 摇杆位移 → 角色方向移动(手势检测)
// ③ HUD:HTML 叠在 3D 上
// <div id="hud">金币:<span id="gold">0</span></div>
// CSS: position:fixed; z-index:10;(3D canvas 之上)
// ④ 点击拾取(第4站 Raycaster)——点击敌人开打
renderer.domElement.addEventListener("click", (e) => {
const hit = pick(e); // 射线检测
if (hit?.object.name === "enemy") attack(hit.object);
});
【输入设计原则】
键盘:WASD/空格(桌面)
鼠标:视角拖拽 + 点击
触屏:虚拟摇杆 + 手势(手机优先)
→ 多端适配:检测设备类型切换输入方案
【UI 与 3D 分层】
3D 场景(Canvas)+ 2D UI(HTML/CSS)两层
→ 3D 互动用射线,2D 信息用 HTML(开发简单)
【WebXR 初识】
VR:眼镜看 3D 场景(AR/VR 设备)
AR:3D 叠到现实(手机摄像头)
→ Three.js 官方 WebXR 支持——未来交互方向
交互 UI 术语
- 键盘/鼠标/触屏:三端输入——设备适配。
- 虚拟摇杆:手机移动方案——触屏手势。
- HUD:HTML/CSS 叠 3D——两层叠加。
- 点击拾取:Raycaster——捡装备/选单位。
- WebXR:浏览器 VR/AR——未来交互。
- 输入轮询:每帧查键——第二十一本 Update。
- 多端适配:桌面/手机切换输入方案。
本站收获:游戏操作 = 输入(键鼠/触屏)+ HUD(HTML 叠加)+ 射线(点击互动)+ WebXR(未来)。3D 世界 + 2D UI 两层叠加——「3D 互动用射线,2D 信息用 HTML」。
性能优化:60fps 流畅
Draw Call · 实例化 · LOD · 纹理图集 · 遮挡剔除网页 3D 的性能瓶颈:CPU 提交渲染指令(Draw Call)+ GPU 像素量。低端手机也要 60fps——这一站,Web 3D 性能方法论(第二十一本同款哲学换个平台)。
性能五板斧:
- Draw Call:渲染指令数——1000 棵树 = 1000 次调用 = 卡;实例化(Instancing):相同的树一次调用画 1000 棵(第二十一本同款概念)。
- LOD(多级细节):远的用低模、近的用高模——第二十二本地图瓦片 LOD 的 3D 版。
- 纹理图集(Atlas):多张贴图合成一张——减少切换绑定。
- 遮挡剔除(Frustum Culling):屏幕外的物体不渲染——Three.js 默认开启视锥剔除。
- 显存/内存:纹理压缩(KTX2)、释放不用的资源——防低端机爆显存。
【帧预算(60fps = 16.7ms/帧)——第二十一本同款】
渲染 8ms + 逻辑 3ms + 物理 2ms + 余量
→ 用浏览器 Profiler(Performance 面板)先测量
【实例化(Instancing):1000 棵树一次画】
const count = 1000;
const instanced = new THREE.InstancedMesh(geometry, material, count);
// 设置每棵的位置/旋转/缩放
for (let i = 0; i < count; i++) {
const m = new THREE.Matrix4().makeTranslation(x[i], y[i], z[i]);
instanced.setMatrixAt(i, m);
}
scene.add(instanced);
// → 1 次 Draw Call 画 1000 棵树(vs 1000 次)
【LOD(多级细节)】
new THREE.LOD()
.addLevel(highModel, 0) // 近处:高模
.addLevel(midModel, 30) // 中距离
.addLevel(lowModel, 80); // 远处:低模
// 相机近了自动换高模——性能与画质平衡
【性能排查流程】
① Performance/Profiler 采样 → 找热点帧
② 是 Draw Call 多?→ 实例化/合批/图集
③ 是像素多?→ LOD/遮挡剔除/降分辨率
④ 是 JS 慢?→ 避免每帧分配/对象池(第二十一本)
→ 先测量再优化(全系列铁律)
【手机特有】
低端机 GPU 弱:降分辨率渲染(renderer.setPixelRatio)
发热降频:负载平缓(别瞬间吃满)
省电:画面静止时降帧率(60→30fps)
【显存管理】
纹理 KTX2 压缩(第6站)——省显存
离开场景释放:geometry.dispose()/material.dispose()
3D 小游戏「森林寻宝」:一开始 500 棵树逐个添加——千元机 15fps 卡成 PPT。整改:InstancedMesh 一次画 500 棵 → Draw Call 从 500 降到 1;远处树用 LOD 低模 → 帧率 55fps。Web 3D 优化的核心就一句话:**让 GPU 少干活**。
性能术语
- Draw Call:渲染指令数——Web 3D 第一瓶颈。
- Instancing(实例化):相同物体一次画——千树一画。
- LOD:远近换模型——细节分级。
- 纹理图集:多图合一——省绑定切换。
- 视锥剔除:屏幕外不渲染——Three 默认。
- 降分辨率:低端机 setPixelRatio——保帧率。
- 先测量再优化:Performance 定位——全系列铁律。
本站收获:性能 = Draw Call 实例化 + LOD 分级 + 图集 + 剔除 + 显存管理。16.7ms 是每帧预算——「让 GPU 少干活」就是 Web 3D 优化的全部秘密。
加载与发布:上线之路
首屏优化 · 进度条 · CDN · 微信小游戏 · 上线指标「点开即玩」的承诺靠什么兑现?首屏加载速度!模型再精美,加载 10 秒用户就跑了。这一站,Web 3D 的「最后一公里」。
加载优化四件事:
- 首屏速度:目标 3 秒内可玩——资源分包(先加载最小场景)+ 进度条(LoadingManager 显示进度)。
- 资源压缩:Draco/KTX2(第 6 站)+ gzip/brotli(Web 传输压缩)——体积砍半。
- CDN 分发:模型/纹理放 CDN(就近下载)——全球加速(第二十一本/二十二本 CDN 老朋友)。
- 微信小游戏:Cocos 导出(第 7 站)→ 微信平台——国内「免下载」主战场(包体限制 4M 主包 + 分包)。
【加载进度条(LoadingManager)】
const manager = new THREE.LoadingManager();
manager.onProgress = (url, loaded, total) => {
bar.style.width = (loaded / total * 100) + "%"; // 进度条
};
const loader = new GLTFLoader(manager);
【首屏优化策略】
① 分包:先加载「开场场景」(小)→ 后台加载正式场景(大)
② 骨架场景:先显示天空/地面(几 KB)→ 模型到了再出现
③ 压缩:Draco(几何) + KTX2(纹理) + brotli(传输) —— 体积减 70%
④ 懒加载:玩家走到哪,加载哪块(区域按需)
【发布平台】
H5 页面:任意服务器/CDN 托管 → 链接即玩
微信小游戏:Cocos 导出 → 微信开发者工具上传 → 审核
→ 国内「免下载」主战场是微信小游戏(社交裂变强)
【上线指标(Web 游戏)】
首屏时间:< 3 秒(用户耐心上限)
加载成功率:> 95%(弱网/低端机都能进)
帧率:主流机型 > 50fps
留存/分享:玩法 + 加载速度决定分享传播
【技术栈总结(全书串联)】
底层 WebGL → 引擎 Three.js/Babylon
→ 模型 glTF+压缩 → 交互射线 → 性能实例化/LOD
→ 加载分包+CDN → 平台 H5/小游戏
→ 一条完整的 Web 3D 游戏生产线
小哲团队把「森林寻宝」发成微信小游戏:Cocos Creator 开发 → 主包控制 4M(模型分包加载)→ 微信审核通过 → 群里发个链接点开即玩——分享裂变带来 10 万玩家。免下载 + 社交传播 = 微信小游戏的红利(也别忘了合规:版号要求同第二十一本)。
发布术语
- 首屏时间:< 3 秒可玩——用户耐心上限。
- LoadingManager:加载进度——进度条实现。
- 分包/懒加载:先小后大、按需加载。
- CDN:就近分发——全球加速。
- 微信小游戏:免下载主战场——4M 主包限制。
- brotli/gzip:Web 传输压缩——体积砍半。
- 上线指标:首屏/成功率/帧率/留存。
本站收获:上线 = 分包 + 压缩 + CDN + 进度条 + 微信小游戏。首屏 3 秒是承诺,加载优化是兑现——「点开即玩」四个字,全靠最后一公里。
技术栈速查 + 面试高频题
面试/开发前最后一页Web 3D 技术栈全景表
| 环节 | 核心知识点 | 一句话人话 |
|---|---|---|
| 底层 | WebGL、Canvas、GLSL、渲染管线、WebGPU | 浏览器 GPU 后门 |
| 引擎 | Three.js、Scene/Camera/Renderer、Mesh | Web 3D 事实标准 |
| 场景 | 场景图、PBR、灯光、贴图 | 搭世界 + 打光 |
| 交互 | 相机控制、Raycaster、OrbitControls | 看得见 + 点得中 |
| 动画物理 | requestAnimationFrame、骨骼动画、cannon | 世界动起来 |
| 资产 | glTF/GLB、Blender、Draco、KTX2 | 内容管线 |
| 框架 | Babylon、R3F、PlayCanvas、Cocos | 游戏引擎选型 |
| 交互 UI | 输入、HUD、射线拾取、WebXR | 游戏操作 |
| 性能 | Draw Call、Instancing、LOD、图集、剔除 | 让 GPU 少干活 |
| 发布 | 首屏优化、CDN、微信小游戏、进度条 | 点开即玩 |
面试高频题速记
- WebGL 是什么?→ 浏览器 GPU 接口——网页 3D 的地基(Canvas + GLSL)。
- Three.js 六大对象?→ Scene/Camera/Renderer + Geometry/Material/Mesh。
- 为什么用 Three.js 不用 WebGL?→ WebGL 太原始(300 行画三角形)——引擎封装。
- glTF 是什么?→ Web 3D 标准格式——几何+材质+动画一包(3D 的 JPEG)。
- 怎么点击 3D 物体?→ Raycaster 射线——相机发线检测相交。
- Draw Call 太多怎么优化?→ 实例化(InstancedMesh)+ 合批 + 图集。
- LOD 是什么?→ 远近换模型——性能与画质平衡。
- requestAnimationFrame 干嘛的?→ 浏览器动画循环——Web 版游戏循环。
- Draco/KTX2 有什么用?→ 几何/纹理压缩——加载快 80%。
- 首屏加载怎么优化?→ 分包 + 压缩 + CDN + 进度条 + 懒加载。
把第二十一本的渲染知识,搬进浏览器的世界。
WebGL 打开的是「浏览器里的 3D 世界」,
Three.js 让它从 300 行代码变成 6 个概念;
而真正让世界流畅的,永远是那句老话——让 GPU 少干活。