专栏首页曾大稳的博客OpenGLES通过SurfaceTexture预览摄像头画面

OpenGLES通过SurfaceTexture预览摄像头画面

在这篇文章主要用到的知识点有如下,建议先看一下:

OpenGLES绘制图片纹理

OpenGLES顶点缓冲VBO

OpenGLES帧缓冲FBO

有一个渲染流数据的相关的示例,也可以看一下,这样对本篇理解就会很简单 :

Android OpenGLES渲染MediaCodec解码数据

原理

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

主要步骤

1.OpenGL ES生成纹理 2.OpenGL ES创建SurfaceTexture并绑定 3.OpenGL ES摄像头预览

比如美颜相机那些,处理摄像头数据展示出来,为了提高预览的效率,所以这里使用了VBOFBO,如果不知道这个,请看上面的文章。

FBO所需的glsl: vertex_shader.glsl

attribute vec4 av_Position;//顶点位置
attribute vec2 af_Position;//纹理位置
varying vec2 v_texPo;//纹理位置  与fragment_shader交互
uniform mat4 u_Matrix;//矩阵变换

void main() {
    v_texPo = af_Position;
    gl_Position = av_Position * u_Matrix;
}

fragment_shader.glsl

#extension GL_OES_EGL_image_external : require //申明使用扩展纹理
precision mediump float;//精度 为float
varying vec2 v_texPo;//纹理位置  接收于vertex_shader
uniform samplerExternalOES  sTexture;//加载流数据(摄像头数据)
void main() {
    gl_FragColor=texture2D(sTexture, v_texPo);
}

这里使用FBO里面使用samplerExternalOES是为了加载流数据,摄像头数据属于流数据,所以这里需要用这个。

创建相机预览扩展纹理:

/**
    * 创建摄像头预览扩展纹理
    */
   private void createCameraRenderTexture() {
       int[] textureIds = new int[1];
       GLES20.glGenTextures(1, textureIds, 0);
       cameraRenderTextureId = textureIds[0];
       GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
       //环绕(超出纹理坐标范围)  (s==x t==y GL_REPEAT 重复)
       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);
       //过滤(纹理像素映射到坐标点)  (缩小、放大:GL_LINEAR线性)
       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);

       surfaceTexture = new SurfaceTexture(cameraRenderTextureId);
       surfaceTexture.setOnFrameAvailableListener(this);

       if (onSurfaceListener != null) {
           //这里相机拿到surfaceTexture绑定
           onSurfaceListener.onSurfaceCreate(surfaceTexture);
       }

       // 解绑扩展纹理
       GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
   }

camera绑定SurfaceTexture:

camera.setPreviewTexture(surfaceTexture);

预览画面,先通过fbo处理,然后拿到fbo的纹理id渲染即可:

@Override
    public void onDrawFrame() {
        //调用触发onFrameAvailable
        surfaceTexture.updateTexImage();
        //清空颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        //设置背景颜色
        GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

        //使用程序
        GLES20.glUseProgram(program);

        //绑定fbo
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);

        //摄像头预览扩展纹理赋值
        GLES20.glActiveTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraRenderTextureId);
        GLES20.glUniform1i(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

        //给变换矩阵赋值
        GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);

        GLES20.glEnableVertexAttribArray(avPosition);
        GLES20.glEnableVertexAttribArray(afPosition);

        //使用VBO设置纹理和顶点值
        useVboSetVertext();

        //绘制 GLES20.GL_TRIANGLE_STRIP:复用坐标
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
        GLES20.glDisableVertexAttribArray(avPosition);
        GLES20.glDisableVertexAttribArray(afPosition);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

        //渲染显示
        cameraRender.onDraw(fboTextureId);
    }

摄像头方向调整

默认的摄像头预览不同的角度预览出来效果是不同的,我们需要把它给矫正,一般通常是在camera里面设置parms,这里通过OpenGLES自己矫正,通过变换矩阵实现即可:

 //变换矩阵 location
private int uMatrix;

 //变换矩阵
private float[] matrix = new float[16];

