300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

时间:2024-02-24 17:40:54

相关推荐

Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

第一篇 Android Camera使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)第二篇 Android Camera使用OpenGL ES 2.0和TextureView对预览进行实时二次处理(黑白滤镜)第三篇 Android Camera2使用OpenGL ES 2.0和GLSurfaceView对预览进行实时二次处理(黑白滤镜)

下面进入正题:第一篇

首先讲一下,本文不使用Camera的PreviewCallback预览回调接口,因为onPreviewFrame()获取的数据格式只能是NV21或NV12,除非修改HAL层代码,一般情况下NV21或NV12需要转成RGB格式然后进行处理,这样太耗时了,所以本文使用SurfaceTexture来获取预览图像。

1. 添加GLSurfaceView作为布局界面

伪代码如下,这个不细讲了,可以参考我之前的博客 Android初始化OpenGL ES,并且分析Renderer子线程原理

//实例化一个GLSurfaceViewmGLSurfaceView = new GLSurfaceView(this);//配置OpenGL ES,主要是版本设置和设置Renderer,Renderer用于执行OpenGL的绘制mGLSurfaceView.setEGLContextClientVersion(2);mGLSurfaceView.setRenderer(new GLSurfaceView.Renderer());//在屏幕上显示GLSurfaceViewsetContentView(mGLSurfaceView);1234567

2. 在onCreate方法中开启相机并设置参数(本文只使用后置摄像头)

mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;mCamera = Camera.open(mCameraId)Camera.Parameters parameters = mCamera.getParameters();parameters.set("orientation", "portrait");parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);parameters.setPreviewSize(1280, 720);mCamera.setDisplayOrientation(90)setCameraDisplayOrientation(mActivity, mCameraId, mCamera);mCamera.setParameters(parameters);123456789

3. GLSurfaceView创建好OpenGL ES的环境后,在Renderer的onSurfaceCreated()中,创建一个外部纹理用于接收预览数据

