7、视频滤镜与特效:LUT滤镜、分屏特效、时间特效(慢动作/快进)、转场动画

各位同学,欢迎来到第七章。这一章我们聊点好玩的——滤镜和特效。

说实话,做视频处理最让人兴奋的就是这部分。你想想看,一段平平无奇的视频素材,加上合适的滤镜、配上酷炫的转场,瞬间就有了电影感。我在做短视频SDK的时候,经常被产品经理追着要「那个抖音同款滤镜」,嗯,其实就是LUT。

7.1 LUT滤镜:让色彩一键变高级

LUT的全称是Look-Up Table,查色表。说白了,就是把输入的颜色值映射到另一个颜色值。你给一个像素的RGB,LUT告诉你输出什么RGB。

我习惯用3D LUT,也就是64x64x64的立方体。为什么是64?因为精度够用,文件又不会太大。每个维度64个采样点,总共262144个颜色映射关系。够精细了。

核心原理:LUT本质上是一个三维数组。输入(R,G,B),查表得到输出(R',G',B')。如果输入值不在采样点上,就用三线性插值。

来看一个加载LUT的代码片段。这是我在项目中实际用过的方案:

// 加载.cube格式的LUT文件
public class LUTLoader {
    public static float[][][] loadCubeLUT(String path) {
        // 解析.cube文件头,获取size
        // 读取RGB映射数据
        // 构建3D LUT数组
        float[][][] lut = new float[size][size][size * 3];
        // 注意:cube文件的数据排列是BGR顺序
        // 我曾经在这里踩过坑,颜色完全不对
        return lut;
    }
}

我的经验:LUT文件格式有很多种,.cube、.3dl、.png。我个人最推荐.cube,因为它纯文本,容易调试。有一次客户给的LUT颜色偏紫,我打开.cube文件一看,发现蓝色通道的映射曲线有问题。

在OpenGL ES里应用LUT,核心是片段着色器。你需要把纹理坐标映射到LUT的3D纹理坐标上:

// 片段着色器核心代码
uniform sampler2D inputTexture;
uniform sampler3D lutTexture;

void main() {
    vec4 color = texture2D(inputTexture, vTexCoord);
    // 把颜色映射到LUT空间
    vec3 lutCoord = color.rgb * (63.0/64.0) + (0.5/64.0);
    vec4 lutColor = texture3D(lutTexture, lutCoord);
    gl_FragColor = lutColor;
}

为什么是63/64和0.5/64?因为LUT的采样点范围是0到63,我们需要把[0,1]的颜色值映射到采样点的中心位置。这个细节,很多教程都不讲。

7.2 分屏特效:一个画面,多种视角

分屏特效在短视频里很常见,比如左右对比、上下对比、九宫格。实现思路其实很简单——把输出画面分成几个区域,每个区域独立渲染。

我做过一个双屏对比功能,左边是原图,右边是滤镜效果。用户滑动中间的分割线,可以实时调整左右比例。嗯,这里要注意滑动时的平滑过渡。

分屏的核心是修改顶点坐标和纹理坐标的映射关系。比如左右分屏:

// 左半屏:纹理坐标不变,顶点坐标x范围[-1, 0]
// 右半屏:纹理坐标不变,顶点坐标x范围[0, 1]

// 更灵活的做法:在片段着色器里判断
void main() {
    if (vTexCoord.x < 0.5) {
        // 左半屏:显示原图
        gl_FragColor = texture2D(originalTexture, vTexCoord * 2.0);
    } else {
        // 右半屏:显示滤镜效果
        vec2 coord = vec2((vTexCoord.x - 0.5) * 2.0, vTexCoord.y);
        gl_FragColor = applyLUT(texture2D(originalTexture, coord));
    }
}

注意:分屏时纹理坐标要重新计算。如果你直接使用原始坐标,画面会被拉伸。我曾经犯过这个错,结果左右两边的画面都变形了。

分屏特效的变种很多:

  • 画中画:小窗口叠加在大画面上,可以拖动位置
  • 镜像分屏:左右对称,或者上下对称
  • 不规则分屏:圆形、心形裁剪区域

7.3 时间特效:让时间变慢或变快

时间特效,说白了就是改变视频的播放速度。慢动作是放慢,快进是加快。但实现起来,远不止「跳帧」这么简单。

我刚开始做慢动作时,直接跳帧播放,结果画面卡顿得不行。后来才明白,慢动作需要做帧插值。

7.3.1 慢动作(慢放)

慢动作的核心是:在原始帧之间插入新的帧。比如原始帧率30fps,你想放慢到10fps,就需要在每两帧之间插入2个新帧。

最简单的插值方法是帧重复:直接复制前一帧。但这样画面会跳,不流畅。更好的方法是线性插值:

// 假设原始帧A和B,时间点t在[0,1]之间
// 插值帧 = A * (1 - t) + B * t
// 对于像素级别的插值,需要在GPU上做

// 更高级的做法:光流法插值
// 计算A到B的运动向量,然后沿着运动方向插值
// 效果最好,但计算量大

我的建议:移动端做慢动作,用线性插值就够了。光流法虽然效果好,但手机发热严重。我曾经在某个项目里试过,手机烫得能煎鸡蛋。

7.3.2 快进(快放)

快进相对简单,就是跳帧播放。比如你想快进2倍,就每隔一帧取一帧。但要注意音频同步问题。

音频也要跟着变速。Android里可以用MediaCodec的变速参数,或者用SoundPool的速率控制。我习惯的做法是:视频跳帧,音频用重采样。

// 快进2倍:视频帧索引跳2
int targetFrameIndex = (int)(currentTime * frameRate * speed);
// speed = 2.0 表示2倍速

// 音频重采样:用AudioTrack的setPlaybackRate
// 或者用MediaCodec的变速参数

7.4 转场动画:让切换更丝滑

转场动画是视频剪辑的「调味料」。两个镜头之间,直接切换太生硬,加个转场就自然多了。

常见的转场类型:

  • 淡入淡出:前一个画面逐渐消失,后一个逐渐出现
  • 滑动:前一个画面滑出,后一个滑入
  • 缩放:前一个缩小消失,后一个放大出现
  • 擦除:像擦黑板一样,从左到右擦除前一个画面

转场的核心是混合两个画面。在片段着色器里,根据时间t(0到1),计算两个画面的混合比例:

// 淡入淡出转场
uniform sampler2D frameA;
uniform sampler2D frameB;
uniform float progress; // 0.0 到 1.0

void main() {
    vec4 colorA = texture2D(frameA, vTexCoord);
    vec4 colorB = texture2D(frameB, vTexCoord);
    gl_FragColor = mix(colorA, colorB, progress);
    // progress=0时全显示A,progress=1时全显示B
}

滑动转场稍微复杂一点,需要根据progress修改纹理坐标:

// 从左到右滑动
void main() {
    vec2 coordA = vTexCoord;
    vec2 coordB = vTexCoord;
    
    // A向左滑出
    coordA.x = vTexCoord.x + progress;
    // B从右侧滑入
    coordB.x = vTexCoord.x - (1.0 - progress);
    
    vec4 colorA = texture2D(frameA, coordA);
    vec4 colorB = texture2D(frameB, coordB);
    
    // 根据坐标是否在[0,1]范围内决定显示哪个
    if (coordA.x < 0.0 || coordA.x > 1.0) {
        gl_FragColor = colorB;
    } else {
        gl_FragColor = colorA;
    }
}

避坑指南:转场动画的progress要平滑变化。我曾经用线性变化,结果转场看起来很机械。后来改用缓动函数(ease-in-out),效果就好多了。推荐用三次贝塞尔曲线控制progress的变化速度。

转场动画的变种非常多,你可以组合使用:

  • 淡入 + 缩放
  • 滑动 + 模糊
  • 擦除 + 颜色渐变

嗯,这一章的内容就到这里。滤镜、分屏、时间特效、转场动画,这四个技能组合起来,你已经能做出很酷的视频效果了。记住,实践出真知,多写代码多调试,你会发现这些特效其实没那么神秘。

视频滤镜与特效 · 知识体系 🎨 LUT滤镜 • 3D LUT查色表 • .cube文件解析 • 三线性插值 • 片段着色器实现 🖥 分屏特效 • 左右/上下分屏 • 画中画叠加 • 纹理坐标重映射 • 不规则裁剪区域 ⏱ 时间特效 • 慢动作帧插值 • 快进跳帧播放 • 线性/光流插值 • 音频同步处理 🔄 转场动画 • 淡入淡出混合 • 滑动/缩放转场 • 擦除效果 • 缓动函数控制 核心技术基础 OpenGL ES 片段着色器 · 纹理映射 · 混合模式 · 时间轴控制 实现流程 输入视频帧 特效处理 混合输出 渲染显示
公众号:蓝海资料掘金营,微信deep3321