AEJoy —— 开发第一个 After Effects 插

「这是我参与11月更文挑战的第 26 天,活动详情查看:2021最后一次更文挑战」。

参加该活动的第 43 篇文章

目标

开发我的第一个 After Effects 插件 —— 变色效果。

Plugin.gif

环境

Adobe CC2019 v16.0.0

Windows10 with environment for C++ compile

Microsoft Visual C++ 2017 ver15.8.4

具体步骤

环境搭建

下载 After Effects SDK

访问 Adobe 网站,单击 “获取 SDKs” 按钮获取 After Effects SDK。选择 AE 版本的 After Effects Plug-in SDK

将创建路径设置为环境变量

添加环境变量 “AE_PLUGIN_BUILD_DIR” 并设置输出目录。如果它不存在,则会报错误 “fatal error LNK1104: cannot open file ***.aex”

创建样例插件,测试环境配置成功与否

解压 “After+Effects+CC+16.0+Win+SDK.zip”,并将解压后的目录“AfterEffectsSDK”放到合适的位置。

打开 AfterEffectsSDK\Examples\BuildAll 例子。sln 将启动 Visual Studio 。

右键单击 “SDK Noise” 并构建它。

image.png

如果出现错误 “C2220: warning treated as error - no 'object' file generated”,修改项目配置——不将警告视为错误

image.png

放置插件到 AE 目录

把 “SDK_Noise.aex“ 文件放到 “C:\Program Files\Adobe\Adobe After Effects CC 2019\Support Files“ 目录。

添加插件效果

打开 AE 项目,点击 Effect>Sample Plug-ins>SDK_Noise

image.png

一个噪声效果被添加到视频中。

image.png

拷贝模板

从 “AfterEffectsSDK\Examples\template\Skeleton” 复制骨架模板,保留“Skeleton”、“Headers”、“Resources” 和 “Util” 的目录层次结构。

重命名文件名,将 “Skeleton” 替换为 “Color Change” 。不要重命名SkeletonPiPL.rc”。因为二进制的 .rc 文件是自动从 .r 文件生成的(AE插件 SDK 指南 “PiPL资源” )。

EffectMain 函数

SkeletonPiPL.r 中定义了 Main 函数,如下。

1
2
3
4
5
6
7
8
9
cpp复制代码#ifdef AE_OS_WIN
#ifdef AE_PROC_INTELx64
CodeWin64X86 {"EffectMain"},
#endif
#else
#ifdef AE_OS_MAC
CodeMacIntel64 {"EffectMain"},
#endif
#endif

EffectMain 是接受 PF_Cmd cmd 作为一个参数的函数,并使用(如PF_Cmd_ABOUTPF_Cmd_PARAMS_SETUPPF_Cmd_RENDER 的) cmd 作为一个选择器来调用该函数。参考 “AE 插件 SDK 指南命令选择器”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
cpp复制代码PF_Err EffectMain(
PF_Cmd cmd,
PF_InData *in_data,
PF_OutData *out_data,
PF_ParamDef *params[],
PF_LayerDef *output,
void *extra)
{
PF_Err err = PF_Err_NONE;
try {
switch (cmd) {
case PF_Cmd_ABOUT:
err = About(in_data,
out_data,
params,
output);
break;
case PF_Cmd_GLOBAL_SETUP:
err = GlobalSetup(in_data,
out_data,
params,
output);
break;
case PF_Cmd_PARAMS_SETUP:
err = ParamsSetup(in_data,
out_data,
params,
output);
break;
case PF_Cmd_RENDER:
err = Render(in_data,
out_data,
params,
output);
break;
}
}
catch(PF_Err &thrown_err){
err = thrown_err;
}
return err;
}

About 函数

About 函数用于显示描述插件的对话框。在 ColorChange_Stgrings.cpp 中修改 TableString 如下。

1
2
3
4
5
6
7
cpp复制代码TableString g_strs[StrID_NUMTYPES] = {
StrID_NONE, "",
StrID_Name, "ColorChange",
StrID_Description, "Change comp color to specified color",
StrID_Gain_Param_Name, "Gain",
StrID_Color_Param_Name, "Color",
};

image.png

打开 About 对话框,就会显示如下信息

image.png

参数设置

ParamsSetup 是设置 UI、描述参数和注册它们的函数。

我将 SKELETON 重命名为 COLORCHANGE ,删除 GAIN 参数,添加 LEVEL 参数。然后在 ColorChange.h 中重置值如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cpp复制代码/* Parameter defaults */

#define COLORCHANGE_LEVEL_MIN 0
#define COLORCHANGE_LEVEL_MAX 100
#define COLORCHANGE_LEVEL_DFLT 50
enum {
COLORCHANGE_INPUT = 0,
COLOECHANGE_LEVEL,
COLORCHANGE_COLOR,
COLORCHANGE_NUM_PARAMS
};
enum {
LEVEL_DISK_ID = 1,
COLOR_DISK_ID,
};