public static int createOESTextureObject() {int[] tex = new int[1];//生成一个纹理GLES20.glGenTextures(1, tex, 0);//将此纹理绑定到外部纹理上GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);//设置纹理过滤参数GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);//解除纹理绑定GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);return tex[0];}12345678910111213141516171819

4. 开启预览

有了外部纹理,现在可以实例化一个SurfaceTexture了,之后即可开启Camera预览

//在onDrawFrame方法中调用此方法public boolean initSurfaceTexture() {//根据外部纹理ID创建SurfaceTexturemSurfaceTexture = new SurfaceTexture(mOESTextureId);mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {//每获取到一帧数据时请求OpenGL ES进行渲染mGLSurfaceView.requestRender();}});//讲此SurfaceTexture作为相机预览输出mCamera.setPreviewTexture(mSurfaceTexture);//开启预览mCamera.startPreview();return true;}1234567891011121314151617

接着在onDrawFrame中更新SurfaceTexture绑定的外部纹理图像,使其获取的是最新的预览数据。

if (mSurfaceTexture != null) {//更新纹理图像mSurfaceTexture.updateTexImage();//获取外部纹理的矩阵,用来确定纹理的采样位置,没有此矩阵可能导致图像翻转等问题mSurfaceTexture.getTransformMatrix(transformMatrix);}123456

到这里相机已经可以将预览数据发送到SurfaceTexture上,并且此预览数据实际上是填充到了SurfaceTexture绑定的外部纹理中,之后就可以操作此纹理为我们所用了。

5. 现在开始OpenGL ES部分代码编写,首先编写最重要的Shader代码

顶点着色器

private static final String VERTEX_SHADER = "" +//顶点坐标"attribute vec4 aPosition;\n" +//纹理矩阵"uniform mat4 uTextureMatrix;\n" +//自己定义的纹理坐标"attribute vec4 aTextureCoordinate";\n" +//传给片段着色器的纹理坐标"varying vec2 vTextureCoord;\n" +"void main()\n" +"{\n" +//根据自己定义的纹理坐标和纹理矩阵求取传给片段着色器的纹理坐标" vTextureCoord = (uTextureMatrix * aTextureCoordinate).xy;\n" +" gl_Position = aPosition;\n" +"}\n";123456789101112131415

片段着色器

private static final String FRAGMENT_SHADER = "" +//使用外部纹理必须支持此扩展"#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;\n" +//外部纹理采样器"uniform samplerExternalOES uTextureSampler;\n" +"varying vec2 vTextureCoord;\n" +"void main() \n" +"{\n" +//获取此纹理(预览图像)对应坐标的颜色值" vec4 vCameraColor = texture2D(uTextureSampler, vTextureCoord);\n" +//求此颜色的灰度值" float fGrayColor = (0.3*vCameraColor.r + 0.59*vCameraColor.g + 0.11*vCameraColor.b);\n" +//将此灰度值作为输出颜色的RGB值,这样就会变成黑白滤镜" gl_FragColor = vec4(fGrayColor, fGrayColor, fGrayColor, 1.0);\n" +"}\n";12345678910111213141516

6. 定义顶点和纹理坐标

//每行前两个值为顶点坐标,后两个为纹理坐标private static final float[] vertexData = {1f, 1f, 1f, 1f,-1f, 1f, 0f, 1f,-1f, -1f, 0f, 0f,1f, 1f, 1f, 1f,-1f, -1f, 0f, 0f,1f, -1f, 1f, 0f};123456789

因为屏幕为四边形,所以需要两个三角形,其坐标系(此时是物体坐标系)如下图所示,左上角为第一个三角形,序号为(1,2,3),右下角为第二个三角形,序号为(4,5,6)

三角形的颜色使用纹理进行填充,所以每个三角形的顶点需要与纹理坐标进行匹配,纹理坐标系如下,顶点在纹理的左下角。

将顶点和纹理坐标数据使用FloatBuffer来存储,防止内存回收

public FloatBuffer createBuffer(float[] vertexData) {FloatBuffer buffer = ByteBuffer.allocateDirect(vertexData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();buffer.put(vertexData, 0, vertexData.length).position(0);return buffer;}1234567

7. 编译Shader和链接program

vertexShader = loadShader(GL_VERTEX_SHADER, VERTEX_SHADER);fragmentShader = loadShader(GL_FRAGMENT_SHADER, FRAGMENT_SHADER);mShaderProgram = linkProgram(vertexShader, fragmentShader);//加载着色器,GL_VERTEX_SHADER代表生成顶点着色器,GL_FRAGMENT_SHADER代表生成片段着色器public int loadShader(int type, String shaderSource) {//创建Shaderint shader = glCreateShader(type);if (shader == 0) {throw new RuntimeException("Create Shader Failed!" + glGetError());}//加载Shader代码glShaderSource(shader, shaderSource);//编译ShaderglCompileShader(shader);return shader;}//将两个Shader链接至program中public int linkProgram(int verShader, int fragShader) {//创建programint program = glCreateProgram();if (program == 0) {throw new RuntimeException("Create Program Failed!" + glGetError());}//附着顶点和片段着色器glAttachShader(program, verShader);glAttachShader(program, fragShader);//链接programglLinkProgram(program);//告诉OpenGL ES使用此programglUseProgram(program);return program;}1234567891011121314151617181922232425262728293031323334

8. 关联顶点数据和顶点属性

现在需要将顶点坐标和纹理坐标传输给Shader,在onDrawFrame方法中执行下述代码:

//获取Shader中定义的变量在program中的位置aPositionLocation = glGetAttribLocation(mShaderProgram, "aPosition");aTextureCoordLocation = glGetAttribLocation(mShaderProgram, "aTextureCoordinate");uTextureMatrixLocation = glGetUniformLocation(mShaderProgram, "uTextureMatrix");uTextureSamplerLocation = glGetUniformLocation(mShaderProgram, "uTextureSampler");//激活纹理单元0glActiveTexture(GLES20.GL_TEXTURE0);//绑定外部纹理到纹理单元0glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mOESTextureId);//将此纹理单元床位片段着色器的uTextureSampler外部纹理采样器glUniform1i(uTextureSamplerLocation, 0);//将纹理矩阵传给片段着色器glUniformMatrix4fv(uTextureMatrixLocation, 1, false, transformMatrix, 0);//将顶点和纹理坐标传给顶点着色器if (mDataBuffer != null) {//顶点坐标从位置0开始读取mDataBuffer.position(0);//使能顶点属性glEnableVertexAttribArray(aPositionLocation);//顶点坐标每次读取两个顶点值,之后间隔16(每行4个值 * 4个字节)的字节继续读取两个顶点值glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 16, mDataBuffer);//纹理坐标从位置2开始读取mDataBuffer.position(2);glEnableVertexAttribArray(aTextureCoordLocation);/纹理坐标每次读取两个顶点值,之后间隔16(每行4个值 * 4个字节)的字节继续读取两个顶点值glVertexAttribPointer(aTextureCoordLocation, 2, GL_FLOAT, false, 16, mDataBuffer);}//绘制两个三角形(6个顶点)glDrawArrays(GL_TRIANGLES, 0, 6);1234567891011121314151617181922232425262728293031323334

至此运行此程序Camera预览就会显示为黑白滤镜,如下图所示。

总结一下:首先创建GLSurfaceView和开启相机,之后创建一个外部纹理,根据此纹理ID创建一个SurfaceTexture,Camera将预览数据输出至此SurfaceTexture上,执行SurfaceTexture.updateTexImage()就会将一帧预览数据推送给外部纹理上。之后OpenGL ES就可以操作此纹理,比如加滤镜,滤镜就是对纹理的RGBA通道进行处理,处理后的数据就通过OpenGL ES绘制出来,其实对于OpenGL ES来说,最终就是画两个三角形,三角形的颜色取自纹理对应的位置。

代码地址(顺手给个Star啊):点击查看源码

作者:lb377463323

出处:/lb377463323

原文链接:/lb377463323/article/details/77071054

转载请注明出处!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。