视频录制——2.加上实时美颜

要加上实时美颜,就要编写texture -> 帧缓冲区的过程。

texture -> 帧缓冲区的过程是用OpenGL绘图。
OpenGL是一台管理渲染管线的状态机。
渲染管线有一个出口,一个入口。
入口可以指向一个texture,出口可以指向帧缓冲区或一个FBO。
帧缓冲区是显存中特殊的一块,储存经渲染管线加工后得到的二维像素数据,它对应的FBO索引为0。

几个步骤:

  1. 输出 -> cameraFrameBuffer,覆盖为(0,0,0,0)
    输入 -> 摄像头纹理
    用cameraProgram绘制 -> 顶点经过Matrix调整,cameraTexture为摄像头纹理
  2. 输出 -> gaussFrameBuffer,覆盖为(0,0,0,0)
    输入 -> cameraTexture
    用gaussProgram绘制 -> 顶点位置不变、gaussTexture为磨皮后的摄像头纹理
  3. 输出 -> 帧缓冲区 ,覆盖为(0,0,0,0)
    输入 -> cameraTexture、gaussTexture、smoothTexture
    用smoothProgram绘制 -> 帧缓冲区为美白后的二维像素数据

*每次glDrawArrays前,都要先把输出的FBO覆盖为(0,0,0,0)。
*如果一个texture绑定到一个FBO,那么对FBO的写入操作会影响texture。这被用来进行render to texture。

常用的GL接口:

// 创建n个纹理,索引用textures装载返回
public static native void glGenTextures(int n, java.nio.IntBuffer textures);

// 创建n个FBO,索引用framebuffers装载返回
public static native void glGenFramebuffers(int n, java.nio.IntBuffer framebuffers);

// 把入口指向索引为texture的texture,texture的类型为target;特别地,当texture= 0时不使用纹理
public static native void glBindTexture(int target, int texture);

// 把出口指向索引为framebuffer的FBO,FBO的类型为target;特别地,当framebuffer = 0时指向帧缓冲区
public static native void glBindFramebuffer(int target, int framebuffer);

// 用bitmap填充texture
// target:texture的类型
// level:质量等级
// internalformat:图像格式
// type:数据格式
// border:边框宽度
public static void texImage2D(int target, int level, int internalformat, Bitmap bitmap, int type, int border)

// 把texture绑定到FBO
// target:texture的类型
// attachment:FBO的接入点
// textarget:texture的索引
// level:质量等级
public static native void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level);

// 设置用来覆盖出口指向的FBO或帧缓冲区的RGBA值(还没有覆盖)
public static native void glClearColor(float red, float green, float blue, float alpha);

// 用上一个接口设置的RGBA值来覆盖出口指向的FBO或帧缓冲区(覆盖了);
// mask指明覆盖哪一层,如GLES20.GL_COLOR_BUFFER_BIT覆盖颜色层
public static native void glClear(int mask);

// 设置视口位置及尺寸
public static native void glViewport(int x, int y, int width, int height);

// 开始绘制
// mode:以怎样的行为绘制
// first:起始处偏移量
// count:顶点数量
public static native void glDrawArrays(int mode, int first, int count);

*注意:所有“创建”的行为只是在OpenGL的命名空间中创建了一个名字(索引),只有在绑定后才真正为texture或FBO分配空间

关于GLSL

磨皮和美白的算法要用GLSL写Shader程序,记录一下常用的GLSL和载入方法

// attribute修饰符表示由APP传来的顶点数据,只用于vertex
attribute vec2 position;
attribute vec2 inputTextureCoordinate;

// uniform修饰符表示由APP传来的常量,可用于vertex和fragment
uniform float texelWidthOffset;
uniform float texelHeightOffset;

// varying修饰符表示由vertex初始化,供fragment使用的值
varying vec2 blurCoordinates[3];

void main() {
    // gl_Position是内建变量,表示顶点的输出位置
    gl_Position = vec4(position.xy,0,1);
    vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);
    blurCoordinates[0] = inputTextureCoordinate.xy;
    blurCoordinates[1] = inputTextureCoordinate.xy + singleStepOffset * optimizedGaussianOffsets[0];
    blurCoordinates[2] = inputTextureCoordinate.xy - singleStepOffset * optimizedGaussianOffsets[1];
}
String s = (上面那一段代码);
// 创建一个Shader,返回它的索引
int shader = GLES20.glCreateShader(GL_VERTEX_SHADER);
// 把源码绑定到这个Shader
GLES20.glShaderSource(shader, s);
// 编译源码
GLES20.glCompileShader(shader);
// 查看是否编译成功
// compiled装载是否成功,GL_TRUE或GL_FALSE
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
// 如果编译失败,删除Shader
if(compiled[0] == GL_FALSE) {
    GLES20.glDeleteShader(shader);
    shader = 0;
}elase {  // 如果编译成功
    // 创建一个Program
    int program= GLES20.glCreateProgram();
    // 把shader加入Program
    GLES20.glAttachShader(program, shader);
    // 释放shader资源
    GLES20.glDeleteShader(vertexShader );
    // 链接程序
    GLES20.glLinkProgram(program);
    // 获取program的链接情况
    GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
    // 若链接失败则删除program
    if (linkStatus[0] == GL_FALSE) {
        GLES20.glDeleteProgram(program);
        program = 0;
    } else {    // 如果链接成功
        // 使用program
        GLES20.glUseProgram(program);
        // 获取变量名的id,location是shader程序中声明为attribute的变量名
        int id = GLES20.glGetAttribLocation(program, location);
        // 允许shader从VBO读取这个变量的值
        GLES20.glEnableVertexAttribArray(id);
        // 设置索引为0的VBO为传输媒介,即APP向这个VBO写入数据,shader从这个VBO读取数据
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
        // 设置向VBO写入的数据,注意只有在调用glDrawArrays时才真正写入
        // size:变量的属性的数量,比如vec2类型的变量num = 2,vec3类型的变量num = 3
        // type:变量的属性的类型
        // normalized:如果写入的不是type类型的数据,是否要归一化
        // stride:每个数据的间隔
        // arrayBuffer:写入的数据
        GLES20.glVertexAttribPointer(id, size, type, normalized, stride, arrayBuffer);
    }
}

你可能感兴趣的:(视频录制——2.加上实时美颜)