mcpField
A
add-effect

add-effect

github.com/remotion-dev开发68.6k830 次安装今天更新
代码仓库

关于 add-effect

面向 `@remotion/effects` 贡献者的开发技能,说明如何实现新效果,并完成包注册、测试、文档、演示和预览素材等工作。

主要功能

  1. 01效果实现规范
  2. 02包入口注册步骤
  3. 03构建与测试命令
  4. 04文档和演示流程
  5. 05预览合成要求

适用场景

  1. 01为 `@remotion/effects` 添加 WebGL2 效果
  2. 02注册新效果的包导入入口
  3. 03制作效果文档和交互演示

安装

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

$npx skills add https://github.com/remotion-dev/remotion --skill add-effect

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

详细介绍

添加新的 @remotion/effects 效果

添加新的 @remotion/effects 效果时使用此技能。

1. 选择效果形态

  • 新效果优先使用 WebGL2 后端。仅当 WebGL 无法实现该效果时,才使用 2D。
  • 简单效果使用单个文件 packages/effects/src/<effect-name>.ts。
  • 如果效果需要多个着色器、运行时辅助函数或多个文件,则使用 packages/effects/src/<effect-name>/ 文件夹,并添加顶层重新导出文件。
  • 遵循此软件包中已有的命名方式:
    • 文件/子路径:kebab-case(chromatic-aberration)
    • 函数:camelCase(chromaticAberration)
    • 类型:PascalCase 参数(ChromaticAberrationParams)
    • 效果类型字符串:remotion/<kebab-case-name>

2. 实现效果

在效果文件中:

  • 从 remotion 导入 SequenceSchema 和 Internals。
  • 使用 const {createEffect, createWebGL2ContextError} = Internals;。
  • 使用 const 值定义默认值。
  • 使用 satisfies SequenceSchema 定义 schema;这些字段会显示在 Studio 可视化编辑中。
  • 导出参数类型。
  • 在 resolve() 辅助函数中处理默认值。
  • 使用以下文件中的辅助函数验证参数:
    • packages/effects/src/validate-effect-param.ts
    • packages/effects/src/color-utils.ts
  • 如果无法获取 WebGL2,则抛出 createWebGL2ContextError('<effect name> effect')。
  • 将 documentationLink 设为 https://www.remotion.dev/docs/effects/<slug>。
  • 在 calculateKey() 中包含每个已解析的参数。

WebGL2 效果请使用以下通用结构:

import type {SequenceSchema} from 'remotion';
import {Internals} from 'remotion';
import {assertOptionalFiniteNumber, validateUnitInterval} from './color-utils.js';
import {assertEffectParamsObject} from './validate-effect-param.js';

const {createEffect, createWebGL2ContextError} = Internals;

const DEFAULT_AMOUNT = 1 as const;

const myEffectSchema = {
	amount: {
		type: 'number',
		min: 0,
		max: 1,
		step: 0.01,
		default: DEFAULT_AMOUNT,
		description: 'Amount',
	},
} as const satisfies SequenceSchema;

export type MyEffectParams = {
	readonly amount?: number;
};

type MyEffectResolved = {
	amount: number;
};

const resolve = (p: MyEffectParams): MyEffectResolved => ({
	amount: p.amount ?? DEFAULT_AMOUNT,
});

const validateMyEffectParams = (params: MyEffectParams): void => {
	assertEffectParamsObject(params, 'My effect');
	assertOptionalFiniteNumber(params.amount, 'amount');
	validateUnitInterval(params.amount ?? DEFAULT_AMOUNT, 'amount');
};

type MyEffectState = {
	readonly gl: WebGL2RenderingContext;
	readonly program: WebGLProgram;
	readonly vao: WebGLVertexArrayObject;
	readonly vbo: WebGLBuffer;
	readonly texture: WebGLTexture;
	readonly uSource: WebGLUniformLocation | null;
	readonly uAmount: WebGLUniformLocation | null;
};

const VERTEX_SHADER = /* glsl */ `#version 300 es
in vec2 aPos;
in vec2 aUv;
out vec2 vUv;

void main() {
	vUv = aUv;
	gl_Position = vec4(aPos, 0.0, 1.0);
}
`;

