学习OpenGL ES for Android(四)

在学习图形变换之前,可以先参考文档https://learnopengl-cn.github.io/01%20Getting%20started/07%20Transformations/学习基本概念。我们之前的绘制的都是静态的图像,如果我们使用改变顶点坐标的方式来让图像变换起来是非常麻烦的,而且会消耗更多的处理时间和性能。我们可以使用一个或多个矩阵(Matrix)对象可以更好的变换(Transform)一个物体。这些变换包括:移动,缩放,旋转。和桌面版不同的是,在Android中可以使用Matrix类来实现各种变换矩阵(注意是android.opengl.Matrix,不要和android.graphics包下的混淆了)。

multiplyMM(float[] result, int resultOffset, float[] lhs, int lhsOffset, float[] rhs, int rhsOffset):将两个4x4矩阵相乘,并将结果存储在第三个4x4矩阵中。以矩阵表示法表示:结果=lhs x rhs。

参数
result:保存结果的浮点数组
resultOffset:保存结果的浮点数组的偏移量
lhs:包含左侧矩阵的浮点数组
lhsOffset:左侧矩阵的浮点数组的偏移量
rhs:包含右侧矩阵的浮点数组
rhsOffset:右侧矩阵的浮点数组的偏移量

multiplyMV(float[] resultVec, int resultVecOffset, float[] lhsMat, int lhsMatOffset, float[] rhsVec, int rhsVecOffset):将4元素向量乘以4x4矩阵,并将结果存储在4元素列向量。以矩阵表示法表示:结果=lhs x rhs。

参数
resultVec:保存结果的浮点数组
resultVecOffset:保存结果的浮点数组的偏移量
lhsMat:包含左侧矩阵的浮点数组
lhsMatOffset:左侧矩阵的浮点数组的偏移量
rhsVec:包含右侧向量的浮点数组
rhsVecOffset:右侧向量浮点数组的偏移量

transposeM(float[] mTrans, int mTransOffset, float[] m, int mOffset):转置矩阵,将矩阵的主对角线翻转,交换矩阵的行索引与列索引。mTrans和m不能重叠。

参数
mTrans:保存结果的浮点数组
mTransOffset:保存结果的浮点数组的偏移量
m:输入的浮点数组
mOffset:输入的浮点数组的偏移量

invertM(float[] mInv, int mInvOffset, float[] m, int mOffset):逆矩阵,存在矩阵M以及矩阵N,假如M*N = 矩阵I(Identify Matrix单位矩阵),那么矩阵M和矩阵N互为逆矩阵。

参数
mInv:保存输出逆矩阵的数组
mInvOffset:输出逆矩阵的数组的偏移量
m:输入的浮点数组
mOffset:输入的浮点数组的偏移量

orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far):计算正射投影矩阵。关于正射投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/

参数
m:保存结果的浮点数组
mOffset:保存结果的数组中的偏移量
left,right,bottom,top:近平面的 左, 右, 下, 上 的值
near:近平面与视点之间的距离
far:远平面与视点之间的距离

frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far):根据六个值计算透视投影矩阵。关于透视投影投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/

参数
m:保存结果的浮点数组
mOffset:保存结果的数组中的偏移量
left,right,bottom,top:近平面的 左, 右, 下, 上 的值
near:近平面与视点之间的距离
far:远平面与视点之间的距离

perspectiveM(float[] m, int offset, float fovy, float aspect, float zNear, float zFar):根据计算透视投影矩阵。关于透视投影投影可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/08%20Coordinate%20Systems/

参数
m:保存结果的浮点数组
offset:保存结果的数组中的偏移量
fovy:y方向上的角度范围
aspect:窗口的宽高比
zNear:z方向上近平面
zFar:z方向上远平面

length(float x, float y, float z):计算向量的长度。

参数
x,y,z:向量的xyz的值

setIdentityM(float[] sm, int smOffset):设置单位矩阵。

参数
sm:保存结果的浮点数组
smOffset:保存结果的数组中的偏移量

scaleM(float[] sm, int smOffset, float[] m, int mOffset, float x, float y, float z):使用xyz向量来缩放矩阵m,把结果放到矩阵sm中。

参数
sm:保存结果的浮点数组
smOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值

scaleM(float[] m, int mOffset, float x, float y, float z):使用xyz向量来缩放矩阵m,把结果放到矩阵m中。

参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值

translateM(float[] tm, int tmOffset, float[] m, int mOffset, float x, float y, float z):使用xyz向量来位移矩阵m,把结果放到矩阵tm中。

参数
tm:保存结果的浮点数组
tmOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值

translateM( float[] m, int mOffset, float x, float y, float z):使用xyz向量来位移矩阵m,把结果放到矩阵m中。

参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值

rotateM(float[] rm, int rmOffset, float[] m, int mOffset, float a, float x, float y, float z):围绕xyz组成的轴来旋转矩阵m,旋转的角度是a,把结果放到矩阵rm中。

参数
rm:保存结果的浮点数组
rmOffset:保存结果的数组中的偏移量
m:源矩阵
mOffset:源矩阵的偏移量
a:旋转的角度
x,y,z:向量的xyz值

