Remotion 视觉效果实战指南:掌握 `effects` 数组与 `createEffect()` 自定义特效开发

发布时间:2026/9/8 22:57:57
Remotion 视觉效果实战指南:掌握 `effects` 数组与 `createEffect()` 自定义特效开发 Remotion 视觉效果实战指南掌握effects数组与createEffect()自定义特效开发【免费下载链接】remotion Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotionCanvas/WebGL2 视觉特效是 Remotion 程序化视频体系中的重要一环把brightness()、blur()、rgbShift()这类「效果函数」塞进effects数组即可对Video、CanvasImage、Solid、HtmlInCanvas等基于 canvas 的组件逐帧施加像素级变换。本指南以仓库内 Agent 技能文档 packages/skills/skills/remotion-markup/effects.md 为核心脉络讲解如何安装并使用内置效果、如何在渲染时开启 WebGL2、如何通过createEffect()编写可复用、可参数化、可在 Studio 中编辑并可与官方效果自由叠加的自定义特效最终给出可复制、可运行的完整代码。effects 系统速览与适用范围安装效果包内置效果统一由remotion/effects包提供安装方式与 Remotion 其他生态包一致npx remotion add remotion/effects安装完成后效果函数就可以作为参数传给基于 canvas 的组件上的effectsprop。注意适用面effects不是给普通 DOM 层级的/p布局用的它面向渲染到 canvas 的源组件文档明确列出的接受者为Video来自remotion/mediaSolidCanvasImageHtmlInCanvas一个最直观的用法示例import {Video} from remotion/media; import {blur} from remotion/effects/blur; Video srchttps://remotion.media/video.mp4 effects{[blur({radius: 8})]} /;渲染时开启 WebGL2这些效果依赖 WebGL2。在 Studio 交互预览之外正式渲染render时需要显式开启 WebGL否则效果可能无法工作import {Config} from remotion/cli/config; Config.setChromiumOpenGlRenderer(angle);从 packages/effects/package.json 的exports字段可以看到remotion/effects把每个效果暴露成了独立的子路径如remotion/effects/blur、remotion/effects/starburst同时根入口remotion/effects汇总导出。效果参数精确写法请查阅对应效果的文档或仓库中 packages/effects/src 内同名源码。内置效果全览完整效果清单原文档共列出 50 余个开箱即用的效果函数按其名称与用途可粗略归为如下几类颜色校正类brightness()、contrast()、colorKey()、duotone()、grayscale()、hue()、invert()、saturation()、tint()、linearGradient()、linearGradientTint()、thermalVision()模糊类blur()、linearProgressiveBlur()、radialProgressiveBlur()、zoomBlur()光效与辉光类dropShadow()、glow()、lightTrail()、evolve()、venetianBlinds()、shine()、lightLeak()、starburst()几何/位移/畸变类mirror()、scale()、uvTranslate()、xyTranslate()、barrelDistortion()、chromaticAberration()、fisheye()、cornerPin()、wave()、vignette()材质与纹理类burlap()、emboss()、dotGrid()、halftone()、noise()、noiseDisplacement()、paper()、roughenEdges()、pattern()、pixelate()、pixelDissolve()、scanlines()、speckle()、shrinkwrap()、contourLines()、checkerboard()、halftoneLinearGradient()、gridlines()、whiteNoise()、tvSignalOff()、lines()、rings()、waves()、zigzag()。效果可以堆叠使用把它们全部放进同一个effects数组渲染管线会按数组顺序逐层处理这也是后续要讲的效果链的核心行为。引入路径规则大多数remotion/effects的效果走remotion/effects/效果slug子路径导入其中两个平移效果是特例uvTranslate()与xyTranslate()都从remotion/effects/translate导入见 packages/effects/package.json 中./translate子路径导出。直接使用示例import {brightness} from remotion/effects; Video srchttps://remotion.media/video.mp4 effects{[brightness({})]} /;效果链的底层工作方式源码级在深入自定义效果之前先理解框架如何执行effects数组能帮你写出更符合运行模型的效果实现。执行逻辑集中在 packages/core/src/effects/run-effect-chain.ts过滤 disabled 效果runEffectChain首先剔除params.disabled true的效果再按 backend 分组避免空跑或不必要的后端切换。Canvas 池 ping-pong每个效果链状态EffectChainState持有一个与输出同尺寸的CanvasPool见同目录canvas-pool.ts同一后端的效果在两张 scratch canvas 之间来回 ping-pong 绘制因此效果自身不需要每帧分配 canvas——这正是 packages/core/src/effects/effect-types.ts 注释中强调的契约。setup 缓存与回收setup()的结果按「效果定义 × target canvas」缓存在WeakMap中并通过cleanupRegistry在链结束时统一回调cleanup()释放资源。跨后端桥接效果按backend2d | webgl2 | webgpu分组为若干 run依次执行。2D → WebGL2 直接传递 canvas其他跨后端桥接使用createImageBitmap避免隐式 GPU readback 阻塞渲染帧率。Y 轴翻转契约apply收到flipSourceY标志——DOM 朝向的 canvas 源上传 WebGL 纹理时需要UNPACK_FLIP_Y_WEBGL而从 WebGL 桥接来的ImageBitmap已按上传朝向就绪不需要再翻转。类型契约定义在 packages/core/src/effects/effect-types.ts所有 canvas 存储premultiplied alpha且按sRGB 编码若效果在线性空间做色彩数学需自行完成 sRGB 往返转换。自定义效果何时用与怎么用选用原则原文档给出明确的决策边界当用户需要一个可复用、参数化、可在 Studio 中编辑、可与其他效果叠加的效果工厂时用createEffect()优先于HtmlInCanvas onPaint——onPaint适合一次性内联绘制而createEffect让变换具备「效果对象」的一切能力文件组织项目内临时效果放在组合旁例如src/effects/palette-map.ts打算进入remotion/effects仓库的效果则遵循仓库的add-effect技能agent 工作流约定而不是本文的快速写法。createEffect()的参数契约createEffect()接受一个EffectDefinition其配置项与原文档一致含义如下配置项类型/取值作用type字符串稳定的reverse-DNS标识符如com.example.paletteMap用于效果身份区分label字符串Studio 中显示的标签惯例写成调用形式如paletteMap()documentationLinkURL 或null指向效果文档没有则传nullbackend2d/webgl2/webgpu声明效果运行后端calculateKey(params)(params) string返回包含所有影响输出参数的稳定字符串用于效果实例的 memoization 比较setup(target)(canvas) S创建可复用的后端状态无状态则返回nullapply({source, target, width, height, params, state, flipSourceY})函数把变换后的结果绘制到target上每帧调用cleanup(state)(state) void释放setup()创建的 GPU/CPU 资源schemaInteractivitySchema定义 Studio 控件disabled字段由框架自动追加validateParams(params)函数参数缺失或非法时抛错在工厂调用时立即执行2D 自定义效果完整最小实现原文档给出了一个可直接运行的「半透明合成」效果示例将不透明度参数映射为ctx.filter输出完整继承如下import {createEffect, type InteractivitySchema} from remotion; type MyEffectParams { readonly amount?: number; }; const myEffectSchema { amount: { type: number, min: 0, max: 1, step: 0.01, default: 1, description: Amount, }, } as const satisfies InteractivitySchema; const resolve (params: MyEffectParams) ({ amount: params.amount ?? 1, }); export const myEffect createEffectMyEffectParams, null({ type: com.example.myEffect, label: myEffect(), documentationLink: null, backend: 2d, calculateKey: (params) { const {amount} resolve(params); return my-effect-${amount}; }, setup: () null, apply: ({source, target, width, height, params}) { const ctx target.getContext(2d); if (!ctx) { throw new Error(Could not get a 2D context for myEffect().); } const {amount} resolve(params); ctx.clearRect(0, 0, width, height); ctx.filter opacity(${amount * 100}%); ctx.drawImage(source, 0, 0, width, height); ctx.filter none; }, cleanup: () undefined, schema: myEffectSchema, validateParams: ({amount 1}) { if (typeof amount ! number || !Number.isFinite(amount) || amount 0 || amount 1) { throw new TypeError(amount must be a number between 0 and 1); } }, });要点拆解backend: 2d的适用场景简单的像素遍历、filter、drawImage或imageData类处理。当需要 shader 数学或 GPU 性能时才切换到 WebGL2resolve()帮助函数统一收敛可选参数与默认值同时被calculateKey、apply、validateParams复用避免默认值散落多处重置 2D 上下文可变状态本例在绘制后把filter复位为none这是必须养成的习惯globalAlpha、变换矩阵、合成模式等同理否则状态会泄漏到下一帧或后续效果。仓库中同风格的完整 2D 实例可对照 packages/example/src/EffectsTestbed/sample-posterize-2d.ts一个带levels/amount两个参数的 posterize 色调分离效果通过getImageData/putImageData逐像素量化。WebGL2 自定义效果RGB 通道分离当效果需要逐像素 shader 计算时应选择backend: webgl2。生命周期分工与原文档一致setup()获取 WebGL2 上下文编译/链接 shader创建全屏 quad 的 VAO/VBO 与纹理读取 uniform location全部存入 stateapply()上传source纹理设置 viewport 与 uniform绘制全屏三角形带cleanup()删除纹理、缓冲、program、VAO释放 GPU 资源。原文档的最小骨架示例RGB 通道偏移红色与蓝色通道沿水平方向错位import {createEffect, type InteractivitySchema} from remotion; type RgbShiftParams { readonly amount?: number; }; type RgbShiftState { readonly gl: WebGL2RenderingContext; readonly program: WebGLProgram; readonly vao: WebGLVertexArrayObject; readonly vbo: WebGLBuffer; readonly texture: WebGLTexture; readonly uSource: WebGLUniformLocation | null; readonly uOffset: WebGLUniformLocation | null; }; const rgbShiftSchema { amount: { type: number, min: 0, max: 80, step: 1, default: 12, description: Amount, }, } as const satisfies InteractivitySchema; export const rgbShift createEffectRgbShiftParams, RgbShiftState({ type: com.example.rgbShift, label: rgbShift(), documentationLink: null, backend: webgl2, calculateKey: ({amount 12}) rgb-shift-${amount}, setup: (target) { const gl target.getContext(webgl2, { premultipliedAlpha: true, alpha: true, preserveDrawingBuffer: true, }); if (!gl) { throw new Error(Could not get a WebGL2 context for rgbShift().); } gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); // Compile/link shaders, create a fullscreen quad VAO/VBO, create a // CLAMP_TO_EDGE RGBA texture, and get uSource/uOffset uniform locations. return createRgbShiftState(gl); }, apply: ({source, width, height, params, state, flipSourceY}) { const amount params.amount ?? 12; const {gl} state; gl.viewport(0, 0, width, height); gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipSourceY); gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, state.texture); gl.texImage2D( gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source as TexImageSource, ); gl.bindFramebuffer(gl.FRAMEBUFFER, null); gl.useProgram(state.program); if (state.uSource) gl.uniform1i(state.uSource, 0); if (state.uOffset) gl.uniform2f(state.uOffset, amount / width, 0); gl.bindVertexArray(state.vao); gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); }, cleanup: ({gl, program, vao, vbo, texture}) { gl.deleteTexture(texture); gl.deleteBuffer(vbo); gl.deleteProgram(program); gl.deleteVertexArray(vao); }, schema: rgbShiftSchema, validateParams: ({amount 12}) { if (typeof amount ! number || !Number.isFinite(amount) || amount 0 || amount 80) { throw new TypeError(amount must be a number between 0 and 80); } }, });上面的createRgbShiftState是骨架占位。仓库提供了完整的、可直接运行的对应实现packages/example/src/EffectsTestbed/sample-rgb-shift-webgl.ts包含完整 GLSL#version 300 es顶点/片元着色器片元着色器分别以clamp(vUv ± uOffset)采样红色与蓝色通道再与绿色通道重组为vec4(red, base.g, blue, base.a)带错误日志的compileShader/createProgram辅助函数全屏三角形带两个 vec2 属性交错为 16 字节 stride的 VAO/VBO 初始化apply中显式clearColor(0,0,0,0)与clear(COLOR_BUFFER_BIT)以及绘制后的状态复位解绑 VAO/纹理、useProgram(null)cleanup按序释放 texture、buffer、program、vertex array。原文档同时建议需要2D 与 WebGL2 成对参照时阅读packages/example/src/EffectsTestbed/sample-posterize-2d.ts与packages/example/src/EffectsTestbed/sample-rgb-shift-webgl.ts。若要在 Studio 中体验全部效果可查看效果测试台 packages/example/src/EffectsTestbed/EffectsTestbed.tsx另有 packages/example/src/EffectsTestbed/palette-map.ts 与 packages/example/src/EffectsTestbed/PaletteMapEffect.tsx 这类更贴近真实调色盘映射的实现。把自定义效果放进 compositioncreateEffect()返回的工厂函数可以直接放进任何接受effects的组件。以下来自原文档的组合示例将自研效果作用于CanvasImageimport {CanvasImage, staticFile} from remotion; import {myEffect} from ./effects/my-effect; export const MyComp: React.FC () { return ( CanvasImage src{staticFile(image.png)} effects{[myEffect({amount: 0.8})]} / ); };框架如何包装自定义效果源码解读createEffect的实现在 packages/core/src/effects/create-effect.ts理解它能解释原文档中多条「使用规范」的由来disabled由框架注入框架级字段disabledEffectField会被自动并入每个效果的 schemaStudio 中呈现为时间线效果行的「眼睛」开关对应/api/save-effect-props持久化也会并入工厂的入参类型。因此不要在自定义 params 类型或 schema 里重复声明disabled——通过返回的工厂传入disabled?: boolean即可。calculateKey被包装源码用-disabled-${disabled}后缀包裹用户的calculateKey。这样在 Studio/代码中切换disabled也会使缓存 key 失效效果链能及时重算原文档也提到getEffectFieldsToShow会过滤该字段让开关成为唯一控件。工厂调用时立即校验返回的工厂在构造 descriptor 前先调用validateParams抛错。测试 packages/core/src/test/create-effect-validate-params.test.ts 验证了「必需参数缺失时调用工厂即抛TypeError传入合法值则不抛」packages/core/src/test/create-effect-disabled.test.ts 则验证disabled的注入行为。类型擦除以支持自由组合descriptor 把P/S擦除为unknown使不同效果的 descriptor 可以在同一个EffectsProp数组中自由编排。工厂的类型签名还是条件类型effect-types.ts 的EffectFactory当你的P含必填字段如TintParams.color时工厂强制要求传参全部可选时参数可省略。编写自定义效果的最终检查清单原文档在收尾处列出的一组硬性规范是让效果进入 Studio、时间线与渲染管线的关键逐条摘录并补充原因disabled只通过工厂注入不要把它写进自定义参数类型或 schema必填参数在工厂调用时用validateParams校验createEffect包装层保证它在返回 descriptor 之前执行缺失参数应当立即抛错而不是在渲染帧中静默失败默认值双写schema与resolve()帮助函数中都要包含默认值——schema 的默认值用于 Studio 控件初始态resolve()的默认值用于渲染时的参数归一复位 2D 上下文可变状态filter、globalAlpha、变换矩阵、合成compositing等绘制后必须复位否则会串染到下一帧或链上的后续效果除非效果刻意改变透明度否则保留 alpha所有链内 canvas 均以 premultiplied alpha 存储透明通道的破坏会直接影响与其他效果的合成结果。视觉验证与测试资源想让效果在浏览器中通过截图像素级比对验证仓库在 packages/effects/src/visual-test/effects-visual.test.ts 提供了基于浏览器Playwright Vitest的视觉回归用例截图输出于同目录__screenshots__单元层面对效果参数边界与工厂行为的校验可参考 packages/effects/src/test/effect-params.test.ts、scale.test.ts、translate.test.ts等。整体脉络是先用本文方法把效果以createEffect封装为独立模块在 packages/example/src/EffectsTestbed 这类测试台中挂到真实 composition 上目测与堆叠验证最后以视觉测试固化输出从而保证效果在 Studio 预览与正式渲染两种路径下表现一致。【免费下载链接】remotion Make videos programmatically with React项目地址: https://gitcode.com/GitHub_Trending/re/remotion创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