mcpField
T
typegpu

typegpu

github.com/chanjing-ai创意与媒体0178 次安装14 周前更新
代码仓库

关于 typegpu

面向 FrameVideo 的 WebGPU 视觉开发技能,涵盖着色器、GPU 计算,以及与时间轴和帧捕获的同步。

主要功能

  1. 01WebGPU 着色器与计算模式
  2. 02基于 FrameVideo seek 的渲染
  3. 03同步注册 GSAP 时间轴
  4. 04同步 GPU 帧捕获
  5. 05确定性视觉渲染

适用场景

  1. 01创建粒子或物理模拟
  2. 02构建自定义着色器和后期效果
  3. 03按 FrameVideo 指定时间渲染效果

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/chanjing-ai/framevideo --skill typegpu

安装后在 Claude Code 中输入/typegpu确认已加载。

详细介绍

FrameVideo 的 TypeGPU / WebGPU

适用场景

以下场景适合使用 TypeGPU / WebGPU:

  • GPU 计算着色器 — 粒子系统、物理模拟
  • 自定义 WGSL 着色器 — 液态玻璃、元球、光线步进
  • 高性能特效 — 复杂片段着色器、后期处理
  • 高级视觉特效 — 噪声场、反应扩散、流体模拟
  • 计算密集型视觉效果 — 当 Three.js 抽象层级过高时

请勿使用

以下场景应避免使用 TypeGPU / WebGPU:

  • 简单的 3D 场景 — 使用 three(更简单、更成熟)
  • 2D 动画 — 使用 gsap(简单得多)
  • 标准特效 — WebGPU 的浏览器支持有限,请使用 WebGL 替代方案
  • 文本 / UI 动画 — 使用 HTML + GSAP(渲染效果更好)

快速开始

FrameVideo 中的基础 WebGPU 片段着色器:

<canvas id="gpu-layer" width="1920" height="1080"></canvas>

<script>
  (async () => {
    if (!navigator.gpu) return;
    
    const adapter = await navigator.gpu.requestAdapter();
    const device = await adapter.requestDevice();
    const canvas = document.getElementById("gpu-layer");
    const ctx = canvas.getContext("webgpu");
    
    ctx.configure({ 
      device, 
      format: navigator.gpu.getPreferredCanvasFormat() 
    });

    // Create time uniform buffer
    const timeUniform = new Float32Array([0]);
    const timeBuf = device.createBuffer({
      size: 16,
      usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
    });

    // Listen for FrameVideo seek events
    window.addEventListener("fv-seek", async (event) => {
      const time = event.detail.time;
      
      // Update time uniform
      timeUniform[0] = time;
      device.queue.writeBuffer(timeBuf, 0, timeUniform);
      
      // Render your pipeline...
      // [render pass code here]
      
      // IMPORTANT: flush GPU work before frame capture
      await device.queue.onSubmittedWorkDone();
    });
  })();
</script>

要点:

  1. 监听 fv-seek 事件
  2. 使用 event.detail.time 更新 uniform
  3. 在捕获帧之前调用 await device.queue.onSubmittedWorkDone()
  4. 对 WebGPU 是否可用进行保护检查

约定

  • 异步初始化 WebGPU(await navigator.gpu.requestAdapter()),但所有 GSAP 补间都必须同步注册 — 在任何 await 之前。FrameVideo 播放器会在页面加载时立即读取时间线。
  • 使用 FrameVideo 的时间进行渲染,而不是 performance.now()。
  • 监听 fv-seek 事件,并在该精确时间重新渲染。
  • 防范 WebGPU 不可用的环境 — 适配器不会替你检查。
  • 对于视频渲染,提交 GPU 工作后调用 await device.queue.onSubmittedWorkDone(),确保画布在捕获帧之前完成刷新。

适配器会在每次 seek 时设置 window.__fvTypegpuTime,并派发 new CustomEvent("fv-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("fv-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 — 降采样:**将全分辨率纹理渲染到小尺寸纹理(1/6 分辨率)。降采样过程中的双线性过滤会自然地对像素求平均。
  2. **通道 2 — 玻璃合成:**对磨砂内部采样小尺寸纹理(双线性放大 = 强烈平滑模糊),对锐利区域和色差折射采样全分辨率纹理。

这与 TypeGPU 的 textureSampleBias mip 层级方案效果相当,无需生成 mipmap。

透明与不透明画布

  • 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 驱动渲染循环 — 仅响应 fv-seek 进行渲染。
  • 不使用 performance.now() 获取动画时间 — 读取 window.__fvTypegpuTime 或 e.detail.time。
  • GPU 提交后,为渲染模式的帧捕获调用 await device.queue.onSubmittedWorkDone()。

评论

支持 Markdown