目前实现视频特效都是用的opengles,不了解opengles的同学请自行学习,opengles在2.0以后出了着色器语言,顶点着色器和片元着色器,可以让开发者在Gpu渲染的时候实现对图形的颜色和形体的控制。
不管是在录屏还是在播放视频,都可以通过EGL和Gpu产生关系将像素显示到屏幕上组成一张张绚丽的图像,或者直接用GLSurfaceView(封装好的EGL)。
opengles顶点着色器支持的颜色是rgba的,而解析的视频手机端常用NV21 / YV12格式,所以在传到着色器处理的时候应该转换一下。
在恐怖电影或电视剧中,常常有灵魂出窍的桥段,一般可以看见都是透明的形体慢慢从身体里飘出。那么想实现灵魂离体的效果的话,只要将原来的人物画到屏幕上,然后再画灵魂,复制形体的每一个像素采用opengles的混合效果,不断的动态改变灵魂的透明度,并用模型矩阵不断的放大形体产生飘出的效果。这个就是实现抖音灵魂出窍的原理。
下面来看一下主要代码的实现:
//画灵魂
GLES20.glEnable(GLES20.GL_BLEND);
//1:源 灵魂 GL_ONE:画灵魂自己
//2: 肉体 也是肉体自己
//两个都是用自己原本的颜色去混合
// GLES20.glBlendFunc(GLES20.GL_ONE,GLES20.GL_ONE);
//让灵魂整体颜色变淡
// GL_SRC_ALPHA: 取源(灵魂)的alpha 作为因子
// 假设alpha是0.2 rgb都是1 -> 混合就是用 rgb都是 0.2*1 整体变淡
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA,GLES20.GL_ONE);
//初始化矩阵 不进行任何缩放平移
Matrix.setIdentityM(matrix,0);
//设置缩放大小 本次放大为 1+当前灵魂次数占总次数*2的比例
//不一次放太大 为了达到较好的表现效果 fps*2
//所以这里值为 1+1/60 ---> 1+20/40 1.025... ---> 1.5
float scale = 1.0f + interval / (mFps * 2.f);
Matrix.scaleM(matrix,0,scale,scale,0);
//给肉体的 无变化矩阵
GLES20.glUniformMatrix4fv(vMatrix,1,false,matrix,0);
//传递透明度 透明度值为0-1 渐渐降低 0.1+x/100 x为fps-[0~fps]
//这里值为0.29 ---> 0.1
GLES20.glUniform1f(mAlpha, 0.1f + (mFps - interval) / 100.f);
实现不断的形体放大和透明度越来越淡,代码还容易懂,下面是片元着色器代码
precision mediump float;
varying vec2 aCoord;
//byte[] -》 分离出yuv ?
uniform sampler2D sampler_y; //yuv
uniform sampler2D sampler_u;
uniform sampler2D sampler_v;
//透明度
uniform float alpha;
void main(){
//4个float数据 y、u、v保存在向量中的第一个
float y = texture2D(sampler_y,aCoord).r;
float u = texture2D(sampler_u,aCoord).r - 0.5;
float v = texture2D(sampler_v,aCoord).r - 0.5;
// yuv转rgb的公式
//R = Y + 1.402 (v-128)
//G = Y - 0.34414 (u - 128) - 0.71414 (v-128)
//B = Y + 1.772 (u- 128)
vec3 rgb;
//u - 128
//1、glsl中 不能直接将int与float进行计算
//2、rgba取值都是:0-1 (128是0-255 归一化为0-1 128就是0.5)
rgb.r = y + 1.402 * v;
rgb.g = y - 0.34414 * u - 0.71414* v;
rgb.b = y + 1.772 * u;
//rgba
gl_FragColor = vec4(rgb,alpha);
}
将yuv12转化为rgb,并合成透明度,下面是顶点着色器:
//画肉体 base_vertex就可以了
//还要画灵魂
attribute vec4 vPosition;
attribute vec2 vCoord;
varying vec2 aCoord;
uniform mat4 vMatrix;
void main(){
gl_Position = vMatrix * vPosition;
// 进过测试 和设备有关(有些设备直接就采集不到图像,有些呢则会镜像)
aCoord = vCoord;
}
很简单总变换矩阵和顶点作用改变形体的大小、位置、角度等等。
想要完整代码的朋友请留言联系。