public void onSurfaceCreated() {
    //...
uMatrix = GLES20.glGetUniformLocation(program, "u_Matrix");
}
 
 @Override
 public void onDrawFrame() {
 //...
 //给变换矩阵赋值
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0); 
 }


/**
 * 初始化矩阵
 */
public void resetMatirx() {
    //初始化
    Matrix.setIdentityM(matrix, 0);
}


/**
 * 旋转
 *
 * @param angle
 * @param x
 * @param y
 * @param z
 */
public void setAngle(float angle, float x, float y, float z) {
    //旋转
    Matrix.rotateM(matrix, 0, angle, x, y, z);
}

在外层调用:

public void previewAngle(Context context) {
        int angle = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        render.resetMatirx();
        switch (angle) {
            case Surface.ROTATION_0:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(90, 0, 0, 1);
                    render.setAngle(180, 1, 0, 0);
                } else {
                    render.setAngle(90f, 0f, 0f, 1f);
                }

                break;
            case Surface.ROTATION_90:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(180, 0, 0, 1);
                    render.setAngle(180, 0, 1, 0);
                } else {
                    render.setAngle(90f, 0f, 0f, 1f);
                }
                break;
            case Surface.ROTATION_180:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(90f, 0.0f, 0f, 1f);
                    render.setAngle(180f, 0.0f, 1f, 0f);
                } else {
                    render.setAngle(-90, 0f, 0f, 1f);
                }
                break;
            case Surface.ROTATION_270:
                if (cameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    render.setAngle(180f, 0.0f, 1f, 0f);
                } else {
                    render.setAngle(0f, 0f, 0f, 1f);
                }
                break;
        }
    }

具体代码下载地址: https://github.com/ChinaZeng/OpenGLESCameraDemo

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • OpenGLES添加水印

    理解了添加水印的原理,不管是视频水印还是图片水印都是很简单的了,只是使用的纹理不一样而已。如果是绘制文字水印的话,则需要将文字生成图片,然后将图片使用纹理绘制即...

    曾大稳
  • OpenGLES帧缓冲FBO

    曾大稳
  • OpenGLES正交投影

    上面我们得到的( ?)是不在归一化坐标范围内的,为了能使OpenGL正确的渲染,我们就需要把(?)以及其他边统一转换到归一化坐标内,这个操作就是正交投影

    曾大稳
  • 【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】五、OpenGL FBO数据缓冲区

    上一篇文章,讲解了如何使用EGL,并且提到EGL可以建立一个离屏渲染的缓冲区,这种离屏渲染的方式通常用于模拟整个渲染窗口,比如可以用于FFmpeg软编码,将显示...

    开发的猫
  • 树莓派3B搭建Flink集群

    今天的实战是用两台树莓派3B组建Flink1.7集群环境,模式是独立集群(Cluster Standalone);

    程序员欣宸
  • Python强大的pyecharts绘画优美图形<三>

    支持保存做种格式 对象.render(path='snapshot.html') 对象.render(path='snapshot.png') 对象.re...

    Python疯子
  • 使用React DnD实现列表拖拽排序

    React DnD 是一组 React 高阶组件,使用的时候只需要将对应的 API 将目标组件进行包裹,即可实现拖动或接受拖动元素的功能。

    IMWeb前端团队
  • Python爬虫:如何自动化下载王祖贤海报?

    上一讲中我给你讲了如何使用八爪鱼采集数据,对于数据采集刚刚入门的人来说,像八爪鱼这种可视化的采集是一种非常好的方式。它最大的优点就是上手速度快,当然也存在一些问...

    慕白
  • 埋在MYSQL数据库应用中的17个关键问题!

    Mysql的使用非常普遍,跟mysql有关的话题也非常多。要想掌握其中的精髓,可得花费不少功力,虽然目前流行的mysql替代方案有很多,可是从最小成本最容易维护...

    serena
  • 还在用手机换脸盆?手机回收ATM面世,无人零售不只是卖货

    智能手机已进入存量时代。2017年全球手机出货量依然高达15亿台,更新换代是人们购买手机的主要需求,“喜新厌旧”导致了大量旧手机产生,统计显示中国每年产生闲置手...

    罗超频道

扫码关注云+社区

领取腾讯云代金券