WebGL 与 Three.js

WebGL 与 Three.js——怎样理解浏览器里的 3D 渲染管线、GPU 思维、着色器、Three.js 抽象层和 React 生态中的 3D 工程实践,而不是只会把示例跑起来。

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 提供很多材质类型。

例如:

  • MeshBasicMaterial
  • MeshStandardMaterial
  • MeshPhongMaterial

它们的差别不只是“效果不同”。

还包括:

  • 是否参与光照
  • 计算成本
  • 是否适合 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 的性能直觉。

最小交付物:

一份场景性能观察记录。

验收标准:

  • 至少能指出一个资源过重问题
  • 能提出至少一个减少渲染负担的调整项

常见误区:

  • 只盯着模型面数,不看纹理、后处理和对象组织

延展阅读