const FRAGMENT_SHADER = /* glsl */ `#version 300 es
precision highp float;

in vec2 vUv;
out vec4 fragColor;

uniform sampler2D uSource;
uniform float uAmount;

void main() {
	vec4 color = texture(uSource, vUv);
	fragColor = vec4(color.rgb * uAmount, color.a);
}
`;

// Follow existing helpers in halftone.ts or a runtime file for shader
// compilation, program linking, fullscreen-quad setup, and texture setup.

export const myEffect = createEffect<MyEffectParams, MyEffectState>({
	type: 'remotion/my-effect',
	label: 'My Effect',
	documentationLink: 'https://www.remotion.dev/docs/effects/my-effect',
	backend: 'webgl2',
	calculateKey: (params) => {
		const r = resolve(params);
		return `my-effect-${r.amount}`;
	},
	setup: (target) => {
		const gl = target.getContext('webgl2', {
			premultipliedAlpha: true,
			alpha: true,
			preserveDrawingBuffer: true,
		});
		if (!gl) {
			throw createWebGL2ContextError('my effect effect');
		}

		gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);

		return createMyEffectState(gl, VERTEX_SHADER, FRAGMENT_SHADER);
	},
	apply: ({source, width, height, params, state, flipSourceY}) => {
		const r = resolve(params);

		state.gl.viewport(0, 0, width, height);
		state.gl.bindFramebuffer(state.gl.FRAMEBUFFER, null);
		state.gl.activeTexture(state.gl.TEXTURE0);
		state.gl.bindTexture(state.gl.TEXTURE_2D, state.texture);
		state.gl.pixelStorei(state.gl.UNPACK_FLIP_Y_WEBGL, flipSourceY);
		state.gl.texImage2D(
			state.gl.TEXTURE_2D,
			0,
			state.gl.RGBA,
			state.gl.RGBA,
			state.gl.UNSIGNED_BYTE,
			source as TexImageSource,
		);

		state.gl.useProgram(state.program);
		if (state.uSource) state.gl.uniform1i(state.uSource, 0);
		if (state.uAmount) state.gl.uniform1f(state.uAmount, r.amount);
		state.gl.bindVertexArray(state.vao);
		state.gl.drawArrays(state.gl.TRIANGLE_STRIP, 0, 4);
	},
	cleanup: ({gl, program, vao, vbo, texture}) => {
		gl.deleteTexture(texture);
		gl.deleteBuffer(vbo);
		gl.deleteProgram(program);
		gl.deleteVertexArray(vao);
	},
	schema: myEffectSchema,
	validateParams: validateMyEffectParams,
});

添加新的辅助函数前,请先查看已有的 WebGL2 效果,例如 halftone.ts、 blur/blur-runtime.ts、chromatic-aberration/chromatic-aberration-runtime.ts 和 wave/wave-runtime.ts。在上面的模板中, createMyEffectState() 代表这些文件中使用的着色器编译、程序链接、 全屏四边形、纹理和 uniform 位置设置。

3. 注册软件包入口点

更新:

  • packages/effects/bundle.ts — 添加新的 src/<effect-name>.ts 入口点。
  • packages/effects/package.json:
    • 添加 exports["./<effect-name>"]。
    • 添加 typesVersions 条目。

如果采用文件夹实现,请添加一个从该文件夹重新导出的顶层文件:

export {myEffect, type MyEffectParams} from './my-effect/index.js';

4. 构建并验证

运行现有测试和构建:

cd packages/effects
bun test src/test
bunx turbo make --filter="@remotion/effects"

5. 添加文档

创建 packages/docs/docs/effects/<effect-name>.mdx。

参照现有的效果页面:

  • Frontmatter:slug、title、sidebar_label、crumb: '@remotion/effects'。
  • 仅在运行 bun render-cards.ts 后添加 image:。
  • H1:# effectName()<AvailableFrom v="..." />。
  • 加入 _属于 [@remotion/effects](/docs/effects/api) 软件包。_。
  • 添加简短描述。
  • 添加 <EffectsDemo type="effects-<effect-name>" />。
  • 添加 title="MyComp.tsx" 的 twoslash 示例。
  • 使用各自的 ### 标题为每个选项编写文档,可选参数使用 ?。
  • 添加 disabled? 小节。
  • 添加“另请参阅”小节。

