音视频开发 - 分屏滤镜

在抖音等常用的音视频软件中经常发现有分屏的滤镜效果,现在总结一下具体的VertexShader中,每个函数中的算法。

常规方法滤镜中的FragmentShader算法

vertexShader代码设置

attribute vec4 Position;   
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;

void main (void) {
    gl_Position = Position;
    TextureCoordsVarying = TextureCoords;
}

其中Position是我们的物体坐标,TextureCoords是纹理坐标,TextureCoordsVarying是我们需要传递到FragmentShader中的纹理坐标值,下面是FragmentShader中的内容

precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;

void main (void) {
    vec4 mask = texture2D(Texture, TextureCoordsVarying);
    gl_FragColor = vec4(mask.rgb, 1.0);
}

需要注意的是在顶点着色其中的数据类型默认是有精度值得,但是在片元着色器中是没有设置精度的,所以我们需要为float设置精度precision highp float;

分析片元着色器中的赋值,首先根据纹理ID和纹理坐标获取纹理像素值mask,将mask进行4维向量转化,其目的是设置1.0的透明度,这里设置的不透明的,也可以直接将mask赋值给gl_FragColor,查看这样算法的效果。


音视频开发 - 分屏滤镜_第1张图片
默认渲染纹理
二屏滤镜中的FragmentShader算法

我们默认是读取图片的中间部分,y轴方向是0.25-0.75,x轴方向0.0-1.0,所以我们在FragmentShader算法中进行修改。

precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;

void main() {
    vec2 uv = TextureCoordsVarying.xy;
    float y;
    if (uv.y >= 0.0 && uv.y <= 0.5) {
        y = uv.y + 0.25;
    } else {
        y = uv.y - 0.25;
    }
    gl_FragColor = texture2D(Texture, vec2(uv.x, y));
}

其中我们获取传递过来的纹理坐标的xy,然后对当前二维向量y值进行重新设置:
当y在uv.y >= 0.0 && uv.y <= 0.5,在y轴方向我们其实应该获取的是屏幕中间部分,所以y = uv.y + 0.25,将y方向上的纹理进行重新设置,读取从0.25 - 0.75的数据。
当0.5-1.0时,在y轴防线我们-0.25,获取从0.25 - 0.75的数据,效果如下。


音视频开发 - 分屏滤镜_第2张图片
二屏滤镜
三屏滤镜中的FragmentShader算法

我们默认是读取图片的中间1/3部分,y轴方向是1/3-2/3,x轴方向0.0-1.0,所以我们在FragmentShader算法中进行修改。

precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;

void main() {
    vec2 uv = TextureCoordsVarying.xy;
    if (uv.y < 1.0/3.0) {
        uv.y = uv.y + 1.0/3.0;
    } else if (uv.y > 2.0/3.0){
        uv.y = uv.y - 1.0/3.0;
    }
    gl_FragColor = texture2D(Texture, uv);
}

其中我们获取传递过来的纹理坐标的xy,然后对当前二维向量y值进行重新设置:
当y在uv.y < 1.0/3.0,在y轴方向我们其实应该获取的是屏幕中间1/3部分,所以uv.y = uv.y + 1.0/3.0,将y方向上的纹理进行重新设置,读取从1/3 - 2/3的数据。
当uv.y > 2.0/3.0时,在y轴防线我们- 1.0/3.0,获取从1/3 - 2/3的数据,效果如下。
其他的区域默认是不变的。
效果如下


音视频开发 - 分屏滤镜_第3张图片
三屏滤镜
四屏滤镜中的FragmentShader算法

我们默认是将整张图片进行压缩,将这张压缩图放到屏幕均分的四个位置,所以我们在FragmentShader算法中进行修改。

precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;

void main() {
    vec2 uv = TextureCoordsVarying.xy;
    if(uv.x <= 0.5){
        uv.x = uv.x * 2.0;
    }else{
        uv.x = (uv.x - 0.5) * 2.0;
    }
    
    if (uv.y<= 0.5) {
        uv.y = uv.y * 2.0;
    }else{
        uv.y = (uv.y - 0.5) * 2.0;
    }
    
    gl_FragColor = texture2D(Texture, uv);
}

其中我们获取传递过来的纹理坐标的xy,然后对当前二维向量x和y值进行压缩之后重新设置:
在x方向,当uv.x <= 0.5,我们将原始的x进行放大2倍,那么新的x获取到的就是原始纹理2倍x出的纹理像素值;在0.5-1.0,我们需要将原始x-0.5,在进行放大2倍,这样我们就将x方向纹理像素值缩小后后放到了新的x位置;
同理y轴方向进行缩放;效果如下


音视频开发 - 分屏滤镜_第4张图片
si屏滤镜
六屏滤镜中的FragmentShader算法

我们默认是读取图片的中间矩形部分,y轴方向是1/3-2/3,x轴方向1/3-2/3,所以我们在FragmentShader算法中进行修改。

precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;

void main() {
    vec2 uv = TextureCoordsVarying.xy;
   
    if(uv.x <= 1.0 / 3.0){
        uv.x = uv.x + 1.0/3.0;
    }else if(uv.x >= 2.0/3.0){
        uv.x = uv.x - 1.0/3.0;
    }
    
    if(uv.y <= 0.5){
        uv.y = uv.y + 0.25;
    }else {
        uv.y = uv.y - 0.25;
    }
    
    
    gl_FragColor = texture2D(Texture, uv);
}

其中我们获取传递过来的纹理坐标的xy,然后对当前二维向量x、y值进行重新设置:
当x方向,在uv.x <= 1.0 / 3.0,我们获取的应该是从1/3开始的纹理,所以+1/3,在uv.x >= 2.0/3.0时,还是取1/3开始的纹理,所以应该-1/3;
在y轴方向,我们取0.25-0.75两部分,y轴展示时我们可以分成0-0.5和0.5-1.0两部分,所以当uv.y <= 0.5,应该+0.25,在其他位置-0.25;
效果如下


音视频开发 - 分屏滤镜_第5张图片
屏幕快照 2019-10-24 下午5.16.09.png
总结

我们只是提供了一种算法的思路,具体的业务变化需要我们灵活的变动相应的算法,来适应不同的业务需要

你可能感兴趣的:(音视频开发 - 分屏滤镜)