第 16 课:综合实战——封装材质库
从能跑的 Shader 到能复用的 API
在业务里反复复制一份 ShaderMaterial 容易让 uniform 名称、默认值和清理职责分散。本课把渐变封装成 createGradientMaterial(options)。输入包括起止颜色、动画速度、方向以及 Shader 源码;返回一个独立的 THREE.ShaderMaterial。每次调用都创建新的 uniform 对象,两个网格就能有不同颜色和时间。调用方拥有返回的材质,停止使用时执行 dispose()。第 1、2 题先在静态页面验证 API 和参数,再用于 React Three Fiber(R3F)和 npm 包。
渐变公式与数据链路
顶点 Shader 把几何体 UV 传给片元。方向角 a 生成单位轴 (cos(a), sin(a)),dot(uv - 0.5, axis) + 0.5 把每个片元投影到这条轴上。这个 0–1 附近的值成为 mix(startColor, endColor, t) 的权重。动画只在权重上叠加有限振幅的正弦偏移,最后 clamp 到 0–1。速度改为 0 时,相位固定;方向从 0 改为 π/2 时,色带从水平变为垂直。JavaScript 的颜色选择器更新 THREE.Color uniform,而 uTime 在渲染循环以秒累加。
THREE.Color 会按 Three.js 的颜色管理解释 CSS 十六进制颜色;材质输出由渲染器处理到画布颜色空间。若与设计稿颜色不同,先检查输出颜色空间和 tone mapping,再检查输入颜色的编码。包内 Shader 源码以字符串导出,避免消费者必须额外配置 .vert/.frag loader。
R3F 组件与所有权
第 3 题附有 GradientPlane.jsx,示范用 useMemo 创建材质、useFrame 更新时间,以及卸载和依赖变化时释放旧材质。它是可复制到已有 R3F 项目的完整组件源码;本仓库不引入 React 运行时,练习页面仍用同一渐变公式提供可直接打开的静态 Three.js 预览。R3F 项目安装与自身 Three.js 版本相配的 react、react-dom、@react-three/fiber 和 three,再复制组件及 Shader 文件并按源码导入即可。共享一个材质给多个组件时,资源所有权必须改变,不能由任意一个组件自行释放。
本地打包与消费
第 4 题的 package/ 是可发布形态:package.json 声明 ESM 入口、exports、files 与 three peer dependency;index.js 暴露材质工厂与两段 Shader 字符串。先在 package/ 执行 npm pack --dry-run 检查将被发布的文件,再执行 npm pack 生成本地压缩包。静态练习页直接从 package/index.js 导入,确认包入口可以在浏览器使用。要在独立项目消费,可运行 npm install ./shader-works-gradient-material-0.1.0.tgz,然后 import { createGradientMaterial } from '@shader-works/gradient-material' 并把返回材质赋给网格。公网 npm publish 涉及包名所有权与账号,不是练习自动步骤;本地打包及消费已覆盖包结构和导入路径的核心验收。
练习与验收
| 难度 | 题目 | 可观察或可测量验收 |
|---|---|---|
| ⭐ | 渐变材质工厂 | 工厂分别生成两个网格的渐变材质,页面离开时各自释放 |
| ⭐⭐ | 颜色、速度、方向参数 | 控件改变预览;速度 0 停止动画,方向改变色带轴 |
| ⭐⭐⭐ | R3F 渐变组件 | 静态预览运行;组件源码可在 R3F 项目以 props 控制并在卸载时释放材质 |
| ⭐⭐⭐⭐ | 材质包本地打包与消费 | npm pack --dry-run 列出入口和 Shader 源码;本地压缩包可安装并导入;静态预览从包入口直接导入 |