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

前言


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

像下图这样:

本文就分析一下为啥会这样,然后将它摆正。

有兴趣可以拷一份代码一起看看:github.com/MReP1/OpenG…

1、分析原因


1.1、猫咪翻转

倒过来也分两种情况,一种是旋转180度,另外一种是垂直翻转,而从结果来看,我们展示的小猫咪是垂直翻转了。

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

1.2、图片拉伸

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

privatevalVERTEX_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()
privateconstvalglPositionId=0

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

valFULL_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)

2、问题解决


2.1、解决图片翻转

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

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

而在顶点着色器中将纹理坐标与矩阵相乘,获得实际渲染的位置,由于渲染图片是2D的,因此只取xy轴。

textureCoordinate= (textureTransform*inputTextureCoordinate).xy;

此时我们就可以对这个矩阵做手脚了,一开始使用的矩阵是一个单位矩阵,我们在小学二年级的线性代数中背过矩阵相乘公式,证实这个矩阵与另一个矩阵相乘的结果是不变的。

validentityMtx=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)

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

我们还是使用Matrix工具类来将矩阵调整一下:

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

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

【文章福利】小编整理了一些音视频学习资料包、大厂面试题、技术视频和学习路线图,包括(C/C++,Linux,FFmpegwebRTCrtmp hlsrtsp ffplay srs 等等资料)有需要的可以点击994289133加群免费领取哦~

3、解决图片拉伸

解决拉伸的方式有很多种,这里就简单讨论两种,注意:下面解决方式只针对本案例,而在实际使用需要考虑更多情况。

3.1、修改顶点

根据肉眼观察,是在竖直方向拉长了,此时我们就将顶点坐标往里缩缩就好啦。

valratio= (bitmap.width).toFloat() / (bitmap.height).toFloat()
valrectangleBuf=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轴往里缩缩就好啦

3.2、修改绘制ViewPort

那如果我不想改顶点,也可以在绘制的时候修改绘制的ViewPort。

valviewPortWidth: Int
valviewPortHeight: Int
if (width>height) {
    viewPortWidth=height*bitmap.width/bitmap.height
    viewPortHeight=height
} else {
    viewPortWidth=width
    viewPortHeight=width*bitmap.height/bitmap.width
}
valy= (height-viewPortHeight) /2
valx= (width-viewPortWidth) /2
// 设置ViewPort
GLES30.glViewport(x, y, viewPortWidth, viewPortHeight)
// 绘制纹理
GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)

第一步首先获取绘制ViewPort的大小。由于图片的比例和我们屏幕比例不一样嘛,于是我们就将绘制的比例调整成和图片的比例一样,再算出屏幕最大能够绘制的宽高像素。

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

如下图所示蓝色框框就是ViewPort位置:

4、总结


到这里,猫咪照片就被摆正了,大家也可以拉一下文章开头的代码跑一下看看,挺好玩的。

这个OpenGL ES小案例就告一段落了,之后就是真正关于Android音视频的内容了。

水平有限,因此写的比较少,推荐大家看看下列参考文章。

参考


矩阵变换:

纹理翻转问题:

着色器

猜你喜欢

转载自blog.csdn.net/2201_76108770/article/details/129011162