Android OpenGL基础知识之正交投影和透视投影的理解

OpenGL 在观察空间转换到裁剪空间时,需要用到投影矩阵。而在着色器脚本中,也需要提供一个投影矩阵给对应的 u_ProjectionMatrix变量。对于初学者来说,正交投影矩阵和透视投影矩阵比较难以理解,通过本文来带你彻底理解一下这两个投影矩阵。

首先要在程序里绑定到对应的变量,然后再给变量赋值。

    // 绑定到着色器脚本中的对应变量
private int uProMatrixLocation;
uProMatrixLocation = glGetUniformLocation(mProgram,U_ProMatrix);
// 给变量赋值,projectionMatrix 为投影矩阵
glUniformMatrix4fv(uProMatrixLocation,1,false,projectionMatrix,0)

正如前面讲到的,投影矩阵会创建一个视景体对物体坐标进行裁剪,得到的裁剪坐标再经过透视除法之后,就会得到归一化设备坐标。归一化设备坐标再经过视口转换,最终将坐标映射到了屏幕上。
    而 OpenGL 提供了两种投影方式:正交投影和透视投影。
Android OpenGL基础知识之正交投影和透视投影的理解_第1张图片
而不管是正交投影还是透视投影,最终都是将视景体内的物体投影在近平面上,这也是 3D 坐标转换到 2D 坐标的关键一步

在用opengl绘制一张图片的时候经常会遇到图片被拉伸的问题,为了解决该问题,关键就是让近平面的宽高比和视口宽高比保持一致,并且以后较短的一边作为 1 的标准,让图像保持居中。
OpenGL 提供了 Matrix.orthoM 函数来生成正交投影矩阵

/**
    * Computes an orthographic projection matrix.
    *
    * @param m returns the result 正交投影矩阵
    * @param mOffset 偏移量,默认为 0 ,不偏移
    * @param left 左平面距离
    * @param right 右平面距离
    * @param bottom 下平面距离
    * @param top 上平面距离
    * @param near 近平面距离
    * @param far 远平面距离
    */
   public static void orthoM(float[] m, int mOffset,
       float left, float right, float bottom, float top,
       float near, float far)

看下面的图片可以加深你的理解
Android OpenGL基础知识之正交投影和透视投影的理解_第2张图片
需要注意的是,我们的左、上、右、下距离都是相对于近平面中心的。

近平面的坐标原点位于中心,向右为 X 轴正方向,向上为 Y 轴正方向,所以我们的 left、bottom 要为负数,而 right、top 要为正数。同时,近平面和远平面的距离都是指相对于视点的距离,所以 near、far 要为正数,而且 far > near。

可以在 GLSurfaceView 的 surfaceChanged 里面来设定正交投影矩阵。

@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
     
        float aspectRatio = width > height ? (float) width / (float) height : (float) height / (float) width;
        if (width > height){
     
            Matrix.orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,0f,10f);
        }else {
     
            Matrix.orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,0f,10f);
        }
    }

这样的话,就把近平面的宽高比设定与视口的宽高比一致了。

透视投影矩阵

OpenGL 提供了两个函数来创建透视投影矩阵:frustumMperspectiveM

frustumM

frustumM 函数创建的视景体是一个锥形。
如图
Android OpenGL基础知识之正交投影和透视投影的理解_第3张图片
它的视景体有点类似于正交投影,在参数理解上基本都相同的。

/**
     * Defines a projection matrix in terms of six clip planes.
     *
     * @param m the float array that holds the output perspective matrix
     * @param offset the offset into float array m where the perspective
     *        matrix data is written
     * @param left 
     * @param right
     * @param bottom
     * @param top
     * @param near
     * @param far
     */
    public static void frustumM(float[] m, int offset,
            float left, float right, float bottom, float top,
            float near, float far)

需要注意的是 near 和 far 变量的值必须要大于 0 。因为它们都是相对于视点的距离,也就是照相机的距离。
当用视图矩阵确定了照相机的位置时,要确保物体距离视点的位置在 near 和 far 的区间范围内,否则就会看不到物体。
由于透视投影会产生近大远小的效果,当照相机位置不变,改变 near 的值时也会改变物体大小,near 越小,则离视点越近,相当于物体越远,那么显示的物体也就越小了。
当然也可以 near 和 far 的距离不动,改变摄像机的位置来改变观察到的物体大小。

你可能感兴趣的:(OpenGL,android)