通過本文你將學(xué)習(xí)到濾鏡的原理橡娄,以及一些常用濾鏡的實(shí)現(xiàn)方式诗箍。
關(guān)于濾鏡大家應(yīng)該都不陌生,濾鏡功能廣泛應(yīng)用
于相機(jī)挽唉、圖庫滤祖、短視頻等應(yīng)用,如抖音瓶籽,看一下抖音的濾鏡界面
可以看到匠童,抖音提供了很多炫酷濾鏡。
無意間看到這篇文章
當(dāng)一個(gè) Android 開發(fā)玩抖音玩瘋了之后(二)
里面介紹了5種抖音濾鏡實(shí)現(xiàn)塑顺,不過因?yàn)檫€沒涉及動(dòng)畫和相機(jī)預(yù)覽汤求,所以這一節(jié)我們就拿一張靜態(tài)圖片來實(shí)現(xiàn)濾鏡功能。
我們最終要實(shí)現(xiàn)的濾鏡效果如下:
繼續(xù)閱讀本文严拒,需要你對(duì)OpenGL有一定了解扬绪,
如果對(duì)OpenGL不熟悉,請(qǐng)先閱讀以下文章:
《OpenGL從入門到放棄01 》一些基本概念
《OpenGL從入門到放棄02 》GLSurfaceView和Renderer
《OpenGL從入門到放棄03 》相機(jī)和視圖
《OpenGL從入門到放棄04 》畫一個(gè)長方形
《OpenGL從入門到放棄05 》著色器語言
《OpenGL從入門到放棄06 》紋理圖片顯示
進(jìn)入正題
一裤唠、原理
基本色調(diào)處理
在OpenGL中挤牛,顏色是用包含四個(gè)浮點(diǎn)的向量vec4表示,四個(gè)浮點(diǎn)分別表示RGBA四個(gè)通道种蘸,取值范圍為0.0-1.0墓赴。
我們先讀取圖片每個(gè)像素的色彩值,再對(duì)讀取到的色彩值進(jìn)行調(diào)整航瞭,這樣就可以完成對(duì)圖片的色彩處理了诫硕。
1. 黑白圖片:
我們應(yīng)該都知道,黑白圖片上沧奴,每個(gè)像素點(diǎn)的RGB三個(gè)通道值應(yīng)該是相等的痘括。知道了這個(gè),將彩色圖片處理成黑白圖片就非常簡(jiǎn)單了滔吠。
我們直接取出像素點(diǎn)的RGB三個(gè)通道,相加然后除以3作為處理后每個(gè)通道的值就可以得到一個(gè)黑白圖片了挠日。這是常見的黑白圖片處理的一種方法疮绷。
類似的還有權(quán)值方法(給予RGB三個(gè)通道不同的比例)、只取綠色通道等方式嚣潜。
與之類似的冬骚,
2. 冷色調(diào) 的處理就是單一增加藍(lán)色通道的值,
3. 暖色調(diào) 的處理是增加紅綠通道的值。
還有其他復(fù)古只冻、浮雕等處理也都差不多庇麦,大家可以自行百度。
圖片模糊處理
圖片模糊處理相對(duì)上面的色調(diào)處理稍微復(fù)雜一點(diǎn)喜德,通常圖片模糊處理是采集周邊多個(gè)點(diǎn)山橄,然后利用這些點(diǎn)的色彩和這個(gè)點(diǎn)自身的色彩進(jìn)行計(jì)算(取平均值),得到一個(gè)新的色彩值作為目標(biāo)色彩舍悯。當(dāng)然航棱,模糊處理有很多算法,類似高斯模糊萌衬、徑向模糊等等饮醇,大家可以自行百度。
放大鏡效果
放大鏡效果相對(duì)模糊處理來說秕豫,處理過程也會(huì)相對(duì)簡(jiǎn)單一些朴艰。我們只需要將制定區(qū)域的像素點(diǎn),都以需要放大的區(qū)域中心點(diǎn)為中心混移,
向外延伸其到這個(gè)中心的距離即可實(shí)現(xiàn)放大效果呵晚。
四分鏡效果
把整張圖片縮成四份,然后分別放在左上角沫屡、右上角饵隙、左下角、右下角等地方沮脖。我們可以通過改變紋理坐標(biāo)實(shí)現(xiàn)
啰嗦那么多金矛,show me the code ?
實(shí)戰(zhàn)
一、修改著色器
由于這次要大改著色器勺届,所以將著色器代碼分別放到一個(gè)文件驶俊,放在assets目錄下,
后綴改為glsl免姿,然后安裝as插件GLSL SUPPORT饼酿,打開即可看到語法高亮,還可以跳轉(zhuǎn)方法胚膊。
1故俐、頂點(diǎn)著色器
頂點(diǎn)著色器(公用):shader/image/filter/filter_vertex_base.glsl
uniform mat4 uMVPMatrix;//接收傳入的轉(zhuǎn)換矩陣
attribute vec4 aPosition;//接收傳入的頂點(diǎn)
attribute vec2 aTexCoord;//接收傳入的紋理坐標(biāo)
varying vec2 vTextureCoord;//增加用于傳遞給片元著色器的紋理坐標(biāo)
varying vec4 vPosition;//傳頂點(diǎn)坐標(biāo)給片元著色器
void main() {
gl_Position = uMVPMatrix * aPosition;//矩陣變換計(jì)算之后的位置
vPosition = uMVPMatrix * aPosition;//矩陣變換計(jì)算之后的位置
vTextureCoord = aTexCoord;
}
這里主要關(guān)注的是定義要傳遞給片元著色器的變量(其它的跟之前文章一樣):
vTextureCoord(紋理坐標(biāo))
vPosition(頂點(diǎn)坐標(biāo))
varying
修飾的變量是傳遞給片元著色器
2、片元著色器
重點(diǎn)在片元著色器的處理紊婉,一個(gè)濾鏡對(duì)應(yīng)一個(gè)片元著色器药版,不同的濾鏡效果在對(duì)應(yīng)的著色器里面處理:
2.1 普通的片元著色器
shader/image/filter/filter_fragment_base.glsl
precision mediump float;// 聲明float類型的精度為中等(精度越高越耗資源)
varying vec2 vTextureCoord;//頂點(diǎn)著色器傳過來的紋理坐標(biāo)向量
uniform sampler2D uTexture;//紋理采樣器,代表一副紋理
varying vec4 vPosition; //頂點(diǎn)著色器把坐標(biāo)傳過來
void main() {
vec4 color = texture2D(uTexture, vTextureCoord);//進(jìn)行紋理采樣,拿到當(dāng)前顏色
gl_FragColor = color;//不特殊處理
}
2.2 黑白濾鏡
shader/image/filter/filter_fragment_gray.glsl
//黑白濾鏡
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
void main() {
vec4 nColor = texture2D(vTexture, vTextureCoord);//進(jìn)行紋理采樣,拿到當(dāng)前顏色
float c=(nColor.r + nColor.g + nColor.b)/3.0; //黑白的處理就是將rgb通道的顏色相加再除以3喻犁,再作為rgb通道的值
gl_FragColor=vec4(c, c, c, nColor.a);
}
2.3 暖色濾鏡
shader/image/filter/filter_fragment_warm.glsl
//暖色濾鏡
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
void main() {
vec4 nColor = texture2D(vTexture, vTextureCoord);//進(jìn)行紋理采樣,拿到當(dāng)前顏色
gl_FragColor=nColor + vec4(0.2, 0.2, 0.0, 0.0); //暖就是多加點(diǎn)紅跟綠
}
2.4 冷色濾鏡
shader/image/filter/filter_fragment_cool.glsl
//冷色濾鏡
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
void main() {
vec4 nColor = texture2D(vTexture, vTextureCoord);//進(jìn)行紋理采樣,拿到當(dāng)前顏色
vec4 deltaColor=nColor + vec4(0.0, 0.0, 0.3, 0.0); //冷就是多加點(diǎn)藍(lán)
gl_FragColor=deltaColor;
}
2.5 模糊濾鏡
shader/image/filter/filter_fragment_buzzy.glsl
//模糊濾鏡
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
void main() {
vec4 nColor = texture2D(vTexture, vTextureCoord);//進(jìn)行紋理采樣,拿到當(dāng)前顏色
float dis = 0.01; //距離越大越模糊
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y+dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y+dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y+dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y+dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x-dis, vTextureCoord.y+dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y-dis));
nColor+=texture2D(vTexture, vec2(vTextureCoord.x+dis, vTextureCoord.y+dis));
nColor/=13.0; //周邊13個(gè)顏色相加槽片,然后取平均何缓,作為這個(gè)點(diǎn)的顏色
gl_FragColor=nColor;
}
2.6 四分鏡濾鏡
shader/image/filter/filter_fragment_four.glsl
//四分鏡
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
void main() {
//四分鏡就是把整張圖片縮成四份,然后分別放在左上角还栓、右上角碌廓、左下角、右下角等地方剩盒。我們可以通過改變紋理坐標(biāo)(x和y)得到
//類似兩分鏡也是同理
vec2 uv = vTextureCoord;
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(vTexture, uv);
}
2.7 發(fā)光濾鏡
shader/image/filter/filter_fragment_light.glsl
//發(fā)光
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D vTexture;
uniform float uTime; //應(yīng)用傳時(shí)間戳過來
void main() {
float lightUpValue = abs(sin(uTime / 1000.0)) / 4.0; //計(jì)算變化值谷婆,sin函數(shù)
vec4 src = texture2D(vTexture, vTextureCoord);
vec4 addColor = vec4(lightUpValue, lightUpValue, lightUpValue, 1.0);
gl_FragColor = src + addColor; //不斷地添加一個(gè)顏色
}
發(fā)光濾鏡利用sin函數(shù)(取值是-1到1),然后abs取正(0到1)勃刨,然后除以4波材,最終lightUpValue 的值是0到0.25,然后給rgb通道都加上一個(gè) lightUpValue 值身隐,就能實(shí)現(xiàn)變亮和變暗的效果廷区,像在發(fā)光。
以上是濾鏡的片元著色器代碼贾铝,參考Camera中的思想隙轻,新建一個(gè)著色器管理類
ShaderManager 來加載和緩存各種濾鏡對(duì)應(yīng)的OpenGL程序參數(shù)
3、 ShaderManager
/**
* 著色器管理垢揩,初始化一次玖绿,以后都從緩存取
*/
public class ShaderManager {
public static final int BASE_SHADER = 1; //默認(rèn)
public static final int GRAY_SHADER = 2; //黑白、灰色
public static final int WARM_SHADER = 3; //暖色
public static final int COOL_SHADER = 4; //冷色
public static final int BUZZY_SHADER = 5; //模糊
public static final int FOUR_SHADER = 6; //四分鏡
public static final int ZOOM_SHADER = 7; //放大
public static final int LIGHT_SHADER = 8; //發(fā)光,比較復(fù)雜一點(diǎn)
private static SparseArray<Param> mParamSparseArray;
public static void init(Context context) {
mParamSparseArray = new SparseArray<>();
insertParam(BASE_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_base.glsl"));
insertParam(GRAY_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_gray.glsl"));
insertParam(WARM_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_warm.glsl"));
insertParam(COOL_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_cool.glsl"));
insertParam(BUZZY_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_buzzy.glsl"));
insertParam(FOUR_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_four.glsl"));
insertParam(ZOOM_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_zoom.glsl"));
insertParam(LIGHT_SHADER, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_vertex_base.glsl")
, GLUtil.loadFromAssetsFile(context, "shader/image/filter/filter_fragment_light.glsl"));
}
public static void insertParam(int key, String vertexShaderCode, String fragmentShaderCode) {
int program = GLUtil.createProgram(vertexShaderCode, fragmentShaderCode);
// 獲取頂點(diǎn)著色器的位置的句柄(這里可以理解為當(dāng)前繪制的頂點(diǎn)位置)
int positionHandle = GLES20.glGetAttribLocation(program, "aPosition");
// 獲取變換矩陣的句柄
int mMVPMatrixHandle = GLES20.glGetUniformLocation(program, "uMVPMatrix");
//紋理位置句柄
int mTexCoordHandle = GLES20.glGetAttribLocation(program, "aTexCoord");
//緩存OpenGL程序
Param param = new Param(program, positionHandle, mMVPMatrixHandle, mTexCoordHandle);
mParamSparseArray.append(key, param);
}
//通過key獲取緩存中的OpenGL程序參數(shù)
public static Param getParam(int key) {
return mParamSparseArray.get(key);
}
/**
* 定義一些要緩存的參數(shù)
*/
public static class Param {
public Param(int program, int positionHandle, int MVPMatrixHandle, int texCoordHandle) {
this.program = program;
this.positionHandle = positionHandle;
mMVPMatrixHandle = MVPMatrixHandle;
mTexCoordHandle = texCoordHandle;
}
public int program;
//一些公用的句柄(頂點(diǎn)位置叁巨、矩陣斑匪、紋理坐標(biāo))
public int positionHandle;
public int mMVPMatrixHandle;
public int mTexCoordHandle;
}
}
在onSurfaceCreated中通過調(diào)用 init
方法,即可完成所有濾鏡對(duì)應(yīng)的著色器的加載锋勺,然后切換濾鏡只需要獲取對(duì)應(yīng)的緩存的參數(shù)即可蚀瘸。
4、濾鏡的基類 BaseFilter
不同的濾鏡效果庶橱,只是在片元著色器上會(huì)有大的不同贮勃,其它基本都是一樣的代碼,所以我們將這些公有的代碼抽取出一個(gè)類苏章,BaseFilter
/**
* 濾鏡的基類
* <p>
* 加載不同的著色器就有不同濾鏡效果
*/
public class BaseFilter {
private static final String TAG = "BaseFilterView";
private FloatBuffer mVertexBuffer; //頂點(diǎn)坐標(biāo)數(shù)據(jù)要轉(zhuǎn)化成FloatBuffer格式
private FloatBuffer mTexCoordBuffer;//頂點(diǎn)紋理坐標(biāo)緩存
//當(dāng)前繪制的頂點(diǎn)位置句柄
protected int vPositionHandle;
//變換矩陣句柄
protected int mMVPMatrixHandle;
//這個(gè)可以理解為一個(gè)OpenGL程序句柄
protected int mProgram;
//紋理坐標(biāo)句柄
protected int mTexCoordHandle;
//變換矩陣寂嘉,提供set方法
private float[] mvpMatrix = new float[16];
//紋理id
protected int mTextureId;
public void setMvpMatrix(float[] mvpMatrix) {
this.mvpMatrix = mvpMatrix;
}
private Context mContext;
private Bitmap mBitmap;
public BaseFilter(Context context, Bitmap bitmap) {
mContext = context;
this.mBitmap = bitmap;
//初始化Buffer、Shader枫绅、紋理
initBuffer();
initShader();
initTexture();
}
//數(shù)據(jù)轉(zhuǎn)換成Buffer
private void initBuffer() {
float vertices[] = new float[]{
-1, 1, 0,
-1, -1, 0,
1, 1, 0,
1, -1, 0,
};//頂點(diǎn)位置
float[] colors = new float[]{
0, 0,
0, 1,
1, 0,
1, 1,
};//紋理頂點(diǎn)數(shù)組
mVertexBuffer = GLUtil.floatArray2FloatBuffer(vertices);
mTexCoordBuffer = GLUtil.floatArray2FloatBuffer(colors);
}
/**
* 著色器
*/
private void initShader() {
//獲取程序泉孩,封裝了加載、鏈接等操作
ShaderManager.Param param = getProgram();
mProgram = param.program;
vPositionHandle = param.positionHandle;
// 獲取變換矩陣的句柄
mMVPMatrixHandle = param.mMVPMatrixHandle;
//紋理位置句柄
mTexCoordHandle = param.mTexCoordHandle;
}
/**
* 濾鏡子類重寫這個(gè)方法撑瞧,加載不同的OpenGL程序
*
* @return
*/
protected ShaderManager.Param getProgram() {
return ShaderManager.getParam(ShaderManager.BASE_SHADER);
}
protected int initTexture() {
Log.d(TAG, "initTexture: start");
int textures[] = new int[1]; //生成紋理id
GLES20.glGenTextures( //創(chuàng)建紋理對(duì)象
1, //產(chǎn)生紋理id的數(shù)量
textures, //紋理id的數(shù)組
0 //偏移量
);
mTextureId = textures[0];
//綁定紋理id棵譬,將對(duì)象綁定到環(huán)境的紋理單元
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);//設(shè)置MIN 采樣方式
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);//設(shè)置MAG采樣方式
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);//設(shè)置S軸拉伸方式
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);//設(shè)置T軸拉伸方式
if (mBitmap == null) {
Log.e(TAG, "initTexture: mBitmap == null");
return -1;
}
//加載圖片
GLUtils.texImage2D( //實(shí)際加載紋理進(jìn)顯存
GLES20.GL_TEXTURE_2D, //紋理類型
0, //紋理的層次,0表示基本圖像層预伺,可以理解為直接貼圖
mBitmap, //紋理圖像
0 //紋理邊框尺寸
);
Log.d(TAG, "initTexture: end,mTextureId=" + textures[0]);
return textures[0];
}
public void draw() {
// 將程序添加到OpenGL ES環(huán)境
GLES20.glUseProgram(mProgram);
/**設(shè)置數(shù)據(jù)*/
// 啟用頂點(diǎn)屬性订咸,最后對(duì)應(yīng)禁用
GLES20.glEnableVertexAttribArray(vPositionHandle);
GLES20.glEnableVertexAttribArray(mTexCoordHandle);
//設(shè)置三角形坐標(biāo)數(shù)據(jù)(一個(gè)頂點(diǎn)三個(gè)坐標(biāo))
GLES20.glVertexAttribPointer(vPositionHandle, 3,
GLES20.GL_FLOAT, false,
3 * 4, mVertexBuffer);
//設(shè)置紋理坐標(biāo)數(shù)據(jù)
GLES20.glVertexAttribPointer(mTexCoordHandle, 2,
GLES20.GL_FLOAT, false,
2 * 4, mTexCoordBuffer);
// 將投影和視圖轉(zhuǎn)換傳遞給著色器,可以理解為給uMVPMatrix這個(gè)變量賦值為mvpMatrix
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
//設(shè)置使用的紋理編號(hào)
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
//綁定指定的紋理id
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId);
/** 繪制三角形酬诀,三個(gè)頂點(diǎn)*/
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
// 禁用頂點(diǎn)數(shù)組(好像不禁用也沒啥問題)
GLES20.glDisableVertexAttribArray(vPositionHandle);
GLES20.glDisableVertexAttribArray(mTexCoordHandle);
}
public void onDestroy() {
GLES20.glDeleteProgram(mProgram);
mProgram = 0;
}
}
濾鏡的基類代碼還是比較清晰的脏嚷,跟上一篇加載紋理圖片基本一樣,這里要注意的地方是
initShader方法里面通過調(diào)用getProgram方法瞒御,從緩存中獲取OpenGL程序的參數(shù)父叙,然后取出對(duì)應(yīng)的句柄;
子類通過重寫getProgram方法這個(gè)方法肴裙,返回不同的著色器參數(shù)趾唱,就實(shí)現(xiàn)不同濾鏡效果
5、黑白濾鏡 GrayFilter
/**
* 黑白濾鏡比較好處理
*/
public class GrayFilter extends BaseFilter {
public GrayFilter(Context context, Bitmap bitmap) {
super(context, bitmap);
}
@Override
protected ShaderManager.Param getProgram(){
return ShaderManager.getParam(ShaderManager.GRAY_SHADER);
}
}
通過對(duì)基類的封裝蜻懦,子類要實(shí)現(xiàn)濾鏡就相當(dāng)清晰了甜癞,只要繼承BaseFilter,重寫
getProgram 方法宛乃,返回該濾鏡對(duì)應(yīng)的 Param
即可悠咱,冷速調(diào)、暖色調(diào)征炼、模糊析既、四分鏡都是類似處理,簡(jiǎn)單返回不同的Param即可谆奥,而對(duì)于發(fā)光濾鏡眼坏,涉及到新的參數(shù),看一下實(shí)現(xiàn)
6酸些、發(fā)光濾鏡 LightFilter
/**
* 發(fā)光濾鏡
*/
public class LightFilter extends BaseFilter {
private int uTimeHandle = 0;
private long startTime= 0;
public LightFilter(Context context, Bitmap bitmap) {
super(context, bitmap);
startTime = System.currentTimeMillis();
uTimeHandle = GLES20.glGetUniformLocation(mProgram,"uTime");
}
@Override
protected ShaderManager.Param getProgram(){
return ShaderManager.getParam(ShaderManager.LIGHT_SHADER);
}
@Override
public void draw() {
super.draw();
//不斷更新時(shí)間
GLES20.glUniform1f(uTimeHandle, (System.currentTimeMillis() - startTime));
}
}
其實(shí)也不難宰译,在構(gòu)造方法中獲取uTime
的句柄(因?yàn)?code>uTime句柄不是共有的句柄),然后在draw
方法中不斷傳新的值過去即可擂仍,然后著色器根據(jù)這個(gè)值的變化做處理囤屹,著色器的代碼上面有,沒印象可以往回翻一下逢渔。
7肋坚、切換模式
上面著色器代碼和濾鏡基類,子類都已經(jīng)準(zhǔn)備好了肃廓,接下來就差濾鏡的切換顯示了智厌。
具體代碼見 com.lanshifu.opengldemo.image.FilterRenderer
這個(gè)類,下面分成幾個(gè)步驟說一下
7.1 onSurfaceCreated 初始化
在onSurfaceCreated 中
- 初始化ShaderManager盲赊,預(yù)加載各種著色器铣鹏,緩存起來
- 創(chuàng)建一個(gè)Bitmap
- 創(chuàng)建BaseFilter,傳bitmap過去
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//著色器初始化哀蘑,緩存操作
ShaderManager.init(mContext);
try {
mBitmap = BitmapFactory.decodeStream(mContext.getResources().getAssets().open("picture.png"));
} catch (IOException e) {
e.printStackTrace();
}
//暫時(shí)傳Bitmap過去诚卸,后面涉及到相機(jī)再修改一下
mFilterView = new BaseFilter(mContext, mBitmap);
// 設(shè)置默認(rèn)背景顏色
GLES20.glClearColor(1.0f, 0.0f, 0, 1.0f);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
}
7.2 onSurfaceChanged 計(jì)算變換矩陣葵第,沒變化
7.3 點(diǎn)擊事件
點(diǎn)擊了不同的濾鏡按鈕,調(diào)用
setType方法設(shè)置當(dāng)前濾鏡類型,并將mFilterChange
標(biāo)志位設(shè)置為true合溺,這樣在onDrawFrame
中判斷這個(gè)這個(gè)標(biāo)志位true卒密,調(diào)用updateFilterView
方法更新當(dāng)前的FilterView
boolean mFilterChange = false;
int mFilterType;
public void setType(int filterType) {
if (this.mFilterType == filterType) {
Log.d(TAG, "setType: this.mFilterType == mFilterType");
return;
}
this.mFilterType = filterType;
mFilterChange = true;
}
7.4 onDrawFrame
調(diào)用updateFilterView
,然后調(diào)用 mFilterView.draw();
public void onDrawFrame(GL10 gl) {
if (mFilterChange) {
updateFilterView();
}
// Redraw background color 重繪背景
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
mFilterView.setMvpMatrix(mMVPMatrix);
mFilterView.draw();
}
7.5 updateFilterView
void updateFilterView() {
mFilterView = null;
switch (this.mFilterType) {
case ShaderManager.GRAY_SHADER:
mFilterView = new GrayFilter(mContext, mBitmap);
break;
case ShaderManager.BASE_SHADER:
mFilterView = new BaseFilter(mContext, mBitmap);
break;
case ShaderManager.WARM_SHADER:
mFilterView = new WarmFilter(mContext, mBitmap);
break;
case ShaderManager.COOL_SHADER:
mFilterView = new CoolFilter(mContext, mBitmap);
break;
case ShaderManager.BUZZY_SHADER:
mFilterView = new BuzzyFilter(mContext, mBitmap);
break;
case ShaderManager.FOUR_SHADER:
mFilterView = new FourFilter(mContext, mBitmap);
break;
case ShaderManager.ZOOM_SHADER:
mFilterView = new ZoomFilter(mContext, mBitmap);
break;
case ShaderManager.LIGHT_SHADER:
mFilterView = new LightFilter(mContext, mBitmap);
break;
default:
mFilterView = new BaseFilter(mContext, mBitmap);
break;
}
mFilterChange = false;
}
到此棠赛,整個(gè)濾鏡功能的實(shí)現(xiàn)流程已經(jīng)分析完了哮奇,總結(jié)一下:
- 修改著色器,增加不同的濾鏡對(duì)應(yīng)的片元著色器
- 用 ShaderManager 管理著色器跟OpenGL程序睛约,緩存起來(提高切換濾鏡的性能)
- 創(chuàng)建濾鏡基類
- 不同濾鏡類繼承濾鏡基類鼎俘,加載各自的濾鏡著色器
- 點(diǎn)擊切換濾鏡
如果這篇文章沒看懂,那么需要先看之前的幾篇文章辩涝,在文章開頭有贸伐。
也可以點(diǎn)擊源碼clone項(xiàng)目學(xué)習(xí)。
源碼僅供參考
下一篇將介紹動(dòng)畫實(shí)現(xiàn)膀值,敬請(qǐng)期待棍丐。