rotateM(float[] m, int mOffset, float a, float x, float y, float z):围绕xyz组成的轴来旋转矩阵m,旋转的角度是a,把结果放到矩阵m中。

参数
m:源矩阵
mOffset:源矩阵的偏移量
x,y,z:向量的xyz值

setRotateM(float[] rm, int rmOffset, float a, float x, float y, float z) :围绕xyz组成的轴旋转角度a创建一个矩阵m,把结果放到矩阵m中。

参数
rm:保存结果的浮点数组
rmOffset:结果矩阵的偏移量
a:旋转的角度
x,y,z:向量的xyz值

setRotateEulerM(float[] rm, int rmOffset, float x, float y, float z):根据欧拉角度生成矩阵rm。

参数
rm:结果矩阵
rmOffset:结果矩阵的偏移量
x,y,z:旋转的角度值

setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ):设置观察视角,参数包括观察点的作品,物体的中心点,视觉向量(朝向)。关于观察点可以参考文档https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/

参数
rm:结果矩阵
rmOffset:结果矩阵的偏移量
eyeX, eyeY, eyeZ:观察点坐标
centerX, enterY, centerZ:物品中心点
upX, pY, pZ:视觉向量

 我们先绘制一个三角形,顶点坐标如下

    private final float[] TriangleCoords = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };

我们知道在OpenGL的坐标范围都是1,而屏幕的宽高是不一样的,竖屏状态下三角形会比较高,而横屏下三角形会比较宽,如果我们想要使三角形变为正三角形就需要改变三角形宽高一致。有两种方式,一种是缩放,一种是正射投影。使用缩放的方式:在竖屏状态下,缩放y轴为宽高比;横屏时,缩放x轴为高宽比;使用正射投影:竖屏状态下,top和bottom分别为高宽比的正负值;横屏状态下,left和right分别为宽高比的正负值。原理上都是一样的,竖屏时缩放y轴,横屏时缩小x轴。

首先定义一个一个4x4的矩阵,然后在onSurfaceChanged时根据宽高来计算矩阵的值

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        super.onSurfaceChanged(gl, width, height);
        Matrix.setIdentityM(projectionMatrix, 0);

        float aspectRatio = width > height ?
                (float) width / (float) height :
                (float) height / (float) width;

        if (width > height) {
            // 横屏
            Matrix.scaleM(projectionMatrix, 0, 1 / aspectRatio, 1, 1);
            //Matrix.orthoM(projectionMatrix, 0, -aspectRatio, aspectRatio, -1f, 1f, -1f, 1f);
        } else {
            // 竖屏or正方形
            Matrix.scaleM(projectionMatrix, 0, 1, 1 / aspectRatio, 1);
            //Matrix.orthoM(mProjectionMatrix, 0, -1f, 1f, -aspectRatio, aspectRatio, -1f, 1f);
        }
    }

我们需要改变顶点着色器代码,定义顶点的坐标为变换矩阵和顶点的坐标相乘,代码如下

        vertexShaderCode =
                "uniform mat4 uMVPMatrix;" +
                        "attribute vec4 aPosition;" +
                        "void main() {" +
                        "  gl_Position = uMVPMatrix * aPosition;" +
                        "}";

然后再onDrawFrame传入我们计算后的矩阵

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);

        ……
        // 得到形状的变换矩阵的句柄
        int mMVPMatrixHandle = GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix");
        // 将投影和视图转换传递给着色器
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, projectionMatrix, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        GLES20.glDisableVertexAttribArray(positionHandle);

    }

显示效果如下

同样的道理,可以使用位移,旋转改变当前的三角。设置一直绘制,根据时间对三角形进行旋转和位移,可以看到一个不停位移和旋转的三角形,绘制的代码如下

@Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        Matrix.setIdentityM(rotationMatrix, 0);
        Matrix.setIdentityM(translateMatrix, 0);

        ……
        // 旋转从0到360循环旋转
        long time = SystemClock.uptimeMillis() % 4000L;
        float angle = 0.090f * ((int) time);
        Matrix.setRotateM(rotationMatrix, 0, angle, 0, 0, -1.0f);
        // 位移 在x轴上循环滑动
        float translate = (SystemClock.uptimeMillis() % 4000f) / 2000f;
        Matrix.translateM(translateMatrix, 0, translate <= 1 ? (translate - 0.5f) : (1 - (translate - 0.5f)), 0, 0);
        // 计算
        Matrix.multiplyMM(tempMatrix, 0, projectionMatrix, 0, translateMatrix, 0);
        // 计算
        Matrix.multiplyMM(vPMatrix, 0, tempMatrix, 0, rotationMatrix, 0);

        // 将视图转换传递给着色器
        GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, vPMatrix, 0);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        GLES20.glDisableVertexAttribArray(positionHandle);
    }

源码地址https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/window/TriangleMatrixView.java

可以自行运行代码查看效果。

发布了53 篇原创文章 · 获赞 17 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/jklwan/article/details/103490031