OpenGL 在相机中的使用也是非常广泛的,可以通过OpenGL给相机的预览数据做滤镜、美颜、水印、结合多纹理的渲染,可以实现很多的拍摄特效。所以,接下来会把之前的openGL基础知识都串联起来,做出一个OpenGL在Camera中的应用示例。
OpenGL预览摄像头数据的原理是利用OpenGL生成纹理并绑定到SurfaceTexture,然后把Camera的预览数据设置显示到SurfaceTexture中,这样就可以在OpenGL中拿到摄像头数据并显示了。
先看效果:
其实这里就是对Camera知识的扩展了,不过我们只是把Camera当作是OpenGL的一种应用场景,OpenGL的应用场景有很多。现在要灵活的配合Camera应用OpenGL,还需要对一些安卓Camera的知识有所了解。所以,我先概括一下Camera的基础知识及要点,后面如果还有时间,我再出一个安卓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" />
Camera1的使用步骤一般为以下几步:
①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)
利用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)
#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);
}
//创建一个扩展纹理,渲染摄像头数据,
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);
//将cameraTextureId 绑定到 surfaceTexture上
surfaceTexture = new SurfaceTexture(cameraTextureId);
if (onSurfaceCreateListener != null) {
onSurfaceCreateListener.onSurfaceCreate(surfaceTexture, fboTextureId);
}
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
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();
}
//绑定 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渲染的摄像头图像了。
即:Camera1/2预览+VBO+FBO+矩阵变换+水印 功能的实现代码。
哈哈哈,有点多,不贴了!直接上传送门!
yOpenGL源码github