对应地,在 ColorChange.cpp 中也重命名常量名称。

渲染

渲染函数是根据输入和参数将效果渲染到输出的函数。

1. GainInfo

GainInfo 是处理参数 GAIN 的结构体。在 ColorChange.h 中创建用于传递参数数据 levelcolor 的新结构体。

1
2
3
4
5
cpp复制代码typedef struct ParamInfo {
PF_FpLong level;
PF_Pixel color;
PF_Pixel16 color16;
} PramInfo, *PramInfoP, **PramInfoH;
1
2
3
4
5
6
7
8
9
cpp复制代码//GainInfo          giP;
//AEFX_CLR_STRUCT(giP);
ParamInfo paramDataP;
AEFX_CLR_STRUCT(paramDataP);
A_long linesL = 0;

linesL = output->extent_hint.bottom - output->extent_hint.top;
paramDataP.level = params[COLOECHANGE_LEVEL]->u.fs_d.value;
paramDataP.color = params[COLORCHANGE_COLOR]->u.cd.value;

2. iterate

迭代函数扫描输入帧并且计算输出帧作为像素对像素的操作,即像素函数。在本例中,像素函数是 “MySimpleGainFunc16” 或 “MySimpleGainFunc8” 。重命名并将它们改为 “MyColorChangeFunc16” 和 “MyColorChangeFunc8” 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
cpp复制代码if (PF_WORLD_IS_DEEP(output))
{
paramDataP.color16.red = CONVERT8TO16(paramDataP.color.red);
paramDataP.color16.green = CONVERT8TO16(paramDataP.color.green);
paramDataP.color16.blue = CONVERT8TO16(paramDataP.color.blue);
paramDataP.color16.alpha = CONVERT8TO16(paramDataP.color.alpha);
ERR(suites.Iterate16Suite1()->iterate(
in_data,
0, // progress base
linesL, // progress final
&params[COLORCHANGE_INPUT]->u.ld, // src
NULL, // area - null for all pixels
(void *)&paramDataP, // refcon - your custom data pointer
MyColorChangeFunc16, // pixel function pointer
output));
}
else
{
ERR(suites.Iterate8Suite1()->iterate(
in_data,
0, // progress base
linesL, // progress final
&params[COLORCHANGE_INPUT]->u.ld, // src
NULL, // area - null for all pixels
(void *)&paramDataP, // refcon - your custom data pointer
MyColorChangeFunc8, // pixel function pointer
output));
}

3. MyColorChangeFunc

我将像素函数更改如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cpp复制代码static PF_Err MyColorChangeFunc8(
void *refcon,
A_long xL,
A_long yL,
PF_Pixel8 *inP,
PF_Pixel8 *outP)
{
PF_Err err = PF_Err_NONE;
ParamInfo *paramDataP = reinterpret_cast<ParamInfo *>(refcon);
PF_FpLong levelF = 0;
float red_diff, green_diff, blue_diff;

if (paramDataP)
{
levelF = paramDataP->level / 100.0;
red_diff = (paramDataP->color.red - inP->red) * levelF;
green_diff = (paramDataP->color.green - inP->green) * levelF;
blue_diff = (paramDataP->color.blue - inP->blue) * levelF;

outP->alpha = inP->alpha;
outP->red = MIN(inP->red + red_diff, PF_MAX_CHAN8);
outP->green = MIN(inP->green + green_diff, PF_MAX_CHAN8);
outP->blue = MIN(inP->blue + blue_diff, PF_MAX_CHAN8);
}
return err;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
cpp复制代码static PF_Err MyColorChangeFunc16(
void *refcon,
A_long xL,
A_long yL,
PF_Pixel16 *inP,
PF_Pixel16 *outP)
{
PF_Err err = PF_Err_NONE;
ParamInfo *paramDataP = reinterpret_cast<ParamInfo *>(refcon);
PF_FpLong levelF = 0;
float red_diff, green_diff, blue_diff;

if (paramDataP)
{
levelF = paramDataP->level / 100.0;
red_diff = (paramDataP->color16.red - inP->red) * levelF;
green_diff = (paramDataP->color16.green - inP->green) * levelF;
blue_diff = (paramDataP->color16.blue - inP->blue) * levelF;

outP->alpha = inP->alpha;
outP->red = MIN(inP->red + red_diff, PF_MAX_CHAN16);
outP->green = MIN(inP->green + green_diff, PF_MAX_CHAN16);
outP->blue = MIN(inP->blue + blue_diff, PF_MAX_CHAN16);
}
return err;
}

构建并安装

构建解决方案,并将生成的 .aex 文件放入 Adobe AE 目录,如 “C:\Program Files\Adobe\Adobe After Effects CC 2019\Support Files”

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%