关于 typegpu
面向 HyperFrames 的 TypeGPU 和原生 WebGPU 技能,说明时间线同步、响应 seek 的渲染方式及 GPU 帧捕获要求。
主要功能
- 01异步初始化前同步注册 GSAP 时间线
- 02依据 HyperFrames 时间和 seek 事件渲染
- 03处理 GPU 帧捕获与队列完成
- 04设计模糊及透明画布渲染通道
- 05使用确定性的动画输入
适用场景
- 01在 HyperFrames 中制作可 seek 的 GPU 效果
- 02将 GPU 合成内容渲染为视频
- 03在 HTML 内容上叠加 GPU 视觉效果
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ heygen-com/ hyperframes --skill typegpu安装后在 Claude Code 中输入/typegpu确认已加载。
详细介绍
HyperFrames 的 TypeGPU / WebGPU
HyperFrames 通过其 typegpu 运行时适配器支持 TypeGPU 和原始 WebGPU。该适配器不负责管理你的渲染管线。它会发布 HyperFrames 时间并分发 seek 事件,以便你的合成能够渲染精确的 GPU 帧。
约定
- 异步初始化 WebGPU(
await navigator.gpu.requestAdapter()),但必须同步注册所有 GSAP 补间动画——在任何await之前。HyperFrames 播放器会在页面加载时立即读取时间线。 - 根据 HyperFrames 时间进行渲染,而不是使用
performance.now()。 - 监听
hf-seek事件,并在该时间点精确重新渲染。 - 在 WebGPU 不可用的环境中加入保护——适配器不会替你进行检查。
- 对于视频渲染,在提交 GPU 工作后调用
await device.queue.onSubmittedWorkDone(),以确保在捕获帧之前画布已完成刷新。
每次 seek 时,适配器都会设置 window.__hfTypegpuTime 并分发 new CustomEvent("hf-seek", { detail: { time } })。
基本模式
<canvas id="gpu-layer"></canvas>
<script>
(async () => {
if (!navigator.gpu) return;
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) return;
const device = await adapter.requestDevice();
const canvas = document.getElementById("gpu-layer");
canvas.width = 1920;
canvas.height = 1080;
const ctx = canvas.getContext("webgpu");
const fmt = navigator.gpu.getPreferredCanvasFormat();
ctx.configure({ device, format: fmt, alphaMode: "opaque" });
// Build your pipeline, buffers, bind groups...
const timeUniform = new Float32Array([0]);
const timeBuf = device.createBuffer({
size: 16,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
function render(t) {
timeUniform[0] = t;
device.queue.writeBuffer(timeBuf, 0, timeUniform);
const enc = device.createCommandEncoder();
const pass = enc.beginRenderPass({
colorAttachments: [
{
view: ctx.getCurrentTexture().createView(),
loadOp: "clear",
clearValue: { r: 0, g: 0, b: 0, a: 1 },
storeOp: "store",
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.draw(3);
pass.end();
device.queue.submit([enc.finish()]);
}
render(0);
window.addEventListener("hf-seek", (e) => render(e.detail.time));
})();
</script>
时间线注册
驱动文本、字幕或 HTML 元素的 GSAP 补间动画必须同步注册——在任何 await 之前:
const tl = gsap.timeline({ paused: true });
// Caption tweens: synchronous, added before WebGPU init
gsap.set(".cap", { opacity: 0 });
tl.to("#cap-1", { opacity: 1, duration: 0.3 }, 1.0);
tl.to("#cap-1", { opacity: 0, duration: 0.2 }, 3.5);
window.__timelines["my-comp"] = tl;
// GPU-dependent tweens can go inside the async IIFE
(async () => {
// ... WebGPU init ...
const proxy = { value: 0 };
tl.to(proxy, { value: 1, duration: 2, onUpdate: render }, 0.5);
})();
基于视频的效果(液态玻璃、扭曲)
要将 <video> 用作 GPU 输入纹理:
const videoEl = document.getElementById("aroll");
// Wait for video metadata before creating the texture
await new Promise((r) => {
if (videoEl.readyState >= 1) r();
else videoEl.addEventListener("loadedmetadata", r, { once: true });
});
// Create texture at the video's NATIVE resolution
const vw = videoEl.videoWidth,
vh = videoEl.videoHeight;
const bgTex = device.createTexture({
size: [vw, vh],
format: "rgba8unorm",
usage:
GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.RENDER_ATTACHMENT,
});
function render(t) {
try {
device.queue.copyExternalImageToTexture({ source: videoEl }, { texture: bgTex }, [vw, vh]);
} catch (_) {
/* frame not decoded yet */
}
// ... draw ...
}
渲染模式注意事项: 无头 Chrome 可能无法对视频元素执行 copyExternalImageToTexture。对于生产渲染,请使用 FFmpeg 预先提取关键帧并保存为 PNG,然后将其作为图像纹理加载。
通过降采样通道实现磨砂模糊
单通道高斯核无法实现玻璃般的磨砂模糊。请使用两通道方案:
- 通道 1 — 降采样: 将全分辨率纹理渲染到小型纹理(1/6 分辨率)。降采样时的双线性过滤会自然地对像素求平均。
- 通道 2 — 玻璃合成: 对磨砂内部采样小型纹理(双线性放大 = 强平滑模糊),对清晰区域和色差折射采样全分辨率纹理。
这种方法无需生成 mipmap,即可实现与 TypeGPU 的 textureSampleBias mip 级别方法相同的效果。
透明画布与不透明画布
alphaMode: 'opaque'— GPU 画布渲染完整画面(视频 + 效果)。当 GPU 管线处理所有视觉内容时使用。alphaMode: 'premultiplied'— GPU 画布在 alpha = 0 的位置透明,使下方的 HTML 元素能够显示出来。用于叠加在常规<video>元素上的覆盖层(粒子、路径动画)。
WGSL 全屏三角形
全屏效果的标准顶点着色器(无需顶点缓冲区):
struct Vo { @builtin(position) pos: vec4f, @location(0) uv: vec2f }
@vertex fn vs(@builtin(vertex_index) vi: u32) -> Vo {
let ps = array<vec2f, 3>(vec2f(-1., -1.), vec2f(3., -1.), vec2f(-1., 3.));
let ts = array<vec2f, 3>(vec2f(0., 1.), vec2f(2., 1.), vec2f(0., -1.));
return Vo(vec4f(ps[vi], 0., 1.), ts[vi]);
}
使用 pass.draw(3) 绘制——一个覆盖视口的三角形。
圆角矩形 SDF(液态玻璃胶囊)
fn sdf_box(p: vec2f, half_size: vec2f, corner_radius: f32) -> f32 {
let d = abs(p) - half_size + vec2f(corner_radius);
return length(max(d, vec2f(0.))) + min(max(d.x, d.y), 0.) - corner_radius;
}
使用它为玻璃效果定义内部/环形/外部区域。负值表示位于形状内部。
确定性渲染
- 不使用
Math.random()——请使用带种子的 PRNG。 - 渲染循环不使用
requestAnimationFrame——仅响应hf-seek进行渲染。 - 动画时间不使用
performance.now()——读取window.__hfTypegpuTime或e.detail.time。 - 提交 GPU 工作后,为渲染模式的帧捕获调用
await device.queue.onSubmittedWorkDone()。
评论
支持 Markdown