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

2020-09-11 16:08:48 浏览数 (1)

一个

有态度

的程序员

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

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

代码语言:javascript复制
val mCamera = Camera.open(i)                
val parameters = mCamera.parameters
val supportSizeList = parameters.supportedPreviewSizes

既然相机的预览尺寸无法随便设置,那如何适配不同分辨率的手机呢?不知道大家对OpenGL ES for Android 视频缩放、旋转、平移是否还有印象,这篇文章中是视频对不同分辨率的适配,视频适配的效果和本文中相机适配的效果有一些不同,视频适配的最终效果是视频画面显示完全,保证不拉伸的前提下会出现黑色区域,而相机的适配最终的效果是保证不拉伸而且不能出现黑色区域,因此我们需要裁剪纹理(相机画面)来实现适配。

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

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

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

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

代码语言:javascript复制
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修改如下:

代码语言:javascript复制
#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纹理矩阵参数句柄,代码如下:

代码语言:javascript复制
 override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
  ...
  mTextureMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "mTextureMatrix")
  ...
}

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

代码语言:javascript复制
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)
      ...
        }

效果如下:

在不同分辨率的手机上不会出现拉伸的问题。

注意:我们手里面可能没有各种不同分辨率的手机,可以通过改变GLSurfaceView的大小来达到验证的目的

0 人点赞