视频混剪-转场效果是怎么实现的

视频混剪-转场效果是怎么实现的

BaseCut 技术博客第四篇。这篇聊转场------两个画面之间的淡入淡出、滑动、擦除是怎么做的。


什么是转场

最简单的解释:让画面 A 逐渐变成画面 B

没有转场的话,两个片段之间是硬切:

复制代码
时间轴:

片段 A    |    片段 B
oooooooooo|oooooooooo
          ↑
      瞬间切换

有转场的话:

复制代码
片段 A      转场区间      片段 B
ooooooooo[A+B 混合区]ooooooooo
         ↑───────────↑
       起点         终点

转场的历史:电影中的视觉语言

转场不是瞎搞,它是电影百年历史沉淀下来的视觉语言

硬切(Hard Cut)

镜头直接切换,没有任何过渡。

用途:

  • 同一场景的不同角度
  • 紧张、快节奏的剪辑

淡入淡出(Fade In/Out)

画面逐渐变亮或变暗,通常是黑场。

用途:

  • 场景开始/结束
  • 表示时间流逝

叠化(Dissolve)

两个画面叠加在一起,A 逐渐消失,B 逐渐出现。

用途:

  • 平行时空
  • 回忆、梦境
  • 时间流逝

擦除(Wipe)

一条线从一侧推到另一侧,后面露出新画面。

用途:

  • 《星球大战》经典转场
  • 复古风格

转场的数学本质

核心公式:线性插值

不管多复杂的转场,核心都是一个公式:

复制代码
output = A × (1 - progress) + B × progress

其中 progress 从 0 变到 1:

复制代码
progress = 0.0  →  100% A,0% B
progress = 0.5  →  50% A,50% B
progress = 1.0  →  0% A,100% B

这个公式叫 Linear Interpolation ,简称 Lerp,是图形学最常用的公式之一。

不同转场类型

虽然公式一样,但 A 和 B 的"混合方式"可以有很多变化:

类型 混合方式
淡入淡出 整个画面透明度渐变
滑动 B 从画面外推入,A 被推走
擦除 一条线逐渐划过,后面露出 B
缩放 A 缩小消失,B 放大出现

缓动函数(Easing)

如果 progress 匀速从 0 增长到 1,动画会很"机械",像机器人一样。

真实世界的物体都有惯性:

  • 汽车启动:先慢后快(Ease-In)
  • 门关闭:先快后慢(Ease-Out)
  • 弹簧:快-慢-回弹

常用缓动函数

线性(Linear)

javascript 复制代码
function linear(t) {
  return t
}

没有加速度,匀速变化。

缓入(Ease-In)

javascript 复制代码
function easeIn(t) {
  return t * t
}

开始慢,结束快。

缓出(Ease-Out)

javascript 复制代码
function easeOut(t) {
  return 1 - (1 - t) * (1 - t)
}

开始快,结束慢。

缓入缓出(Ease-In-Out)

javascript 复制代码
function easeInOut(t) {
  return t < 0.5
    ? 2 * t * t
    : 1 - Math.pow(-2 * t + 2, 2) / 2
}

两头慢,中间快。最常用。

对比效果

复制代码
时间:  0    0.25   0.5   0.75   1
线性:  0    0.25   0.5   0.75   1.0
缓动:  0    0.12   0.5   0.88   1.0

缓动让动画有"弹性",更自然。


WebGL 实现

双纹理绑定

转场需要同时访问两帧画面,所以要绑定两个纹理:

javascript 复制代码
// 绑定 A 帧到纹理槽位 0
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, textureA)
gl.uniform1i(uTexALocation, 0)

// 绑定 B 帧到纹理槽位 1
gl.activeTexture(gl.TEXTURE1)
gl.bindTexture(gl.TEXTURE_2D, textureB)
gl.uniform1i(uTexBLocation, 1)

// 传入进度
gl.uniform1f(uProgressLocation, progress)

// 渲染
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)

淡入淡出着色器

