使用 Alpha 通道视频屏蔽 HTML 内容

Use Alpha-Channeled video to mask HTML content

我正在尝试在 After Effect 中实现当前可能的效果,该效果是关于剪辑蒙版(luma matte)一个视频和另一个视频,因此最终输出将是透明视频。但我正在尝试制作一个动态透明的 HTML 内容。

视频看起来像这样:https://www.youtube.com/watch?v=jfzcrO4694E

基本显示 HTML 内容并改变透明度。

这一定是关于 + 的,但是在谷歌搜索大约一个小时后,找不到任何可以描述和影响 black\white 视频的内​​容,这会改变 HTML 内容透明度。

有什么想法吗?

您的示例视频的白色区域逐渐被后退的黑色区域显露出来。

您可以使用现有视频 + canvas 来揭示基础 html 内容。

创建一个包含您已经制作的视频的视频元素。

使用计时器抓取正在播放的视频的帧。强烈建议 requestAnimationFrame,因为它会与您的显示刷新同步。

在每个动画循环中,在 canvas 上绘制视频帧。您可以在 canvas 上绘制视频,因为 canvas 因为 canvas 可以使用视频元素作为其图像源。

一旦你的黑雾框架在 canvas 上,你可以通过以下方式使 canvas 的任何白色区域透明:

  • 使用context.getImageData

  • 获取canvas上每个像素的颜色数据
  • 如果任何像素的颜色是 "white-ish",您可以将该像素的 alpha 更改为 0(表示透明)。

  • context.putImageData

  • 将修改后的像素数据放回canvas

总的来说:

  • 隐藏您正在播放的视频元素

  • 使用 CSS 将您的 canvas 元素定位在您希望显示的 html 内容上。

  • 创建一个动画循环,在 canvas 上绘制视频帧并将白色像素转换为透明像素。

  • html 内容将通过视频的 canvas 播放帧显示,其中白色像素被完全透明化。

HTML5 不支持 alpha 通道视频(例如 MOV 32 位等)。

唯一的选择是像您的视频一样使用遮罩合成并结合 canvas 元素。但是,canvas 只能合成已经存在的 alpha 通道,并且由于遮罩是实心层而不是 alpha,因此您必须将其转换为 alpha。

这意味着您必须绘制每个帧并将其迭代为 canvas,并将灰度级转换为透明度级别。但是,这非常 CPU 密集并且会影响计算机 performance/resources。视频还必须满足 CORS 要求,以免污染 canvas(在这种情况下您无法提取像素)。

方法如下

对于每一帧:

  • 从任何通道为每个像素抓取一个级别,我们将在此处使用红色
  • 从 255 中减去该值,因为这里的哑光白色将是目标(反转,将显示)
  • 清除图像数据,这是遮罩本身,否则您会得到白色遮罩泄漏到数据中
  • 将计算值设置为新的 alpha 通道值

示例循环(假设视频是动态创建的,设置了源并触发了开始事件 - canvas 放置在您要屏蔽的位置上下文 ctx 已初始化):

ctx.globalCompositeOperation = "copy";       // will clear previous alpha

function unmask() {
    ctx.drawImage(video, 0, 0);              // draw current video frame

    var idata = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height),
        data = idata.data,
        len = data.length, i = 0;            // ..image data from canvas

    while(i < len) {
        var alpha = 255 - data[i];           // in this matte, white = fully transparent
        data[i] = data[i+1] = data[i+2] = 0; // clear matte to black
        data[i+3] = alpha;                   // set alpha
        i += 4;                              // next pixel
    }

    ctx.putImageData(idata, 0, 0);           // update canvas
    requestAnimationFrame(unmask);           // todo: add criteria to stop this
}

加速提示:使用黑色(或预反转遮罩视频),然后在数据上使用 Uint32Array 视图,右移一个像素长字 (<<24) 回到原位。


您有可能使用新的 CSS custom filters (aka CSS shaders), but this is not widely supported at the time of this writing. It will also require the use of shader language (GLSL) 执行此操作。但我发现至少值得一提。

到目前为止,其他答案都假设了 2D canvas 上下文。您可以使用 WebGL 更有效地做到这一点。这样,所有实际工作都在 GPU 中完成。

您需要一个片段着色器,它基本上可以完成 Ken Fyrstenberg 的回答中循环所做的事情:找到每个像素的亮度,从最大值(1,在 GLSL 中,而不是 255)中减去它,然后将其用作阿尔法值。我将跟随他的脚步 (a) 仅使用绿色通道作为亮度,以及 (b) 将输出颜色通道设置为黑色。以下是我的做法:

precision lowp float;
uniform sampler2D sampler0;
varying vec2 v_texCoord;
void main(void) {
  vec4 color = texture2D(sampler0, v_texCoord);
  float alpha = 1.0 - color.g;
  gl_FragColor = vec4(0.0, 0.0, 0.0, alpha);
}

然后,对于每一帧,您将一个矩形渲染到 WebGL canvas,将您的视频作为纹理源,并使用上面的片段着色器。 我已经开始编写一个示例来说明如何执行此操作,但这需要一些工作。有多少利息?让我知道,如果有足够的需求,我会尽力完成它。

编辑:通过使用 glfx.js,您或许可以避免学习太多关于 WebGL 的知识。我没有深入研究它,但我认为它可以使用视频作为图像源。