要加上实时美颜,就要编写texture -> 帧缓冲区的过程。
texture -> 帧缓冲区的过程是用OpenGL绘图。
OpenGL是一台管理渲染管线的状态机。
渲染管线有一个出口,一个入口。
入口可以指向一个texture,出口可以指向帧缓冲区或一个FBO。
帧缓冲区是显存中特殊的一块,储存经渲染管线加工后得到的二维像素数据,它对应的FBO索引为0。
几个步骤:
- 输出 -> cameraFrameBuffer,覆盖为(0,0,0,0)
输入 -> 摄像头纹理
用cameraProgram绘制 -> 顶点经过Matrix调整,cameraTexture为摄像头纹理 - 输出 -> gaussFrameBuffer,覆盖为(0,0,0,0)
输入 -> cameraTexture
用gaussProgram绘制 -> 顶点位置不变、gaussTexture为磨皮后的摄像头纹理 - 输出 -> 帧缓冲区 ,覆盖为(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);
}
}