NDK OpenGL ES 3.0 开发(十九):相机抖音滤镜

简介: 文主要介绍抖音短视频 App 里面一些常见滤镜的实现,这里只做抛砖引玉,玩滤镜主要靠想象力去实现一些酷炫的效果。

作者:字节流动

来源:https://blog.csdn.net/Kennethdroid/article/details/103449935


OpenGLES 相机抖音滤镜

最近几篇文章主要是利用 OpenGL 实现相机预览的一些常见的滤镜,上一篇主要介绍了 LUT 滤镜的原理及简单实现方法,而本文主要介绍抖音短视频 App 里面一些常见滤镜的实现,这里只做抛砖引玉,玩滤镜主要靠想象力去实现一些酷炫的效果。

分色偏移


分色偏移滤镜原理:基于原纹理坐标进行偏移,分别采样后再按照 RGBA 通道进行合成,组成一个新的颜色。

//分色偏移
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
uniform float u_offset;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
void main()
{
    vec4 originColor = YuvToRgb(v_texcoord);
    //右下方偏移
    vec4 offsetColor0 = YuvToRgb(vec2(v_texcoord.x + u_offset, v_texcoord.y + u_offset));
    //左上方偏移
    vec4 offsetColor1 = YuvToRgb(vec2(v_texcoord.x - u_offset, v_texcoord.y - u_offset));
    //混合成一个颜色输出
    gl_FragColor = vec4(originColor.r, offsetColor1.g, offsetColor0.b, originColor.a);
}

灵魂出窍


灵魂出窍滤镜的原理:根据偏移量 offset,进行 scale 变换纹理坐标,分别进行采样后,再按照混合系数进行加权混合。

//灵魂出窍
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
uniform float u_offset;
uniform vec2 texSize;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
const float MAX_ALPHA = 0.5;
const float MAX_SCALE = 0.8;
void main()
{
    //根据偏移量计算混合系数 alpha
    float alpha = MAX_ALPHA * (1.0 - u_offset);
    //根据偏移量计算混合系数 scale
    float scale = 1.0 + u_offset * MAX_SCALE;
    //缩放操作
    float scale_x = 0.5 + (v_texcoord.x - 0.5) / scale;
    float scale_y = 0.5 + (v_texcoord.y - 0.5) / scale;
    vec2 scaleCoord = vec2(scale_x, scale_y);
    vec4 maskColor = YuvToRgb(scaleCoord);
    vec4 originColor = YuvToRgb(v_texcoord);
    //加权混合
    gl_FragColor = originColor * (1.0 - alpha) + maskColor * alpha;
}

旋转的圆


旋转的圆:对某一半径内的所有像素,按照偏移量转换成的角度进行旋转,半径之外的像素正常渲染。

//旋转的圆
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
uniform float u_offset;
uniform vec2 texSize;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
const float PI = 3.141592653;
void main()
{
    //纹理坐标转为图片坐标
    vec2 imgTex = v_texcoord * texSize;
    float r = 0.3 * texSize.x; //设置半径为图片宽度的 0.3 倍
    //取圆心为中心点
    if(distance(imgTex, vec2(texSize.x / 2.0, texSize.y / 2.0)) < r)
    {
        vec2 tranTex = v_texcoord - 0.5;
        vec2 imgTranTex = tranTex * texSize;
        float len = length(imgTranTex);
        float angle = 0.0;
        angle = acos(imgTranTex.x / len);
        if(tranTex.y < 0.0)
        {
            angle *= -1.0;
        }
        angle -= u_offset;
        imgTranTex.x = len * cos(angle);
        imgTranTex.y = len * sin(angle);
        vec2 newTexCoors = imgTranTex / texSize + 0.5;
        gl_FragColor = YuvToRgb(newTexCoors);
    }
    else
    {
        gl_FragColor = YuvToRgb(v_texcoord);
    }
}

画中画


画中画:将原纹理采样到屏幕中间的一块区域中,而屏幕之外区域的纹理坐标进行缩放之后再进行采样。

//画中画
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
vec2 scale(vec2 uv, float level)
{
    vec2 center = vec2(0.5, 0.5);
    vec2 newTexCoord = uv.xy;
    newTexCoord -= center;
    newTexCoord = newTexCoord / level;
    newTexCoord += center;
    return newTexCoord;
}
const float OFFSET_LEVEL = 0.15;
const float SCALE_LEVEL = 4.0;
void main()
{
    if(OFFSET_LEVEL < v_texcoord.x && v_texcoord.x < (1.0 - OFFSET_LEVEL)
       && OFFSET_LEVEL < v_texcoord.y && v_texcoord.y < (1.0 - OFFSET_LEVEL))
    {
        //将原图下采样到指定区域中
        vec2 newTexCoord = v_texcoord;
        newTexCoord -= OFFSET_LEVEL;
        newTexCoord = newTexCoord / (1.0 - 2.0 * OFFSET_LEVEL);
        gl_FragColor = YuvToRgb(newTexCoord);
    }
    else
    {
        //原纹理坐标缩放之后再进行采样
        gl_FragColor = YuvToRgb(scale(v_texcoord, SCALE_LEVEL));
    }
}

联系与交流

技术交流/获取源码可以添加我的微信:Byte-Flow


「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。

阿里云社区.png

相关文章
|
18天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
42 1
Android开发之使用OpenGL实现翻书动画
|
18天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
23 1
Android开发之OpenGL的画笔工具GL10
|
18天前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
61 0
|
18天前
|
XML Java Android开发
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
41 1
|
存储 编解码 算法
Opengl ES之LUT滤镜(上)
Opengl ES之连载系列
350 0
|
数据安全/隐私保护 开发者
OpenGL ES 多目标渲染(MRT)
Opengl ES连载系列
229 0
|
数据安全/隐私保护 索引
Opengl ES之纹理数组
Opengl ES连载系列
190 0
|
数据安全/隐私保护
Opengl ES之水印贴图
Opengl ES之连载系列
93 0
|
Java 数据安全/隐私保护 Android开发
Opengl ES之矩阵变换(下)
Opengl ES连载系列
87 0
|
Java API 数据安全/隐私保护
Opengl ES之矩阵变换(上)
Opengl ES连载系列
90 0