天天看點

音視訊開發之旅(14) OpenGL ES 實時濾鏡

目錄

  1. OES是什麼?SurfaceTeture是什麼?
  2. 實時濾鏡的流程
  3. 具體實踐
  4. 遇到的問題
  5. 收獲

一、基本知識介紹

**外部紋理是什麼? **

public static native void glBindTexture(
        int target,
        int texture
    );
           

我們在前面幾篇中target一直是GL_TEXTURE_2D,即一張紋理圖檔,直接進行渲染。而在播放視訊或者Camera預覽 對資料進行濾鏡、特效處理後在渲染到螢幕上而不是直接渲染到螢幕,這是就需要用到OES外部紋理,GLES11Ext.GL_TEXTURE_EXTERNAL_OES

SurfaceTeture是什麼?

我們在前面章節介紹Camera預覽時使用的是SurfaceHolder進行的preview。

SurfaceTexture 類是在 Android 3.0 中引入的,它對圖像流的處理并不直接顯示,而是轉為GL外部紋理,可用于圖像流資料的二次處理(如Camera濾鏡,特效等)

SurfaceTexture從圖像流(Camera預覽、視訊解碼)中獲得幀資料,調用updateTexImage(),根據内容流中最近的圖像更新SurfaceTexture對應的GL紋理對象,接下來,就可以像操作普通GL紋理一樣操作它了。

二、流程

Camera采集資料後不直接顯示到螢幕上,而是先對這個圖像做濾鏡處理,即先渲染在一個外部紋理上,處理完之後在顯示在螢幕上。

OpenGL紋理繪制的基本流程。

  1. 建立Camera
  2. 建立和加載glsl,建立編譯連結program, 擷取location
  3. 建立外部紋理、綁定紋理、設定參數
  4. 根據建立的紋理id生成一個SurfaceTexture
  5. 設定Camera通過SurfaceTeture方式而不是SurfaceHolder進行預覽
  6. Camera将預覽資料輸出至此SurfaceTexture上,将一幀預覽資料推送給外部紋理上
  7. 給此紋理添加濾鏡
  8. 處理後的資料通過OpenGL ES繪制出來

三、實踐:Camera預覽添加實時濾鏡(原圖、黑白、冷暖色)

1. 建立Camera

private void initCamera(int cameraId) {
        curCameraId = cameraId;
        mCamera = Camera.open(curCameraId);
        Log.d(TAG, "initCamera: Camera Open ");


        Camera.Parameters parameters = mCamera.getParameters();
        Camera.Size closelyPreSize = CameraUtil.getCloselyPreSize(true, SystemUtils.getDisplayWidth(), SystemUtils.getDisplayHeight(), parameters.getSupportedPreviewSizes());
        Log.i(TAG, "initCamera: closelyPreSizeW=" + closelyPreSize.width + " closelyPreSizeH=" + closelyPreSize.height);
        parameters.setPreviewSize(closelyPreSize.width, closelyPreSize.height);
        //這裡把Camera的寬高做下反轉
        mPreviewWidth = closelyPreSize.height;
        mPreviewHeight = closelyPreSize.width;

        mCamera.setParameters(parameters);
    }
           

2. 加載glsl,建立program,擷取location

省略
           

3. 建立外部紋理

private int genOesTextureId() {
        int[] textureObjectId = new int[1];
        GLES20.glGenTextures(1, textureObjectId, 0);
        //綁定紋理
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureObjectId[0]);
        //設定放大縮小。設定邊緣測量
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST_MIPMAP_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
        return textureObjectId[0];
    }

int[] textureObjectId = new int[1];
        GLES20.glGenTextures(1, textureObjectId, 0);
         綁定外部紋理
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureObjectId[0]);
         設定放大縮小。設定邊緣測量
     GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST_MIPMAP_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
           

4. 根據建立的紋理id生成一個SurfaceTexture

mSurfaceTexture = new SurfaceTexture(mTextureId);
           

5. 設定Camera通過SurfaceTeture方式而不是SurfaceHolder進行預覽

mCamera.setPreviewTexture(mSurfaceTexture);
           

6. Camera将預覽資料輸出至此SurfaceTexture上,将一幀預覽資料推送給外部紋理上

public void onDrawFrame(GL10 gl) {
                //每次繪制後,都通知texture重新整理
                if (mSurfaceTexture != null) {              

                     mSurfaceTexture.updateTexImage();
                }
                
            }

           

7. OpenGL ES就可以操作此紋理,加濾鏡

private void onBindFilter() {
        GLES20.glUniform1i(mUFilterIndex, mIndex);

        switch (mIndex){
            case 0:
                //原圖效果,不同處理
                break;
            case 1:
                //黑白濾鏡

                GLES20.glUniform3fv(uColor,1,ImageBgData.GRAY_FILTER_COLOR_DATA,0);
                break;
            case 2:
                //暖色濾鏡
                GLES20.glUniform3fv(uColor,1,ImageBgData.WARM_FILTER_COLOR_DATA,0);

                break;
            case 3:
                //冷色濾鏡
                GLES20.glUniform3fv(uColor,1,ImageBgData.COOL_FILTER_COLOR_DATA,0);

                break;
        }
    }
           

8. 處理後的資料通過OpenGL ES繪制出來

GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + getTextureType());
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, getTextureId());
        GLES20.glUniform1i(mUTexture, getTextureType());

//設定定點資料
        GLES20.glEnableVertexAttribArray(mAPosition);
        GLES20.glVertexAttribPointer(
                mAPosition,
                2,
                GLES20.GL_FLOAT,
                false,
                0,
                mVerBuffer);
        //
        GLES20.glEnableVertexAttribArray(mACoord);
        GLES20.glVertexAttribPointer(
                mACoord,
                2,
                GLES20.GL_FLOAT,
                false,
                0,
                mTextureCoordinate);
        //繪制三角形帶
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glDisableVertexAttribArray(mAPosition);
        GLES20.glDisableVertexAttribArray(mACoord);
           

代碼托管在Github上

四、遇到的問題

  1. 如何設定給Camera設定SurfaceTexure而不是SurfaceHolde
  2. 畫面預覽被放大了,進行正交投影 矩陣變換前 把視圖的寬高比反轉下,因為camera的width大于height
  3. 在片元着色器中定義了uniform vec3 u_Color;但是寫program中綁定資料時卻用了glVertexAttrib3fv(這個隻用于頂點着色器,如果把color定義在頂點着色器,然後通過varying傳遞也可以)

    修正為 GLES20.glUniform3fv(uColor,1,ImageBgData.COOL_FILTER_COLOR_DATA,0);即可正常切換預覽

五、資料

[Android視訊編輯器(四)通過OpenGL給視訊增加不同濾鏡效果]

[專欄:Android圖像處理之實時濾鏡]

[專欄:圖像處理]

[Android Camera使用OpenGL ES 2.0和GLSurfaceView對預覽進行實時二次處理(黑白濾鏡)]

六、收獲

  1. 了解OES概念
  2. 了解FBO、VBO、PBO概念
  3. 通過實踐實時濾鏡熟悉流程

感謝你的閱讀

下一篇我們開始進入粒子系統,逐漸實作網易雲鲸雲特效。歡迎關注公衆号“音視訊開發之旅”,一起學習成長。

歡迎交流

版權聲明:本文為CSDN部落客「u011570979」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/u011570979/article/details/110595041