目錄
- OES是什麼?SurfaceTeture是什麼?
- 實時濾鏡的流程
- 具體實踐
- 遇到的問題
- 收獲
一、基本知識介紹
**外部紋理是什麼? **
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紋理繪制的基本流程。
- 建立Camera
- 建立和加載glsl,建立編譯連結program, 擷取location
- 建立外部紋理、綁定紋理、設定參數
- 根據建立的紋理id生成一個SurfaceTexture
- 設定Camera通過SurfaceTeture方式而不是SurfaceHolder進行預覽
- Camera将預覽資料輸出至此SurfaceTexture上,将一幀預覽資料推送給外部紋理上
- 給此紋理添加濾鏡
- 處理後的資料通過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上
四、遇到的問題
- 如何設定給Camera設定SurfaceTexure而不是SurfaceHolde
- 畫面預覽被放大了,進行正交投影 矩陣變換前 把視圖的寬高比反轉下,因為camera的width大于height
-
在片元着色器中定義了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對預覽進行實時二次處理(黑白濾鏡)]
六、收獲
- 了解OES概念
- 了解FBO、VBO、PBO概念
- 通過實踐實時濾鏡熟悉流程
感謝你的閱讀
下一篇我們開始進入粒子系統,逐漸實作網易雲鲸雲特效。歡迎關注公衆号“音視訊開發之旅”,一起學習成長。
歡迎交流
版權聲明:本文為CSDN部落客「u011570979」的原創文章,遵循CC 4.0 BY-SA版權協定,轉載請附上原文出處連結及本聲明。
原文連結:https://blog.csdn.net/u011570979/article/details/110595041