1. 程式人生 > 其它 >最通俗的安卓OpenGL教學10——Camera1/2預覽+VBO+FBO+矩陣變換+水印

最通俗的安卓OpenGL教學10——Camera1/2預覽+VBO+FBO+矩陣變換+水印

技術標籤:OpenGL ES

OpenGL 在相機中的使用也是非常廣泛的,可以通過OpenGL給相機的預覽資料做濾鏡、美顏、水印、結合多紋理的渲染,可以實現很多的拍攝特效。所以,接下來會把之前的openGL基礎知識都串聯起來,做出一個OpenGL在Camera中的應用示例。

OpenGL預覽攝像頭資料的原理是利用OpenGL生成紋理並繫結到SurfaceTexture,然後把Camera的預覽資料設定顯示到SurfaceTexture中,這樣就可以在OpenGL中拿到攝像頭資料並顯示了。

先看效果:

在這裡插入圖片描述

其實這裡就是對Camera知識的擴充套件了,不過我們只是把Camera當作是OpenGL的一種應用場景,OpenGL的應用場景有很多。現在要靈活的配合Camera應用OpenGL,還需要對一些安卓Camera的知識有所瞭解。所以,我先概括一下Camera的基礎知識及要點,後面如果還有時間,我再出一個安卓Camera系列的總結,看能不能也做成一個通俗易懂的教程。

1.安卓Camera基本知識及概念

一般,如果只是想簡單的實現拍照和拍視訊功能,可以利用Intent開啟系統提供的功能。

拍攝照片:MediaStore.ACTION_IMAGE_CAPTURE ;
拍攝視訊:MediaStore.ACTION_VIDEO_CAPTURE ;

但是如果要定製相機功能,那就會用到安卓Camera相關的api了

安卓有2套Camera API,其相容性如下:

API 21以下:android.hardware.Camera(已過時)

API 21以上:android.hardware.Camera2

安卓Camera可以利用裝置的相機來預覽畫面,拍照和錄製視訊,如果需要自動對焦功能,就要加入相應的許可權。

 <uses-permission android:name="android.permission.CAMERA" />
 <uses-feature android:name="android.hardware.camera" />
 <uses-feature android:name="android.hardware.camera.autofocus" />
 <uses-permission android:name="android.permission.WEITE_EXTERNAL_STORAGE"
/>

2.Camera1相關知識

2.1 使用步驟

Camera1的使用步驟一般為以下幾步:

  • 利用open(int cameraID)獲取Camera例項
  • 利用getParameters()獲取預設設定
  • 利用setParameters(Camera.Parameters)進行引數設定
  • 利用setDisplayOrientation(int)函式設定正確的預覽方向
  • 預覽影象,可以配合SurfaceView,利用setPreviewDisplay(SurfaceHolder)設定SurfaceView的SurfaceHolder用於預覽;也可以加入openGL進行影象渲染。
  • 呼叫startPreview()開始預覽
  • takePicture 拍攝照片
  • 呼叫takePickture後預覽會停止,想要繼續預覽就再呼叫- - startPreview()函式
  • 呼叫stopPreview()停止預覽
  • 呼叫release()釋放資源,可在onPause呼叫停止預覽,在onResume開始預覽。

3.Camera2 相關知識點

3.1重要的類

  • CamaraManager
  • CameraDevice
  • CameraCuptureSession
  • CameraCharacteristics
  • CuptureRequest
  • ImageRender
  • Listsurfaces

3.2使用步驟

①TextrueView.setSurfaceTextureListner()
onSurfaceTextureAvailable(SurfaceTexture surface,w,h);
②獲取CameraDevice物件
CameraManager.openCamera("0",new CameraStateCallback)
onOpened(CameraDevice cameraDevice);
③獲取CameraCaptureSsion物件
cameraDevice.createCapatureSsion(surfaces,CameraCaptureSession.StateCallback)
onConfigured( CameraCaptureSession session);
④建立會話
val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
val surface = Surface(mTextureView.surfaceTexture)
captureRequestBuilder.addTarget(surface)  // 將CaptureRequest的構建器與Surface物件繫結在一起
captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH)      // 閃光燈
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) // 自動對焦

⑤預覽
session.setRepeatingRequest(captureRequestBuilder.build(), mCaptureCallBack, mCameraHandler)

