前言

OpenGL是Android音视频开发绕不过去的东西,书接上文,OpenGL ES加载一张小猫咪图片加载出来的小猫咪图片是倒着的,而且还把猫脸拉长了。像下图这样:

【Android音视频】OpenGL ES翻转拉伸问题

本文就分析一下为啥会这样,然后将它摆正。有爱好能够拷一份代码一同看看:github.com/MReP1/OpenG…。

分析原因

猫咪翻转

倒过来也分两种状况,一种是旋转180度,别的一种是笔直翻转,而从结果来看,咱们展现的小猫咪是笔直翻转了。

【Android音视频】OpenGL ES翻转拉伸问题

因为在默认状况下,OpenGL ES 中的纹路坐标系的原点在左下角,而图画数据通常是从左上角开始存储的。因而,当加载图画数据到纹路中时,图画会在笔直方向上翻转。

【Android音视频】OpenGL ES翻转拉伸问题

图片拉伸

而图片被拉伸的原因是因为纹路坐标和极点坐标的映射不正确。咱们看一下代码中的极点着色器的shader代码。

private val VERTEX_SHADER_STRING = """
    #version 300 es
    precision mediump float;
    layout(location = 0) in vec4 position;
    layout(location = 1) in vec4 inputTextureCoordinate;
    uniform mat4 textureTransform;
    out vec2 textureCoordinate;
    void main()
    {
        gl_Position = position;
        textureCoordinate = (textureTransform * inputTextureCoordinate).xy;
    }    
""".trimIndent()
private const val glPositionId = 0

代码中的position入参能够用于调整极点的方位,它是一个四维变量,在前文中,我图方便,直接传入了填满整个NDC坐标系的四个极点,由所以展现图片,没有涉及到3D,所以只传入了x轴和y轴的值。

val FULL_RECTANGLE_BUF = floatArrayOf(
    -1.0f, -1.0f,  // Bottom left.
    1.0f, -1.0f,   // Bottom right.
    -1.0f, 1.0f,   // Top left.
    1.0f, 1.0f     // Top right.
).toFloatBuffer()
GLES30.glVertexAttribPointer(
    glPositionId, 2, GLES30.GL_FLOAT, false, 0, FULL_RECTANGLE_BUF
)

此刻上屏展现之后的效果便是将纹路中的内容拉伸填满至四角。

OpenGL以三个极点为单位制作内容,但是为什么这儿能够传入四个极点能正常显现。这个和制作API传入的参数GLES30.GL_TRIANGLE_STRIP有关。此处与拉伸的问题无关先不展开。

GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)

问题处理

处理图片翻转

还记得咱们在极点着色器传入了一个矩阵吗?

// 将矩阵传入着色器uniform变量
val glTexTransformId = GLES30.glGetUniformLocation(programId, "textureTransform")
GLES30.glUniformMatrix4fv(glTexTransformId, 1, false, identityMtx, 0)

而在极点着色器中将纹路坐标与矩阵相乘,取得实践烘托的方位,由于烘托图片是2D的,因而只取xy轴。

textureCoordinate = (textureTransform * inputTextureCoordinate).xy;

此刻咱们就能够对这个矩阵做手脚了,一开始运用的矩阵是一个单位矩阵,咱们在小学二年级的线性代数中背过矩阵相乘公式,证明这个矩阵与另一个矩阵相乘的结果是不变的。

val identityMtx = floatArrayOf(
    1.0f, 0.0f, 0.0f, 0.0f,
    0.0f, 1.0f, 0.0f, 0.0f,
    0.0f, 0.0f, 1.0f, 0.0f,
    0.0f, 0.0f, 0.0f, 1.0f
)

看到这儿,其实处理方法很简略了,咱们能够将这个矩阵换成一个翻转的矩阵。所以我运用Matrix东西类的函数来做缩放操作,将y轴缩到-1。

Matrix.scaleM(identityMtx, 0, 1f, -1f, 1f)

所以咱们就把图片翻到下面去了,咱们需要将这个图片挪回原位,像这样:

【Android音视频】OpenGL ES翻转拉伸问题

咱们仍是运用Matrix东西类来将矩阵调整一下:

Matrix.translateM(identityMtx, 0, 0f, -1f, 0f)

翻转问题搞好了,接下来处理拉伸问题。

处理图片拉伸

处理拉伸的方式有很多种,这儿就简略评论两种,注意:下面处理方式只针对本案例,而在实践运用需要考虑更多状况。

修正极点

依据肉眼调查,是在竖直方向拉长了,此刻咱们就将极点坐标往里缩缩就好啦。

【Android音视频】OpenGL ES翻转拉伸问题

val ratio = (bitmap.width).toFloat() / (bitmap.height).toFloat()
val rectangleBuf = floatArrayOf(
    -1F, -1F * ratio,
    1F, -1F * ratio,
    -1F, 1F * ratio,
    1.0F, 1F * ratio
).toFloatBuffer()
GLES30.glVertexAttribPointer(
    glPositionId, 2, GLES30.GL_FLOAT, false, 0, rectangleBuf
)

将极点的y轴往里缩缩就好啦

修正制作ViewPort

那假如我不想改极点,也能够在制作的时候修正制作的ViewPort。

val viewPortWidth: Int
val viewPortHeight: Int
if (width > height) {
    viewPortWidth = height * bitmap.width / bitmap.height
    viewPortHeight = height
} else {
    viewPortWidth = width
    viewPortHeight = width * bitmap.height / bitmap.width
}
val y = (height - viewPortHeight) / 2
val x = (width - viewPortWidth) / 2
// 设置ViewPort
GLES30.glViewport(x, y, viewPortWidth, viewPortHeight)
// 制作纹路
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)

第一步首先获取制作ViewPort的巨细。由于图片的份额和咱们屏幕份额不相同嘛,所以咱们就将制作的份额调整成和图片的份额相同,再算出屏幕最大能够制作的宽高像素。

第二部就需要调整制作ViewPort的方位,也便是x,y轴,将制作区域居中。

如下图所示蓝色框框便是ViewPort方位:

【Android音视频】OpenGL ES翻转拉伸问题

总结

到这儿,猫咪照片就被摆正了,我们也能够拉一下文章开头的代码跑一下看看,挺好玩的。这个OpenGL ES小案例就告一段落了,之后便是真正关于Android音视频的内容了。水平有限,因而写的比较少,引荐我们看看下列参阅文章。

参阅

矩阵改换:

  • /post/684490…
  • /post/684490…

纹路翻转问题:

  • /post/685457…

着色器

  • xcsf.github.io/blog/2020/0…