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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章