要加上實(shí)時(shí)美顏沼琉,就要編寫(xiě)texture -> 幀緩沖區(qū)的過(guò)程傻昙。
texture -> 幀緩沖區(qū)的過(guò)程是用OpenGL繪圖。
OpenGL是一臺(tái)管理渲染管線的狀態(tài)機(jī)贾惦。
渲染管線有一個(gè)出口绞惦,一個(gè)入口杰刽。
入口可以指向一個(gè)texture雁乡,出口可以指向幀緩沖區(qū)或一個(gè)FBO曲饱。
幀緩沖區(qū)是顯存中特殊的一塊,儲(chǔ)存經(jīng)渲染管線加工后得到的二維像素?cái)?shù)據(jù),它對(duì)應(yīng)的FBO索引為0。
幾個(gè)步驟:
- 輸出 -> cameraFrameBuffer象对,覆蓋為(0沥邻,0,0邮利,0)
輸入 -> 攝像頭紋理
用cameraProgram繪制 -> 頂點(diǎn)經(jīng)過(guò)Matrix調(diào)整贸诚,cameraTexture為攝像頭紋理 - 輸出 -> gaussFrameBuffer械念,覆蓋為(0,0,0,0)
輸入 -> cameraTexture
用gaussProgram繪制 -> 頂點(diǎn)位置不變、gaussTexture為磨皮后的攝像頭紋理 - 輸出 -> 幀緩沖區(qū) ,覆蓋為(0,0赛蔫,0,0)
輸入 -> cameraTexture彤恶、gaussTexture术徊、smoothTexture
用smoothProgram繪制 -> 幀緩沖區(qū)為美白后的二維像素?cái)?shù)據(jù)
*每次glDrawArrays前,都要先把輸出的FBO覆蓋為(0,0,0,0)。
*如果一個(gè)texture綁定到一個(gè)FBO,那么對(duì)FBO的寫(xiě)入操作會(huì)影響texture。這被用來(lái)進(jìn)行render to texture该镣。
常用的GL接口:
// 創(chuàng)建n個(gè)紋理,索引用textures裝載返回
public static native void glGenTextures(int n, java.nio.IntBuffer textures);
// 創(chuàng)建n個(gè)FBO,索引用framebuffers裝載返回
public static native void glGenFramebuffers(int n, java.nio.IntBuffer framebuffers);
// 把入口指向索引為texture的texture擦耀,texture的類型為target涩堤;特別地眷蜓,當(dāng)texture= 0時(shí)不使用紋理
public static native void glBindTexture(int target, int texture);
// 把出口指向索引為framebuffer的FBO胎围,F(xiàn)BO的類型為target;特別地福荸,當(dāng)framebuffer = 0時(shí)指向幀緩沖區(qū)
public static native void glBindFramebuffer(int target, int framebuffer);
// 用bitmap填充texture
// target:texture的類型
// level:質(zhì)量等級(jí)
// internalformat:圖像格式
// type:數(shù)據(jù)格式
// border:邊框?qū)挾?public static void texImage2D(int target, int level, int internalformat, Bitmap bitmap, int type, int border)
// 把texture綁定到FBO
// target:texture的類型
// attachment:FBO的接入點(diǎn)
// textarget:texture的索引
// level:質(zhì)量等級(jí)
public static native void glFramebufferTexture2D(int target, int attachment, int textarget, int texture, int level);
// 設(shè)置用來(lái)覆蓋出口指向的FBO或幀緩沖區(qū)的RGBA值(還沒(méi)有覆蓋)
public static native void glClearColor(float red, float green, float blue, float alpha);
// 用上一個(gè)接口設(shè)置的RGBA值來(lái)覆蓋出口指向的FBO或幀緩沖區(qū)(覆蓋了)颤介;
// mask指明覆蓋哪一層滚朵,如GLES20.GL_COLOR_BUFFER_BIT覆蓋顏色層
public static native void glClear(int mask);
// 設(shè)置視口位置及尺寸
public static native void glViewport(int x, int y, int width, int height);
// 開(kāi)始繪制
// mode:以怎樣的行為繪制
// first:起始處偏移量
// count:頂點(diǎn)數(shù)量
public static native void glDrawArrays(int mode, int first, int count);
*注意:所有“創(chuàng)建”的行為只是在OpenGL的命名空間中創(chuàng)建了一個(gè)名字(索引)学赛,只有在綁定后才真正為texture或FBO分配空間
關(guān)于GLSL
磨皮和美白的算法要用GLSL寫(xiě)Shader程序,記錄一下常用的GLSL和載入方法
// attribute修飾符表示由APP傳來(lái)的頂點(diǎn)數(shù)據(jù),只用于vertex
attribute vec2 position;
attribute vec2 inputTextureCoordinate;
// uniform修飾符表示由APP傳來(lái)的常量给梅,可用于vertex和fragment
uniform float texelWidthOffset;
uniform float texelHeightOffset;
// varying修飾符表示由vertex初始化,供fragment使用的值
varying vec2 blurCoordinates[3];
void main() {
// gl_Position是內(nèi)建變量迅办,表示頂點(diǎn)的輸出位置
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 = (上面那一段代碼);
// 創(chuàng)建一個(gè)Shader宅静,返回它的索引
int shader = GLES20.glCreateShader(GL_VERTEX_SHADER);
// 把源碼綁定到這個(gè)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 { // 如果編譯成功
// 創(chuàng)建一個(gè)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讀取這個(gè)變量的值
GLES20.glEnableVertexAttribArray(id);
// 設(shè)置索引為0的VBO為傳輸媒介,即APP向這個(gè)VBO寫(xiě)入數(shù)據(jù)矾策,shader從這個(gè)VBO讀取數(shù)據(jù)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
// 設(shè)置向VBO寫(xiě)入的數(shù)據(jù)磷账,注意只有在調(diào)用glDrawArrays時(shí)才真正寫(xiě)入
// size:變量的屬性的數(shù)量,比如vec2類型的變量num = 2贾虽,vec3類型的變量num = 3
// type:變量的屬性的類型
// normalized:如果寫(xiě)入的不是type類型的數(shù)據(jù)逃糟,是否要?dú)w一化
// stride:每個(gè)數(shù)據(jù)的間隔
// arrayBuffer:寫(xiě)入的數(shù)據(jù)
GLES20.glVertexAttribPointer(id, size, type, normalized, stride, arrayBuffer);
}
}