《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ù)閱讀本文严拒,需要你對(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目錄下,

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è)濾鏡對(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 中

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

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市沧踏,隨后出現(xiàn)的幾起案子歌逢,更是在濱河造成了極大的恐慌,老刑警劉巖翘狱,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秘案,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡潦匈,警方通過查閱死者的電腦和手機(jī)阱高,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茬缩,“玉大人赤惊,你說我怎么就攤上這事』宋” “怎么了未舟?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長掂为。 經(jīng)常有香客問我裕膀,道長,這世上最難降的妖魔是什么勇哗? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任昼扛,我火速辦了婚禮,結(jié)果婚禮上欲诺,老公的妹妹穿的比我還像新娘抄谐。我一直安慰自己渺鹦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布斯稳。 她就那樣靜靜地躺著海铆,像睡著了一般迹恐。 火紅的嫁衣襯著肌膚如雪挣惰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天殴边,我揣著相機(jī)與錄音憎茂,去河邊找鬼。 笑死锤岸,一個(gè)胖子當(dāng)著我的面吹牛竖幔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播是偷,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼拳氢,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蛋铆?” 一聲冷哼從身側(cè)響起馋评,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刺啦,沒想到半個(gè)月后留特,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玛瘸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蜕青,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糊渊。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡右核,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渺绒,到底是詐尸還是另有隱情贺喝,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布芒篷,位于F島的核電站搜变,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏针炉。R本人自食惡果不足惜挠他,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望篡帕。 院中可真熱鬧殖侵,春花似錦贸呢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茉唉,卻和暖如春固蛾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背度陆。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工艾凯, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人懂傀。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓趾诗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蹬蚁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恃泪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

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