用途
Shader Fills 用于在设计产物中实现代码驱动的着色器填充与特效。其核心理念是:着色器是代码,而非不透明的资源。每个着色器由 GLSL 片段源码与 JSON uniforms 清单组成,直接持久化在页面 HTML 中,并由内嵌于同一 HTML 的自包含 WebGL 运行时实时渲染。因此着色器在编辑器、分享链接以及导出的独立文件中均可工作。用户可在代码面板中查看和编辑 GLSL,uniform 旋钮会自动出现在检查器中(填充 → Shader 绘制类型,以及 特效 → Shader)。
主要能力
持久化格式(v1)
每个着色器对应一个定义块,放置在 </body> 之前,使用 <script type="application/x-agent-native-shader"> 标签,携带 data-shader-id、data-shader-name、data-shader-mode 属性。源码以 /*! an-shader v1 开头,后接 JSON uniforms 清单,再跟 GLSL 代码。
元素引用支持两种方式,且可在同一元素上共存(类似 Figma):
- 填充:
data-an-shader-fill,canvas 渲染在元素内容之后,使用负 z-index 并处于隔离的层叠上下文中,位于元素自身背景之上。应保留静态回退背景色,以便在无 JS/WebGL 时仍能渲染。 - 特效:
data-an-shader-effect,透明叠加 canvas 位于内容之上,pointer-events: none。每个元素的覆盖值存放在单独属性data-an-shader-effect-uniforms中。
运行时必须以 <script data-agent-native-shader-runtime data-runtime-version="1"> 的形式在每份文档中嵌入一次,由 ensureShaderRuntime() / applyShaderToHtml() 负责处理。
应用着色器(推荐路径)
使用纯辅助函数加源码编辑操作。在 templates/design/ 中:
// script: apply-shader.ts — run with: pnpm exec tsx apply-shader.ts
import { applyShaderToHtml, newShaderId } from "./shared/shader-fills";
import { getGlslShaderPreset } from "./shared/shader-presets";
const preset = getGlslShaderPreset("water-caustics")!;
const result = applyShaderToHtml(currentHtml /* from read-source-file */, {
nodeId: "hero", // the element's data-agent-native-node-id
def: {
id: newShaderId(),
name: preset.label,
mode: preset.mode, // "fill" | "effect"
glsl: preset.glsl,
uniforms: preset.uniforms,
},
fallbackColor: "#06283d", // fills only — static no-JS fallback
});
if (result.errors.length) throw new Error(result.errors.join("; "));
// result.html → write back via apply-source-edit (full-replace) with the
// versionHash you got from read-source-file.
操作流程:read-source-file → 变换 → apply-source-edit(edit: { kind: "full-replace", content }),并传入 expectedVersionHash。若只是小幅手改(例如调整清单中某个 uniform 值),也可用 edit-design 对该块原文做精确搜索替换。
移除使用 removeShaderFromNode(html, nodeId, mode?) — 清除注解并垃圾回收未被引用的定义块。
从选择器发起「Create a custom shader fill.」
填充选择器中的 Create new (AI) 磁贴会用隐藏上下文行(designId、fileId、目标 nodeId、mode)预填此提示。随后:
- 仅当用户完全没给描述时,才询问想要的外观。
- 为该请求编写原创 GLSL(不要只是重新套用预设),暴露 2–5 个精心选择的 uniform 作为旋钮。按用户描述控件的方式命名旋钮。
- 通过上述辅助流程应用,目标是提供的 nodeId。
- 汇报着色器名称、其旋钮,以及 GLSL 可在代码面板中编辑。
GLSL 规则(WebGL1 / GLSL ES 1.00)
- 以
precision highp float;开头,写入gl_FragColor。 - 使用到时声明
uniform vec2 u_resolution;和uniform float u_time;— 运行时自动驱动它们(u_time以秒为单位;在prefers-reduced-motion下保持为 0)。切勿把内置变量放进清单。 - 清单中的每个 uniform 都必须在 GLSL 中按映射类型声明:
float→uniform float,vec2→uniform vec2,color→uniform vec3(颜色以归一化 0–1 RGB 传入)。校验会拒绝未使用的旋钮。 - uniform 名称匹配
u_[A-Za-z0-9_]+;不超过 16 个 uniform;float 旋钮需要 min/max/step;颜色值为#rrggbb十六进制。 - 循环边界必须是编译期常量(
for (int i = 0; i < 5; i++))。 - 特效叠加在内容之上:不进行输出预乘,因此
gl_FragColor = vec4(color, alpha)且 alpha < 1 时可干净地叠加。 - 切勿在 GLSL、名称或标签中包含
</script、<script或会破坏*/的序列 —validateShaderDef()会强制检查;写入前请运行它(或运行会调用它的applyShaderToHtml)。
预设库
shared/shader-presets.ts 中的 GLSL_SHADER_PRESETS 附带 12 个精选预设 — 填充类:mesh-gradient、glowing-wave、water-caustics、fractal-noise、clouds、nebula、moire、concentric-rings、pattern-grid;特效类:film-grain、halftone、scanlines。应用预设会把一份新副本(新 id)盖入设计;此后即为用户可编辑的代码。
填充与特效对比
| 项目 | 填充(data-an-shader-fill) | 特效(data-an-shader-effect) |
|---|---|---|
| Canvas 位置 | 内容之后(隔离层叠上下文中的负 z-index,位于元素自身背景之上) | 内容之上的叠加层,pointer-events: none |
| 输出 | 不透明像素(一种材质) | 透明叠加层(alpha 合成) |
| 回退 | 元素保留静态背景色 | 退化为无叠加 |
| 覆盖属性 | data-an-shader-uniforms | data-an-shader-effect-uniforms |
限制
失败情形(无 WebGL、编译错误、每屏挂载超过 8 个)会移除 canvas,并把原因记录在元素的 data-an-shader-error 属性上 — 当着色器「不显示」时检查该属性。