⑥拍照
//設定為預覽
val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
//用mImageReader存照片資料
captureRequestBuilder.addTarget(mImageReader?.surface)  // 將CaptureRequest的構建器與Surface物件繫結在一起
//根據攝像頭方向對儲存的照片進行旋轉,使其為"自然方向"
captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, mCameraSensorOrientation)

4.OpenGL渲染相機的預覽資料

4.1 原理:

利用OpenGL生成紋理並繫結到SurfaceTexture,然後把camera的預覽資料設定顯示到SurfaceTexture中,這樣就可以在OpenGL中拿到攝像頭資料並顯示了。

//繫結紋理
GLE20.glBindTexture(Exterend_OES,CameraTextureID);
//根據紋理ID建立 SurfaceTexture
surfaceTexture=new  SurfaceTexture(CameraTextureID);

預覽

//Camera1:
Camara.serPreviewTexture(surfaceTexture);

//Camera2:
surface=new Surface(SurfaceTexture)

4.2 渲染步驟:

  1. 修改著色器的型別為擴充套件紋理
  2. 建立繫結擴充套件紋理
  3. 使用擴充套件紋理生成SurfaceTexture
  4. 設定Camera1/2的預覽資料設定顯示到SurfaceTexture中
  5. 繪製紋理

4.2.1 修改著色器的型別為擴充套件紋理

#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 ft_Position;
uniform samplerExternalOES vTexture;//擴充套件紋理

void main(){
    gl_FragColor = texture2D(vTexture, ft_Position);
}

4.2.2. 建立繫結擴充套件紋理

 		//建立一個擴充套件紋理,渲染攝像頭資料,
        int[] textureIdCamera = new int[1];
        GLES20.glGenTextures(1, textureIdCamera, 0);
        int cameraTextureId = textureIdCamera[0];
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTextureId);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

4.2.3. 使用擴充套件紋理生成SurfaceTexture

        //將cameraTextureId 繫結到 surfaceTexture上
        surfaceTexture = new SurfaceTexture(cameraTextureId);

        if (onSurfaceCreateListener != null) {
            onSurfaceCreateListener.onSurfaceCreate(surfaceTexture, fboTextureId);
        }
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

4.2.3. 設定Camera1/2的預覽資料設定顯示到SurfaceTexture中

Camera1:

try {
            mCamera = Camera.open(mCameraId);
            if (mCamera != null) {
                mCamera.setPreviewTexture(surfaceTexture);
                Camera.Parameters parameters = mCamera.getParameters();
                if (parameters != null) {
                    parameters.setFlashMode("off");
                    parameters.setPreviewFormat(ImageFormat.NV21);//yuv420
                    Camera.Size size = getFitSize(parameters.getSupportedPictureSizes());
                    parameters.setPictureSize(size.width, size.height);
                    size = getFitSize(parameters.getSupportedPreviewSizes());
                    parameters.setPreviewSize(size.width, size.height);
                    parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                    mCamera.setParameters(parameters);
                    if (mOnCameraListener != null) {
                        mOnCameraListener.created();
                    }
                    mCamera.startPreview();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

Camera2:

        Surface surface = new Surface(mSurfaceTexture);
        try {
            mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    mCameraCaptureSession = session;
                    try {
                        CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                        builder.addTarget(surface);
                        mCameraCaptureSession.setRepeatingRequest(builder.build(), mCaptureCallback, mainHandler);
                        previewAngle(mContext,mCameraDevice);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    LogUtil.e("onConfigureFailed");
                }
            }, mainHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

4.2.4. 繪製相機預覽資料的擴充套件紋理

        //繫結 VBO,開始使用
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
        //使能頂點屬性資料,使之有效,使能之後,為頂點屬性賦值,繫結頂點座標
        GLES20.glEnableVertexAttribArray(vPosition);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 0);
        //使能片元屬性資料,使之有效, 使能之後,為片元屬性賦值,繫結紋理座標
        GLES20.glEnableVertexAttribArray(fPosition);
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4);
        //繪製
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

這樣就可以在自定義的YGLSurfaceView上顯示OpenGL渲染的攝像頭影象了。

5.完整程式碼

即:Camera1/2預覽+VBO+FBO+矩陣變換+水印 功能的實現程式碼。

哈哈哈,有點多,不貼了!直接上傳送門!

yOpenGL原始碼github