glsl 复制代码
uniform sampler2D u_texA;
uniform sampler2D u_texB;
uniform float u_progress;

void main() {
  vec4 colorA = texture2D(u_texA, v_texCoord);
  vec4 colorB = texture2D(u_texB, v_texCoord);
  
  // GLSL 内置的 mix 函数就是 Lerp
  gl_FragColor = mix(colorA, colorB, u_progress);
}

滑动着色器

glsl 复制代码
void main() {
  // A 向左移动
  vec2 uvA = v_texCoord + vec2(u_progress, 0.0);
  // B 从右边进来
  vec2 uvB = v_texCoord - vec2(1.0 - u_progress, 0.0);
  
  vec4 colorA = texture2D(u_texA, uvA);
  vec4 colorB = texture2D(u_texB, uvB);
  
  // 根据 UV 坐标判断显示哪个
  if (v_texCoord.x > 1.0 - u_progress) {
    gl_FragColor = colorB;
  } else {
    gl_FragColor = colorA;
  }
}

擦除着色器

glsl 复制代码
void main() {
  vec4 colorA = texture2D(u_texA, v_texCoord);
  vec4 colorB = texture2D(u_texB, v_texCoord);
  
  // 一条竖线从左向右扫过
  if (v_texCoord.x < u_progress) {
    gl_FragColor = colorB;
  } else {
    gl_FragColor = colorA;
  }
}

转场时机判断

播放器在渲染每一帧时,需要判断当前是否处于转场区间:

typescript 复制代码
function render(currentTime: number) {
  // 查找当前时间是否有转场
  const transition = findActiveTransition(currentTime)
  
  if (transition) {
    // 计算进度 (0-1)
    const rawProgress = (currentTime - transition.startTime) / transition.duration
    
    // 应用缓动函数
    const progress = easeInOut(rawProgress)
    
    // 获取两帧画面
    const frameA = getFrame(transition.clipA, currentTime)
    const frameB = getFrame(transition.clipB, currentTime)
    
    // 渲染转场
    webgl.renderTransition(frameA, frameB, progress, transition.type)
  } else {
    // 正常渲染单帧
    const frame = getCurrentFrame(currentTime)
    webgl.renderFrame(frame)
  }
}

下一篇

讲视频导出------怎么把编辑好的内容变成 MP4 文件,以及 WebCodecs 的原理。


系列目录

  1. 技术选型与项目结构
  2. 时间轴数据模型
  3. WebGL 渲染与滤镜
  4. 转场动画实现(本文)
  5. WebCodecs 视频导出
  6. LeaferJS 贴纸系统
相关推荐
三十_A1 天前
前端技术分享:基于 Canvas 实现视频帧截取与下载方案
前端·音视频
地狱为王1 天前
Unity使用NovaSR将沉闷的16kHz音频升频成清晰的48kHz音频
unity·游戏引擎·音视频·novasr
Dreams°1231 天前
进阶实战:Wan2.2-T2V-A5B 实现可点击跳转的互动式教育视频
算法·microsoft·ai·音视频
Coovally AI模型快速验证1 天前
YOLO26技术详解:原生NMS-Free架构设计与实现原理
人工智能·计算机视觉·开源·音视频·无人机
郭涤生1 天前
高斯滤波从入门到精通
linux·音视频
想你依然心痛2 天前
AI 音效新征程:HunyuanVideo-Foley 视频配音实战
人工智能·音视频·智能电视
子夜江寒2 天前
OpenCV 学习:文档扫描与视频运动检测与跟踪
opencv·学习·计算机视觉·音视频
wgfhill2 天前
面向自媒体工作者的视频搬运内容去重技术方案:智能抽帧降帧处理工具解析
新媒体运营·音视频·媒体·视频
智能相对论2 天前
Hilight开启公测:营销视频的“DeepSeek时刻”,来了
大数据·人工智能·音视频
hhcgchpspk2 天前
python实现音频淡入淡出功能
python·程序人生·音视频·pygame