美文网首页
OpenGL ES - 渲染摄像头

OpenGL ES - 渲染摄像头

作者: Lucky胡 | 来源:发表于2019-11-25 17:32 被阅读0次

    OpenGL : Open Graphics Library

    OpenGL ES : 针对嵌入式系统设计的OpenGL的子集。

    在Android中使用OpenGL ES 2.0需要在AndroidManifest.xml添加:
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />

    GLSurfaceView :

    android.opengl提供的渲染界面,是SurfaceView的子类,封装了一些使用OpenGL ES需要的配置。
    管理Surface和EGL。
    (EGL是OpenGL和设备屏幕之间做桥接的类)
    允许自定义渲染器Renderer.
    让渲染器在独立线程里运作,和UI主线程分离。
    支持按需渲染和连续渲染。

    OpenGL绘制流程

    由三角形组成各种图形。


    OpenGL绘制流程

    OpenGL坐标系

    OpenGL坐标系

    坐标映射

    坐标映射

    Shader着色器

    着色器是运行在GPU上的小程序。
    顶点着色器(vertex shader)
    如何处理顶点、法线等数据的小程序
    片元着色器(fragment shader)
    如何处理光、阴影、遮挡、环境等对物体表面的影响。

    OpenGL函数命名格式

    函数库:gl
    根命令:Color
    参数个数:3
    参数类型:f
    glColor3f(...)

    Open GL语言 Open GL Shading Language GLSL

    数据类型

    float / vec2 / vec4 / sampler2D

    修饰符

    • attribute : 属性变量,只能用于顶点着色器中。一般用该变量表示一些顶点数据,如:顶点坐标、纹理坐标、颜色等。
    • uniforms : 一致变量,在着色器执行期间值不变的。与const常量不同的是,该值在编译时是不确定的,是运行时由着色器外部初始化的。
    • varying : 易变变量,从顶点着色器传递给片元着色器的数据变量。

    内建函数

    texture2D(采样器,坐标) : 采样指定位置的纹理

    内建变量

    gl_Position vec4类型,表示顶点着色器中顶点位置
    gl_FragColor vec4类型,表示片元着色器中的颜色

    精度控制

    precision lowp 低精度
    precision mediump 中精度
    precision highp 高精度

    顶点着色器 Vertex Shader

    //把顶点坐标给这个变量,确定要画画的形状
    attribute vec4 vPosition;
    
    //接收纹理坐标,接收采样器采样图片的坐标
    attribute vec4 vCoord;
    
    //变换矩阵,需要将原本的vCoord(01,11,00,10)与矩阵相乘,才能得到surfacetexture的正确采样坐标
    uniform mat4 vMatrix;
    
    //传递给片元着色器,像素点
    varying vec2 aCoord;
    
    void main() {
        //内置变量gl_position
        //把顶点数据赋值给这个变量,opengl就知道要画什么形状了
        gl_Position = vPosition;
    
        //和设备相关
        aCoord = vCoord.xy;
    }
    
    

    片元着色器

    //预览相机的着色器,顶点着色器不变,需要修改片元着色器
    //不再用sampler2D采样,需要用samplerExternalOES纹理采样器
    //需要在头部增加使用扩展纹理的声明#extension GL_OES_EGL_image_external : require
    #extension GL_OES_EGL_image_external : require
    
    //设置float采用的数据精度
    precision mediump float;
    
    //采样点坐标
    varying vec2 aCoord;
    
    //采样器
    uniform samplerExternalOES vTexture;
    
    
    void main() {
        //gl_FragColor 变量接收像素值
        //texture2D() : 采样器,采集aCoord的像素
        gl_FragColor = texture2D(vTexture,aCoord);
    }
    
    

    OpenGL实例:渲染Camera数据

    项目代码放在GitHub上,地址为:
    https://github.com/Hujunjob/AdvanceAndroid/tree/master/opengldemo

    1、自定义GLSurfaceView
    用于设置EGL版本号、设置自定义的渲染器、设置渲染模式

    class MyGLSurface(context: Context?, attrs: AttributeSet? = null) : GLSurfaceView(context, attrs) {
        init {
            //1、设置EGL版本
            setEGLContextClientVersion(2)
    
            //2、设置渲染器
            setRenderer(MyGLRenderer(this))
    
            //3、设置渲染模式
            renderMode = RENDERMODE_WHEN_DIRTY
        }
    }
    

    2、自定义渲染器

    /**
     * 核心类,自定义渲染器
     */
    class MyGLRenderer(var myGLSurface: MyGLSurface) : GLSurfaceView.Renderer,
        SurfaceTexture.OnFrameAvailableListener {
        private lateinit var mTextureIds: IntArray
        private lateinit var screenFilter: ScreenFilter
        var cameraEngine: ICameraEngine? = null
        var mSurfaceTexture:SurfaceTexture? = null
    
        var mtx = FloatArray(16)
    
        override fun onDrawFrame(gl: GL10?) {
            //在这里画画
            //1、清空画布,设置画布颜色,这里设置为红色
            GLES20.glClearColor(255f,0f,0f,0f)
            //设置清理模式
            //GL_COLOR_BUFFER_BIT 颜色缓存区
            //GL_DEPTH_BUFFER_BIT 深度缓冲区
            //GL_STENCIL_BUFFER_BIT 模型缓冲区
            GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT)
    
            //2、输出摄像头数据
            //更新纹理
            mSurfaceTexture?.updateTexImage()
            //获取旋转矩阵
            mSurfaceTexture?.getTransformMatrix(mtx)
    
            screenFilter.onDrawFrame(mTextureIds[0],mtx)
    
        }
    
        override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
            cameraEngine?.openCamera(true)
    
            //设置gl窗口
    //        GLES20.glViewport(0,0,width,height)
    
            screenFilter.onReady(width,height)
        }
    
        override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
            cameraEngine =
                CameraFactory.createCameraEngine(CameraFactory.CameraType.CAMERA1, myGLSurface.context)
    
            //准备画布
            mTextureIds = IntArray(1)
            //通过OpenGL创建纹理id
            GLES20.glGenTextures(mTextureIds.size,mTextureIds,0)
            //通过纹理id创建画布
            mSurfaceTexture = SurfaceTexture(mTextureIds[0])
            mSurfaceTexture?.setOnFrameAvailableListener(this)
            cameraEngine?.addSurfaceView(mSurfaceTexture!!)
    
            screenFilter = ScreenFilter(myGLSurface.context)
    
    
        }
    
        //画布有可用数据时回调
        override fun onFrameAvailable(surfaceTexture: SurfaceTexture?) {
            //请求渲染
            myGLSurface.requestRender()
        }
    
    }
    

    3、绘制类

    class ScreenFilter(var mContext: Context) {
    
        private var mVertexBuffer: FloatBuffer
        private var mTextureBuffer: FloatBuffer
        private var vTexture: Int
        private var vMatrix: Int
        private var vCoord: Int
        private var vPosition: Int
        private var mWidth = 0
        private var mHeight = 0
        var mProgramId = 0
    
        fun onReady(width: Int, height: Int) {
            mWidth = width
            mHeight = height
        }
    
        fun onDrawFrame(textureId: Int, mtx: FloatArray) {
            //1、设置视窗的宽高
            GLES20.glViewport(0,0,mWidth,mHeight)
    
            //2、使用着色器程序
            GLES20.glUseProgram(mProgramId)
    
            //3、渲染传值
            // 先传递顶点
            mVertexBuffer.clear()
    
            // C function void glVertexAttribPointer ( GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLint offset )
    //        fun glVertexAttribPointer(
    //            indx: Int,
    //            size: Int,
    //            type: Int,
    //            normalized: Boolean,
    //            stride: Int,
    //            ptr: Buffer?
    //        ): Unit
            //参数分别为:顶点坐标的索引,每个值的长度,值类型,是否归一化,步进(每次取完size后跳过多少个值取下一次值),数据
            GLES20.glVertexAttribPointer(vPosition,2,GLES20.GL_FLOAT,false,0,mVertexBuffer)
    
            //传递值后需要激活
            GLES20.glEnableVertexAttribArray(vPosition)
    
    
            //传递纹理
            mTextureBuffer.clear()
            GLES20.glVertexAttribPointer(vCoord,2,GLES20.GL_FLOAT,false,0,mTextureBuffer)
            GLES20.glEnableVertexAttribArray(vCoord)
    
            //4、变换矩阵
            GLES20.glUniformMatrix4fv(vMatrix,1,false,mtx,0)
    
    
            //片元着色器
            //首先激活图层
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
            //绑定纹理
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,textureId)
            //然后再传递片元着色器参数
            GLES20.glUniform1f(vTexture,0f)
    
            //通知OpenGL绘制
            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4)
        }
    
    
        init {
            //顶点着色器
            val vertext = TextResourceReader.read(mContext, R.raw.camera_vertex)
            //片元着色器
            val fragment = TextResourceReader.read(mContext, R.raw.camera_fragment)
    
            //一、配置顶点着色器
            //1、创建着色器
            val vShaderId = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
    
            //2、绑定代码到着色器上面,把着色器里的代码加载的着色器里
            GLES20.glShaderSource(vShaderId, vertext)
    
            //3、编译着色器代码
            GLES20.glCompileShader(vShaderId)
    
            var status = IntArray(1)
            //4、主动获取编译是否成功状态
            GLES20.glGetShaderiv(vShaderId, GLES20.GL_COMPILE_STATUS, status, 0)
    
            if (status[0] != GLES20.GL_TRUE) {
                throw IllegalStateException("配置顶点着色器失败")
            }
    
    
            //一、配置片元着色器
            //1、创建着色器
            val fShaderId = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
    
            //2、绑定代码到着色器上面,把着色器里的代码加载的着色器里
            GLES20.glShaderSource(fShaderId, fragment)
    
            //3、编译着色器代码
            GLES20.glCompileShader(fShaderId)
    
            //4、主动获取编译是否成功状态
            GLES20.glGetShaderiv(fShaderId, GLES20.GL_COMPILE_STATUS, status, 0)
    
            if (status[0] != GLES20.GL_TRUE) {
                throw IllegalStateException("配置片元着色器失败")
            }
    
    
            //三、着色器程序
            //1. 创建新的OpenGL程序
            mProgramId = GLES20.glCreateProgram()
    
            //2. 将着色器附加到程序,两个着色器都附加
            GLES20.glAttachShader(mProgramId, vShaderId)
            GLES20.glAttachShader(mProgramId, fShaderId)
    
            //3. 链接程序
            GLES20.glLinkProgram(mProgramId)
    
            //4. 获取链接状态
            GLES20.glGetShaderiv(fShaderId, GLES20.GL_LINK_STATUS, status, 0)
            if (status[0] != GLES20.GL_TRUE) {
                throw IllegalStateException("链接片元着色器失败")
            }
            GLES20.glGetShaderiv(vShaderId, GLES20.GL_LINK_STATUS, status, 0)
            if (status[0] != GLES20.GL_TRUE) {
                throw IllegalStateException("链接顶点着色器失败")
            }
    
            //四、释放、删除着色器
            //链接完成后,着色器都放到了OpenGL程序Program里,则着色器可以删除了
            GLES20.glDeleteShader(vShaderId)
            GLES20.glDeleteShader(fShaderId)
    
    
            //五、获取着色器程序变量的索引,通过索引来赋值
            //1. 顶点各个变量的索引
            vPosition = GLES20.glGetAttribLocation(mProgramId, "vPosition")
            vCoord = GLES20.glGetAttribLocation(mProgramId, "vCoord")
            vMatrix = GLES20.glGetUniformLocation(mProgramId, "vMatrix")
    
            //2、片元各个变量的索引
            vTexture = GLES20.glGetUniformLocation(mProgramId, "vTexture")
    
            //六、赋值
            //1、顶点坐标赋值
            mVertexBuffer = ByteBuffer
                .allocateDirect(4 * 4 * 2)     // 屏幕一共4个点,每个点有x/y两个坐标,坐标是Float类型,4个字节,则为4*4*2
                .order(ByteOrder.nativeOrder())     //设置使用硬件本地字节序列,保证数据排序
                .asFloatBuffer()                    //创建FloatBuffer
            mVertexBuffer.clear()
    
            //采用OpenGL的坐标系
            //屏幕边缘4个点
            //不能采用顺时针或逆时针依次获取4个点,因为这样无法无法组成闭合的矩形
            // 例如顺时针四个点分别为 a b c d,则输入进去的点为a b d c
            val v = floatArrayOf(
                -1f, 1f,
                -1f, -1f,
                1f, 1f,
                1f, -1f
            )
            mVertexBuffer.put(v)
    
    
            //2、纹理坐标赋值
            mTextureBuffer = ByteBuffer
                .allocateDirect(4 * 4 * 2)     // 屏幕一共4个点,每个点有x/y两个坐标,坐标是Float类型,4个字节,则为4*4*2
                .order(ByteOrder.nativeOrder())     //设置使用硬件本地字节序列,保证数据排序
                .asFloatBuffer()                    //创建FloatBuffer
            mTextureBuffer.clear()
    
            //纹理的坐标系采用Android系统坐标系
            //屏幕边缘4个点
            //需要跟顶点坐标的顺序一一对应,且需要是OpenGL坐标系和Android屏幕坐标系对应
    //        val t = floatArrayOf(
    //            0f, 0f,
    //            0f, 1f,
    //            1f, 0f,
    //            1f, 1f
    //        )
    
            //后摄顺时针旋转90度
    //        val t = floatArrayOf(
    //            0f, 1f,
    //            1f, 1f,
    //            0f, 0f,
    //            1f, 0f
    //        )
    
            //前摄逆时针旋转90度
    //        val t = floatArrayOf(
    //            1f, 0f,
    //            0f, 0f,
    //            1f, 1f,
    //            0f, 1f
    //        )
            //前摄逆时针旋转90度后再左右镜像
            val t = floatArrayOf(
                1f, 1f,
                0f, 1f,
                1f, 0f,
                0f, 0f
            )
            mTextureBuffer.put(t)
    
        }
    }
    

    4、配套工具类
    将raw文件里的文件读取为String输出。

    class TextResourceReader {
        companion object {
            fun read(context: Context, resourceId: Int):String {
                val sb = StringBuilder()
                val inputStream = context.resources.openRawResource(resourceId)
                val inputStreamReader = InputStreamReader(inputStream)
                val bufferReader = BufferedReader(inputStreamReader)
                var s = bufferReader.readLine()
                while (s!=null){
                    sb.append(s)
                    sb.append("\n")
                    s = bufferReader.readLine()
                }
                return sb.toString()
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:OpenGL ES - 渲染摄像头

          本文链接:https://www.haomeiwen.com/subject/jwsjwctx.html