Android OpenGL ES 相机预览适配不同分辨率的手机

当相机预览分辨率设置为1280*720,但是GLSurfaceView设置为正方形会如何?很明显画面会被拉伸导致变形,在想一下如果设置GLSurfaceView为全屏,但目前市场上的手机有很多种不同的分辨率,尤其是全面屏、折叠屏屏,这些手机并不是常见的16:9的手机,因此我们需要适配这些不同分辨率的手机​。

​有的同学可能可能会想根据不同分辨率的手机而设置不同的预览尺寸,是否可以呢?答案是 NO,因为camera的预览尺寸是需要硬件支持的,比如:camera支持640*4801280*720,我们只能设置支持的分辨率,在项目中可以通过如下方式获取camera支持的预览尺寸:

val mCamera = Camera.open(i)                
val parameters = mCamera.parameters
val supportSizeList = parameters.supportedPreviewSizes

既然相机的预览尺寸无法随便设置,那如何适配不同分辨率的手机呢?

视频适配和相机适配有一些不同,视频适配的最终效果是视频画面显示完全,保证不拉伸的前提下会出现黑色区域,而相机的适配最终的效果是保证不拉伸而且不能出现黑色区域,因此我们需要裁剪纹理(相机画面)来实现适配​。

假设相机预览尺寸是比率3/4(640*480),渲染窗口比率是9/16,正常不拉伸的效果如下:

底下的浅红色表示渲染窗口,上面的浅蓝色表示相机预览画面,想要达到画面不拉伸而且铺满渲染窗口需要放大相机预览画面,放大到如下效果:

 

将纹理等比放大,如上图所示。因此我们只需要裁剪浅红色区域的纹理并显示就达到了适配的目的​。

原理搞清楚了,接下来在相机预览的基础上进行修改,顶点shader修改如下:

attribute vec4 a_Position;
attribute vec4 a_TexCoordinate;
uniform mat4 mMatrix;
uniform mat4 mTextureMatrix;
varying vec4 v_TexCoord;

void main()
{
    v_TexCoord = mTextureMatrix * a_TexCoordinate;
    gl_Position = mMatrix * a_Position;
}

增加mTextureMatrix纹理矩阵,mTextureMatrix作用在纹理坐标上。

片段shader修改如下:

#extension GL_OES_EGL_image_external : require
precision mediump float;

uniform samplerExternalOES u_Texture;
varying vec4 v_TexCoord;

void main()
{
    gl_FragColor = texture2D(u_Texture, v_TexCoord.xy);
}

v_TexCoord类型由vec2变为vec4,采样的时候使用v_TexCoord.xy。

获取mTextureMatrix纹理矩阵参数句柄,代码如下:

override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
    ...
    mTextureMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "mTextureMatrix")
    ...
}

通过矩阵裁剪纹理,代码如下:

var mTextureMatrix = FloatArray(16)
        private var screenWidth = 0
        private var screenHeight = 0
        private var cameraWidth = 640
        private var cameraHeight = 480
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
            ...
            screenWidth = width
            screenHeight = height

            computeTextureMatrix()
        }

        private fun computeTextureMatrix() {
            val cameraRatio = cameraWidth / cameraHeight.toFloat()
            val screenRatio = screenWidth / screenHeight.toFloat()
            Matrix.setIdentityM(mTextureMatrix, 0)
            if (cameraRatio > screenRatio) {
                Matrix.scaleM(mTextureMatrix, 0, 1F, 1 - ((cameraRatio - screenRatio) / 2), 1F)
            } else if (cameraRatio < screenRatio) {
                Matrix.scaleM(mTextureMatrix, 0, 1 - ((screenRatio - cameraRatio) / 2), 1F, 1F)
            }
        }

绘制时设置mTextureMatrix纹理矩阵数据:

override fun onDrawFrame(p0: GL10?) {
    ...
    GLES20.glUniformMatrix4fv(mTextureMatrixLoc, 1, false, mTextureMatrix, 0)
    ...
}

 

本文内容转载自:https://zhuanlan.zhihu.com/p/107651857

 

posted @ 2021-03-14 12:01  灰色飘零  阅读(949)  评论(0编辑  收藏  举报