《OpenGL從入門到放棄07》濾鏡,一定要看,不虧

通過本文你將學(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)的濾鏡效果如下:

濾鏡.gif

繼續(xù)閱讀本文,需要你對OpenGL有一定了解,
如果對OpenGL不熟悉,請先閱讀以下文章:
《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è)像素的色彩值,再對讀取到的色彩值進(jìn)行調(diào)整,這樣就可以完成對圖片的色彩處理了。

1. 黑白圖片
我們應(yīng)該都知道,黑白圖片上,每個(gè)像素點(diǎn)的RGB三個(gè)通道值應(yīng)該是相等的。知道了這個(gè),將彩色圖片處理成黑白圖片就非常簡單了。

我們直接取出像素點(diǎn)的RGB三個(gè)通道,相加然后除以3作為處理后每個(gè)通道的值就可以得到一個(gè)黑白圖片了。這是常見的黑白圖片處理的一種方法。
類似的還有權(quán)值方法(給予RGB三個(gè)通道不同的比例)、只取綠色通道等方式。

與之類似的,
2. 冷色調(diào) 的處理就是單一增加藍(lán)色通道的值,
3. 暖色調(diào) 的處理是增加紅綠通道的值。

還有其他復(fù)古、浮雕等處理也都差不多,大家可以自行百度。

圖片模糊處理

圖片模糊處理相對上面的色調(diào)處理稍微復(fù)雜一點(diǎn),通常圖片模糊處理是采集周邊多個(gè)點(diǎn),然后利用這些點(diǎn)的色彩和這個(gè)點(diǎn)自身的色彩進(jìn)行計(jì)算(取平均值),得到一個(gè)新的色彩值作為目標(biāo)色彩。當(dāng)然,模糊處理有很多算法,類似高斯模糊、徑向模糊等等,大家可以自行百度。

放大鏡效果

放大鏡效果相對模糊處理來說,處理過程也會(huì)相對簡單一些。我們只需要將制定區(qū)域的像素點(diǎn),都以需要放大的區(qū)域中心點(diǎn)為中心,
向外延伸其到這個(gè)中心的距離即可實(shí)現(xiàn)放大效果。

四分鏡效果

把整張圖片縮成四份,然后分別放在左上角、右上角、左下角、右下角等地方。我們可以通過改變紋理坐標(biāo)實(shí)現(xiàn)


啰嗦那么多,show me the code ?


實(shí)戰(zhàn)

一、修改著色器

由于這次要大改著色器,所以將著色器代碼分別放到一個(gè)文件,放在assets目錄下,

image.png

后綴改為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è)濾鏡對應(yīng)一個(gè)片元著色器,不同的濾鏡效果在對應(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 來加載和緩存各種濾鏡對應(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 方法,即可完成所有濾鏡對應(yīng)的著色器的加載,然后切換濾鏡只需要獲取對應(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)建紋理對象
                1, //產(chǎn)生紋理id的數(shù)量
                textures, //紋理id的數(shù)組
                0  //偏移量
        );
        mTextureId = textures[0];

        //綁定紋理id,將對象綁定到環(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)屬性,最后對應(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è)置使用的紋理編號
        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ù),然后取出對應(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);
    }
}

通過對基類的封裝,子類要實(shí)現(xiàn)濾鏡就相當(dāng)清晰了,只要繼承BaseFilter,重寫
getProgram 方法,返回該濾鏡對應(yīng)的 Param
即可,冷速調(diào)、暖色調(diào)、模糊、四分鏡都是類似處理,簡單返回不同的Param即可,而對于發(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 中

  1. 初始化ShaderManager,預(yù)加載各種著色器,緩存起來
  2. 創(chuàng)建一個(gè)Bitmap
  3. 創(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é)一下:

  1. 修改著色器,增加不同的濾鏡對應(yīng)的片元著色器
  2. 用 ShaderManager 管理著色器跟OpenGL程序,緩存起來(提高切換濾鏡的性能)
  3. 創(chuàng)建濾鏡基類
  4. 不同濾鏡類繼承濾鏡基類,加載各自的濾鏡著色器
  5. 點(diǎn)擊切換濾鏡

如果這篇文章沒看懂,那么需要先看之前的幾篇文章,在文章開頭有。

也可以點(diǎn)擊源碼clone項(xiàng)目學(xué)習(xí)。
源碼僅供參考

下一篇將介紹動(dòng)畫實(shí)現(xiàn),敬請期待。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容