5.正交投影
要定义正交投影,我们将使用Android的Matrix类,它在android.opengl包中。这个类有一个称为orthoM()的方法,它可以为我们生成一个正交投影。
我们来看一下orthoM()参数:
orthom(float[] m,int mOffset,float left,float rigth,float bottom,float top,float near,float far)
float[] m:目标数组,这个数组长度至少有16个元素,这样它才能存储正交投影矩阵。
int mOffset:结果矩阵起始的偏移值。
float left:X轴的最小范围。
float right:X轴的最大范围。
float bottom:Y轴的最小范围。
float top:Y轴的最大范围。
float near:Z轴的最小范围。
float far:Z轴的最大范围。
当我们调用这个方法的时候,它应该产生下面的正交投影矩阵:
这个正交投影矩阵会把所有在左右之间,上下之间和远近之间的事物映射到归一化设备坐标中从-1到1的范围,在这个范围内所有事物在屏幕上都是可见的。
主要的区别就是Z轴有一个负值符号,它的效果是反转Z坐标。这就意味着,物体离得越远,Z坐标的负值会越来越小。之所以这样完全是历史和传统的原因。
6.左手与右手坐标系统
为了更好的理解Z轴问题,我们需要理解左手坐标系统与右手坐标系统之间的区别。想知道一个坐标系统是左手的还是右手的,你拿出一只手,把大拇指指向X轴正值方向,然后把食指指向Y轴正值方向。
现在,把你的中指指向Z轴。如果你需要用左手做这些,那你看到的就是一个左手坐标系统;如果你需要用右手,那你看到的就是一个右手坐标系统。把你的中指指向Z轴,记住要把大拇指指向X轴方向,食指指向Y轴正值方向。如下图:
其实左手还是右手选择都没关系,只是一个简单的转换。归一化设备坐标使用的是左手坐标系统,而在OpenGL的早期版本,默认使用的确实右手坐标系统,其使用Z的负值增加表示距离增加。这就是为什么Android的Matrix会默认生成反转Z的矩阵。
7.更新程序
7.1更新着色器
修改前一章的顶点着色器中的代码如下:
uniform mat4 u_Matrix; attribute vec4 a_Position; attribute vec4 a_Color; varying vec4 v_Color; void main() { v_Color=a_Color; gl_Position = u_Matrix*a_Position; gl_PointSize = 10.0; }
我们这里添加了一个新的uniform定义的“u_Matrix”,并把它定义为一个mat4类型,意思是这个uniform代表一个4*4的矩阵。更新位置负值那一行:
gl_Position =u_Matrix*a_Position;
我们没有传递数组中定义的位置,而是传递那个位置与一个矩阵的乘积。它意味着顶点数组不用再被翻译为归一化设备的坐标了,其将被理解为存在于这个矩阵所定义的虚拟坐标空间中。这个矩阵会把坐标从虚拟坐标空间变化回归一化设备坐标。
7.2添加矩阵数组和一个新的uniform
打开上一节的LYJRenderer添加成员变量:
private static final String U_MATRIX="u_Matrix";
我们还需要一个顶点数组存储矩阵:
private final float[] projectionMatrix=new float[16];
我们也需要一个整型值用于保存那个矩阵uniform的位置:
private int uMatrixLocation;
然后我们只需要在onSurfaceCreated()中加入如下代码:
uMatrixLocation=GLES20.glGetUniformLocation(program,U_MATRIX);
7.3创建正交投影矩阵
更新onSurfaceChanged(),在GLES20.glViewport()调用后面加入如下代码:
final float aspectRatio=width>height?(float)width/(float)heigth:(float)height/(float)width; if(width>height){ orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,-1f,1f); }else{ orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,-1f,1f); }
这段代码会创建一个正交投影矩阵,这个矩阵会把屏幕的当前方向计算在内。注意在Android中不只有一个Matrix类,因此你要确保导入了android.opengl.Matrix。
我们首先计算了宽高比,它使用宽和高中的较大值除以宽和高的较小值。不管是竖屏还是横屏,这个值都一样。
7.4传递矩阵给着色器
在LYJRenderer中的onDrawFrame()中,我们在glClear()调用之后加入如下代码:
GLES20.glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);