OpenGL ES使用正交投影来解决图像变形的问题
一、概述
上一节实践了,通过改变GLSurfaceView的宽高来解决图像变形的问题。
本节将通过正交投影的方式解决图像变形的问题。
分三步:
1.计算屏幕的宽高比及图像的宽高比
var screenRatio = screenWidth.toFloat() / screenHeight var imgRatio = imageWidth.toFloat() / imageHeight
2.比较屏幕宽高比及图像宽高比,并设置正交投影矩阵的参数(left、right、top、bottom、near、far)
if (imgRatio > screenRatio) { var aspectRatio = imgRatio / screenRatio//计算出变换矩阵底和高 Matrix.orthoM( projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f ) } else { var aspectRatio = screenRatio / imgRatio//计算出变换矩阵的左右 Matrix.orthoM( projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f ) }
3.将投影矩阵应用到顶点着色器的顶点上。
var uOrthoMLocation = GLES30.glGetUniformLocation(programId, "uOrthM") GLES30.glUniformMatrix4fv(uOrthoMLocation, 1, false, projectionMatrix, 0)
4.顶点着色器字符串写法
attribute vec3 vPosition; attribute vec2 vCoord; varying vec2 aCoord; uniform mat4 uOrthM; void main(){ gl_Position = uOrthM*vec4(vPosition,1.0f);//利用正交投影矩阵改变图片变形问题 aCoord = vCoord; }
二、完整代码如下
1.计算投影矩阵
fun getProjectionMatrix( screenWidth: Int, screenHeight: Int, imageWidth: Int, imageHeight: Int ): FloatArray { var projectionMatrix = FloatArray(4 * 4) Matrix.setIdentityM(projectionMatrix, 0)//设置成单位矩阵 var screenRatio = screenWidth.toFloat() / screenHeight var imgRatio = imageWidth.toFloat() / imageHeight if (imgRatio > screenRatio) { var aspectRatio = imgRatio / screenRatio//计算出变换矩阵底和高 Matrix.orthoM( projectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f ) } else { var aspectRatio = screenRatio / imgRatio//计算出变换矩阵的左右 Matrix.orthoM( projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f ) } return projectionMatrix }
2.应用投影矩阵的shader
package com.yw.filter.shader import android.content.Context import android.graphics.Bitmap import android.opengl.GLES30 import android.opengl.Matrix import com.yw.filter.R import java.nio.ByteBuffer import java.nio.ByteOrder import java.nio.FloatBuffer import java.nio.IntBuffer /** * 利用正交投影结局图片变形问题 */ class OrthoMTextureShader(var context: Context, var bitmapSrc: Bitmap) : BaseShader() { private var vertices = floatArrayOf(//在android上纹理坐标需要上下颠倒之后才能够正确显示,否则会出现镜像、颠倒等问题 // ---- 位置 ---- - 纹理坐标 - 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右上 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, // 右下 -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, // 左下 -1.0f, 1.0f, 0.0f, 0.0f, 0.0f // 左上 ) private var indices = intArrayOf( 0, 1, 3,//第一个三角形 1, 2, 3 // 第二个三角形 ) private var vertexBuffer: FloatBuffer? = null private var indicesBuffer: IntBuffer? = null private var projectionMatrix = FloatArray(4 * 4)//正交投影矩阵 private var VAO = IntArray(1) var textureId: Int = 0 override fun onSurfaceCreate() { //分配空间填充数据 vertexBuffer = ByteBuffer .allocateDirect(vertices.size * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() vertexBuffer?.put(vertices) vertexBuffer?.position(0) indicesBuffer = ByteBuffer .allocateDirect(indices.size * 4) .order(ByteOrder.nativeOrder()) .asIntBuffer() indicesBuffer?.put(indices) indicesBuffer?.position(0) //创建并绑定VAO GLES30.glGenVertexArrays(1, VAO, 0) GLES30.glBindVertexArray(VAO[0]) //创建并绑定VBO var VBO = IntArray(1) GLES30.glGenBuffers(1, VBO, 0) GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, VBO[0]) GLES30.glBufferData( GLES30.GL_ARRAY_BUFFER, vertices.size * 4, vertexBuffer, GLES30.GL_STATIC_DRAW ) //创建EBO var EBO = IntArray(1) GLES30.glGenBuffers(1, EBO, 0) GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, EBO[0]) GLES30.glBufferData( GLES30.GL_ELEMENT_ARRAY_BUFFER, indices.size * 4, indicesBuffer, GLES30.GL_STATIC_DRAW ) //告知显卡如何解析顶点数据 GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 4 * 5, 0) GLES30.glEnableVertexAttribArray(0) //告知显卡如何解析纹理数据 GLES30.glVertexAttribPointer(1, 2, GLES30.GL_FLOAT, false, 4 * 5, 4 * 3) GLES30.glEnableVertexAttribArray(1) //销毁 GLES30.glBindVertexArray(0) GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0) GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0) textureId = getTextureId(bitmapSrc, GLES30.GL_RGBA) loadProgram(context, R.raw.image_ortho_vert, R.raw.image_ortho_frag) } override fun onDrawFrame() { GLES30.glClearColor(1.0f, 0.0f, 0.0f, 1.0f) GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT) GLES30.glBindVertexArray(VAO[0]) GLES30.glUseProgram(programId) var uOrthoMLocation = GLES30.glGetUniformLocation(programId, "uOrthM") GLES30.glUniformMatrix4fv(uOrthoMLocation, 1, false, projectionMatrix, 0) GLES30.glActiveTexture(GLES30.GL_TEXTURE0) GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId) var vTextureLocation = GLES30.glGetUniformLocation(programId, "vTexture") GLES30.glUniform1i(vTextureLocation, 0) GLES30.glDrawElements(GLES30.GL_TRIANGLES, 6, GLES30.GL_UNSIGNED_INT, 0) GLES30.glBindVertexArray(0) } override fun onSurfaceChanged(width: Int, height: Int) { GLES30.glViewport(0, 0, width, height) // projectionMatrix = getProjectionMatrix(width, height) projectionMatrix = getProjectionMatrix( width, height, bitmapSrc.width, bitmapSrc.height ) } }
3.把shader应用到GLSurfaceView的回到函数中就行了