最通俗的安卓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 渲染步驟:
- 修改著色器的型別為擴充套件紋理
- 建立繫結擴充套件紋理
- 使用擴充套件紋理生成SurfaceTexture
- 設定Camera1/2的預覽資料設定顯示到SurfaceTexture中
- 繪製紋理
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+矩陣變換+水印 功能的實現程式碼。
哈哈哈,有點多,不貼了!直接上傳送門!