更新:

  • packages/docs/sidebars.ts — 按字母顺序添加 'effects/<effect-name>'。
  • packages/docs/docs/effects/table-of-contents.tsx — 在合适的类别中添加卡片。
  • 运行卡片生成器更新 packages/docs/src/data/articles.ts,不要手动修改。

文档措辞请使用 writing-docs 技能。

6. 添加交互式文档演示

创建 packages/docs/components/effects/effects-<effect-name>-preview.tsx。

使用与其他效果相同的预览源:

import {myEffect} from '@remotion/effects/my-effect';
import React from 'react';
import {CanvasImage} from 'remotion';
import {EFFECTS_PREVIEW_IMAGE_SRC} from './effects-preview-image';

export const EffectsMyEffectPreview: React.FC<{
	readonly amount: number;
}> = ({amount}) => {
	return (
		<CanvasImage
			src={EFFECTS_PREVIEW_IMAGE_SRC}
			width={1280}
			height={720}
			fit="cover"
			effects={[myEffect({amount})]}
		/>
	);
};

文档效果预览使用 fit="cover",确保共享预览图像填满 16:9 画布, 不留透明条带。

在 packages/docs/components/effects-demos/registry.ts 中注册演示:

  • 导入预览组件和实际效果 schema(或从 effect().definition.schema 读取)。
  • 添加 id: 'effects-<effect-name>' 的条目。
  • 仅当必填字段的 schema 默认值为 undefined 时,才添加 initialValues。

演示详情请使用 docs-demo 技能。

7. 添加并渲染目录预览合成

目录卡片必须来自 packages/docs 中真实的 Remotion 合成,而不是手写素材。 始终将预览素材渲染为 PNG 文件。

在 packages/docs/src/remotion/Root.tsx 的 effect-previews 文件夹下添加一个 Still:

<Still
	id="effects-my-effect-preview"
	component={EffectsMyEffectPreview}
	width={1280}
	height={720}
	defaultProps={{
		amount: 1,
	}}
/>

使用与预览组件的 CanvasImage 相同的 width 和 height。 如果预览组件使用共享的文档预览图片,请在 CanvasImage 上保留 fit="cover"。将 16:9 的预览组件渲染为不同的宽高比,可能导致生成的目录图片出现黑边。

然后从 packages/docs 运行:

bunx remotion still src/remotion/entry.ts effects-my-effect-preview static/img/effects-my-effect-preview.png --overwrite --image-format=png

提交以下两项:

  • packages/docs/src/remotion/Root.tsx 中的合成条目
  • packages/docs/static/img/ 中渲染出的图片

8. 生成文档卡片

运行:

cd packages/docs
bun render-cards.ts

提交生成的 packages/docs/static/generated/articles-docs-effects-<effect-name>.png 和新增的 image: frontmatter 行。

如果 render-cards.ts 顺带生成了其他缺失的卡片,除非这些文件属于当前改动,否则请删除它们。

9. 更新 Remotion 技能

确保面向 Agent 的 Remotion 技能与新效果保持同步。

仅当新效果改变了一般使用机制、导入约定、安装指南或自定义效果建议时,才更新 packages/skills/skills/remotion-markup/effects.md。不要在其中重复完整的效果列表;请以文档目录作为规范列表。

10. 格式化、构建和验证

运行:

cd packages/effects
bunx oxfmt src --write
cd ../..
bun run build
bun run formatting

如果改动涉及文档源文件,bun run formatting 会处理 packages/docs/src。如果只编辑 MDX,请不要对文档页面运行格式化工具。

提交之前,检查:

git diff --check
git status --short

常见问题

  • 不要忘记 package.json 中的 exports 和 typesVersions;诸如 @remotion/effects/my-effect 的子路径导入依赖它们。
  • 不要忘记 bundle.ts;否则不会构建 ESM 子路径。
  • 不要在 packages/docs/src/remotion 中留下临时渲染入口。
  • 不要为效果目录预览使用手写 SVG。
  • 除非效果有意改变透明度,否则请保留 alpha。
  • 进行像素运算时,请注意画布存储的是预乘 alpha。
  • WebGL 颜色运算通常需要先对采样得到的 RGB 取消预乘,再进行亮度或阈值计算,之后重新预乘输出 RGB。

评论

支持 Markdown