WebGL 与 Three.js
为什么前端工程师会越来越常碰到 3D
过去很多前端项目和 3D 没太大关系。
现在情况不一样了。
越来越多的 Web 产品开始碰到:
- 品牌展示页里的 3D 交互
- 电商中的 3D 商品预览
- 地图、数据和空间可视化
- 游戏化界面
- 编辑器和创作工具
- CAD、BIM、数字孪生类产品
这并不意味着所有前端工程师都要成为图形程序员。
但如果你的项目已经进入这类场景,只懂 DOM 和普通 CSS 动画就不够了。
你需要理解:
- GPU 在做什么
- 渲染管线大概怎么工作
- Three.js 到底帮你抽象了什么
- 什么时候该往底层走,什么时候不该
先把 WebGL 讲简单
WebGL 可以先理解成:
浏览器暴露给 JavaScript 的一套图形 API,让你可以把数据交给 GPU 渲染。
这句话里最关键的是“把数据交给 GPU”。
也就是说,WebGL 的思路和普通 DOM 编程不一样。
DOM 编程更像:
- 创建元素
- 改样式
- 浏览器帮你渲染
而 WebGL 更像:
- 组织顶点数据
- 提供着色器程序
- 控制缓冲区、纹理、矩阵
- 让 GPU 按管线去执行
这就是为什么很多人第一次接触 WebGL 会觉得陡。
它不是“多写一点 JavaScript”。
它背后是完全不同的渲染思维。
WebGL 真正解决的是什么问题
不是“在网页上做酷炫效果”这么简单。
它解决的是:
浏览器环境里如何利用 GPU 处理大量图形计算和绘制。
如果没有这层能力,很多 3D 场景、粒子系统、复杂图表和空间可视化都很难达到可接受性能。
所以 WebGL 的核心价值不是样式。
而是算力和渲染模型。
为什么 GPU 思维和普通前端思维差别很大
普通前端更常以组件、状态、布局和事件来组织问题。
GPU 图形更常关注:
- 顶点
- 片元
- 缓冲区
- 纹理
- 矩阵变换
- draw call
这意味着你在 3D 场景里最常做的事,不是“插入一个节点”。
而是“把一批数据以正确形式喂给渲染管线”。
如果没有这个转变,很多 Three.js 代码虽然能写,但很难真正理解性能和边界。
WebGL 渲染管线大概长什么样
一个高层次的理解通常足够覆盖多数前端工程语境:
- CPU 侧准备数据
- 顶点进入 vertex shader
- 图元组装
- 光栅化
- fragment shader 计算像素输出
- 最终写入帧缓冲
- 浏览器把结果显示到画布
这不是考试式背诵。
它的意义在于帮助你理解:
- 为什么顶点数据组织方式很重要
- 为什么 shader 是核心
- 为什么某些效果更吃 fragment 阶段
- 为什么 draw call 多了会影响性能
顶点、片元、缓冲区分别是什么
顶点
顶点可以简单理解成:
构成图形形状的一批关键点。
一个三角形至少要三个顶点。
更复杂的模型会有大量顶点。
片元
片元可以先理解成:
光栅化阶段产生的候选像素数据。
它还不是最终屏幕像素,但已经非常接近“每个点要画什么颜色”这个问题。
缓冲区
缓冲区就是你交给 GPU 的一块数据存储。
顶点位置、法线、UV、索引等都可能放在缓冲区里。
理解这几个概念后,WebGL 的“数据驱动渲染”味道就出来了。
Shader 到底是什么
Shader 可以简单理解成:
运行在 GPU 上的小程序。
最常见的两类是:
- vertex shader
- fragment shader
vertex shader
主要负责每个顶点的空间变换。
例如:
- 模型坐标到世界坐标
- 世界坐标到相机坐标
- 相机坐标到裁剪空间
fragment shader
主要负责像素层面的颜色、纹理采样、光照和后处理计算。
如果你看到一段 GLSL 代码在做颜色混合、法线计算、噪声、渐变,那往往是在 fragment 阶段。
为什么 shader 是 WebGL 的灵魂
因为它决定的不只是“怎么画”。
而是“渲染行为的真正可编程部分”。
你可以把 Three.js 理解成帮你组织场景和对象。
但真正把图形算出来的,仍然是 shader 和 GPU。
这也是为什么只会 Three.js API 而完全不理解 shader,很多问题就会停留在“会用”阶段。
Three.js 为什么重要
因为直接写原生 WebGL 很快就会碰到大量底层样板:
- 上下文管理
- 缓冲区绑定
- shader 编译
- 纹理与材质管理
- 矩阵运算
- 相机与光源组织
Three.js 的价值就是在这些复杂度之上提供更可用的抽象。
它没有改变 WebGL 的本质。
但它显著降低了你进入 3D 工程的门槛。
Three.js 帮你抽象了哪些核心对象
最常见的几个是:
Scene
场景树的根。
所有对象、光源通常都会挂在这里。
Camera
相机决定你从什么视角看场景。
Renderer
渲染器负责把场景和相机送进底层渲染流程。
Geometry
几何体描述形状。
Material
材质描述表面如何被着色。
Mesh
几何体和材质结合起来,才是可渲染对象。
这一组抽象非常重要。
因为它把底层图形编程压缩成了前端工程师更容易理解的对象模型。
Geometry 和 Material 为什么要分开
这是 Three.js 非常典型的设计。
形状和表面是两个维度。
同一个几何体可以搭配不同材质。
同一个材质也可以用于多个几何体。
这不是语法上的拆分。
它体现的是图形系统里的职责边界。
相机为什么不是“看一眼这么简单”
相机决定的不只是视角。
还包括:
- 透视还是正交
- 可见范围
- 近裁剪面和远裁剪面
- 画面空间感
如果近远裁剪面设置不合理,场景可能出现深度精度问题。
如果 FOV 设置不合理,画面观感也会非常怪。
所以相机不是随便设个值就行。
光源为什么常常比模型本身更影响观感
很多初学者会把重点都放在模型上。
实际上 3D 场景里,“看起来像不像” 很大程度取决于:
- 光照模型
- 材质参数
- 阴影
- 环境贴图
一个模型即使本身没变,换一套光照和环境,观感可能完全不同。
这也是为什么图形工程里材质和光照要单独理解。
材质为什么是工程判断题
Three.js 提供很多材质类型。
例如:
MeshBasicMaterialMeshStandardMaterialMeshPhongMaterial
它们的差别不只是“效果不同”。
还包括:
- 是否参与光照
- 计算成本
- 是否适合 PBR 工作流
在现代产品里,MeshStandardMaterial 往往更接近主流真实感工作流。
但更真实通常也意味着更多计算和资源成本。
PBR 为什么这些年越来越常见
PBR 是 physically based rendering。
简单理解,就是更接近物理规律地描述材质与光的关系。
这让不同材质在不同光照环境下更稳定、更可信。
对于电商展示、产品展示、写实场景,它非常有用。
但它也会把资源质量和环境设置要求拉高。
React Three Fiber 到底在做什么
React Three Fiber 不是一个新的 3D 引擎。
它更准确的定位是:
用 React 的声明式模型来驱动 Three.js 场景。
这会带来几个直接好处:
- 组件化组织场景
- 更自然地接入 React 状态
- 更容易和现有 React 应用集成
但也有边界:
- 图形性能问题不会因为用了 React 就消失
- 不理解 Three.js 本身,R3F 也会写得很吃力
所以更稳妥的顺序通常是:
先理解 Three.js 场景模型,再用 R3F 组织复杂应用。
Three.js 和 React Three Fiber 是什么关系
可以把 Three.js 理解成底层图形引擎抽象。
把 React Three Fiber 理解成 React 风格的场景描述层。
也就是说:
- 你真正渲染的仍然是 Three.js 世界
- R3F 只是换了一种更贴近 React 的写法
理解这个关系非常重要。
否则很容易把框架语法误当成图形原理。
什么时候该直接用 Three.js,什么时候适合 R3F
更适合直接用 Three.js 的情况
- 小型独立 demo
- 非 React 项目
- 需要更直接地控制底层流程
更适合用 R3F 的情况
- React 应用中的复杂 3D 模块
- 需要组件化复用
- 需要和 React 状态、路由、UI 生态协同
这不是绝对边界。
但这个判断通常够实用。
WebGL 性能问题最常从哪里来
前端接触 3D 后,最容易把性能理解成“显卡够不够强”。
其实很多问题来自更具体的工程点:
- draw call 太多
- 几何体过重
- 纹理太大
- 过多后处理
- shader 太重
- 场景更新频率不必要地过高
换句话说,性能问题常常不是一个总开关。
而是一连串设计选择积累出来的。
Draw Call 为什么值得重点理解
draw call 可以先粗略理解成:
CPU 向 GPU 发起的一次绘制命令。
它不是唯一性能指标。
但在实时图形里非常关键。
如果场景里对象数量太碎、材质切换频繁、合批做得差,draw call 很容易上升。
这会增加 CPU 到 GPU 的调度压力。
所以优化 3D 场景时,很多时候不是“减少一切”,而是“减少不必要的绘制组织成本”。
纹理为什么经常是隐形大户
模型多边形很容易被关注。
但真实产品里,显存和加载压力经常更被纹理放大。
要特别注意:
- 纹理尺寸
- 压缩格式
- mipmap
- 是否重复加载
如果纹理策略不稳,页面很容易出现:
- 首屏慢
- 内存高
- 移动端发热
- 切场景卡顿
后处理为什么很好看,也很容易过量
Bloom、景深、色调映射、抗锯齿、屏幕后处理,确实能明显提升质感。
但它们往往也会增加额外渲染成本。
所以一个成熟 3D 页面通常不会无脑叠效果。
而是围绕目标体验做预算。
Three.js 项目为什么也要讲资源管控
普通前端项目会讲懒加载、图片优化、代码分割。
3D 项目同样需要讲资源管理,只是对象换成了:
- glTF / GLB 模型
- 纹理
- HDR 环境图
- shader 资源
如果这些资源的加载、缓存和释放做不好,产品会非常重。
glTF 为什么成为主流格式之一
因为它更适合现代实时 3D 传输和渲染。
相比一些更传统的模型格式,glTF 更强调:
- 运行时可用性
- 材质与动画表达
- 传输效率
在 Three.js 生态里,它也是非常常见的资产格式。
WebGL 和 WebGPU 是什么关系
WebGPU 经常被描述成 WebGL 的下一代。
这个说法有方向性,但要避免说得过满。
更稳妥的理解是:
WebGPU 提供了更现代、更底层、更统一的 GPU 编程模型,覆盖图形和计算能力。
它不是对 WebGL 的简单小升级。
它代表的是更接近现代 GPU API 的路线。
那现在是不是就该全面转 WebGPU
不应该这么讲。
今天的大多数 Web 3D 产品、教学资料、工程生态和成熟库,仍然大量建立在 WebGL 和 Three.js 之上。
WebGPU 值得跟进,但是否立即采用,要看:
- 浏览器支持目标
- 团队能力
- 依赖生态
- 场景需求
所以更成熟的判断通常是:
- WebGL / Three.js 仍然是当前生产主轴之一
- WebGPU 是重要趋势,但不是所有团队现在的默认答案
中国互联网语境下,Web 3D 常见压力点
在国内场景里,Web 3D 经常会碰到这些现实约束:
- 活动页和品牌页需要效果
- 移动设备和 WebView 环境复杂
- 资源预算很紧
- 页面往往不是纯 3D,而是 2D 业务与 3D 混排
这意味着成熟方案通常不是追求最完整的图形特性。
而是要平衡:
- 首屏
- 兼容
- 交互稳定性
- 视觉收益
海外产品语境里,为什么 React Three Fiber 更常被提到
很多海外产品和创作类网站更倾向于:
- 在 React 应用中嵌入 3D 模块
- 用组件体系组织可复用场景
- 把动效、设计系统和 3D 表达结合起来
这会让 R3F 的生态优势更明显。
但不管语境如何,底层图形判断仍然离不开 WebGL / Three.js 本身。
这类主题为什么很适合做表达训练
因为它很容易被讲成:
- WebGL 是 3D
- Three.js 更简单
这不够。
更好的表达应该能继续说明:
- WebGL 真正解决什么问题
- Three.js 抽象了哪些底层复杂度
- shader 在整个系统里是什么位置
- 为什么 3D 性能问题和普通 DOM 性能问题思路不同
- WebGPU 为什么重要但不该被说成“现在都该换”
建议实践
实践 1:用原生 Three.js 搭最小场景
练什么:
理解 scene、camera、renderer、geometry、material 的关系。
最小交付物:
一个可旋转的立方体 demo。
验收标准:
- 能解释每个核心对象的职责
- 能独立加入光源和相机控制
常见误区:
- 只会复制示例,不理解几何体和材质为何分开
实践 2:用 R3F 把同一场景改成组件化版本
练什么:
理解 Three.js 与 React 声明式组织方式的关系。
最小交付物:
一个 React Three Fiber 版本的相同场景。
验收标准:
- 能说明 R3F 没有取代 Three.js,只是换了组织方式
- 能把状态更新安全接到场景逻辑里
常见误区:
- 把 React 组件语法误当成图形原理
实践 3:做一次 3D 资源性能审查
练什么:
建立模型、纹理、draw call 的性能直觉。
最小交付物:
一份场景性能观察记录。
验收标准:
- 至少能指出一个资源过重问题
- 能提出至少一个减少渲染负担的调整项
常见误区:
- 只盯着模型面数,不看纹理、后处理和对象组织