最通俗的安卓OpenGL教学10——Camera1/2预览+VBO+FBO+矩阵变换+水印

OpenGL 在相机中的使用也是非常广泛的,可以通过OpenGL给相机的预览数据做滤镜、美颜、水印、结合多纹理的渲染,可以实现很多的拍摄特效。所以,接下来会把之前的openGL基础知识都串联起来,做出一个OpenGL在Camera中的应用示例。

OpenGL预览摄像头数据的原理是利用OpenGL生成纹理并绑定到SurfaceTexture,然后把Camera的预览数据设置显示到SurfaceTexture中,这样就可以在OpenGL中拿到摄像头数据并显示了。

先看效果:

最通俗的安卓OpenGL教学10——Camera1/2预览+VBO+FBO+矩阵变换+水印_第1张图片

其实这里就是对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

你可能感兴趣的:(OpenGL,ES)