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的回到函数中就行了

  

posted on 2024-09-09 16:51  飘杨......  阅读(108)  评论(0编辑  收藏  举报