Android OpenGL ES(四)----调整屏幕的宽高比

1.宽高比问题


咱们如今至关熟悉这样一个事实,在OpenGL里,咱们要渲染的一切物体都要映射到X轴和Y轴上[-1,1]的范围内,对于Z轴也同样。这个范围内的坐标被称为归一化设备坐标,其独立于屏幕实际尺寸或形状。android


不幸的是,由于它们独立于实际的屏幕尺寸,若是直接使用它们,咱们就会遇到问题,例如在横屏模式下被压扁的桌子。数组


 

假设实际的设备分辨率以像素为单位是1280*720,这在新的Android设备上是一个经常使用的分辨率。为了使讨论更加容易,让咱们也暂时假定OpenGL占用整个显示屏。spa


 

若是设备是在竖屏模式下,那么[-1,1]的范围对应1280像素高,却只有720像素宽。图像会在X轴显得扁平,若是在横屏模式,一样的问题也会发生在Y轴上。翻译


 

归一化设备坐标假定坐标空间是一个正方形,以下图所示:orm



然而,由于实际的视口可能不是一个正方形,图像就会在一个方向上被拉伸,在另外一个方向上被压扁。在一个竖屏设备上,归一化设备坐标上定义的图像看上去就是在水平方向上被压扁了:游戏


       


在横屏模式下,一样的图像就在另外一个方向上看起来被压扁的。it




2.适应宽高比


 

咱们须要调整坐标空间,以使它把屏幕的形状考虑在内,可行的一个方法是把较小的范围固定在[-1,1]内,而按屏幕尺寸的比例调整较大的范围。io

 

举例来讲,在竖屏状况下,其宽度是720,而高度是1280,所以咱们能够把宽度范围限定在[-1,1],并把高度范围调整为[-1280/720,1280/720]或[-1.78,1.78]。同理在横屏模式状况下,把高度范围设为[-1.78,1.78],而把高度范围设为[-1,1]。form


经过调整已有的坐标空间,最终会改变咱们可用的空间。基础


 

经过这个方法,不管是竖屏模式仍是横屏模式,物体看起来就都同样了。



3.使用虚拟坐标空间


 

调整坐标空间,以便咱们把屏幕方向考虑进来,咱们须要中止直接在归一化设备坐标上工做,遥开始在虚拟坐标空间里工做。接下来,咱们须要找到某种能够把虚拟空间坐标转化回归依化设备坐标的方法,让OpenGL能够正确的渲染它们。这种转换应该把屏幕方向计算在内,以使图像在竖屏模式和横屏模式看上去都同样。


咱们想要进行的操做叫做正交投影。使用正交投影,无论多远或者多近,全部物体看上去大小老是相同的。为了更高的理解这种投影是作什么的,想象一下,在咱们的场景中有一个火车轨道,直接从空中俯瞰,这些轨道看起来是这样的:




还有一种特殊类型的正交投影,被称为等轴测投影,它是从侧角观察一种正交投影。正如在一些城市模拟和策略游戏中看到的,这种类型的投影能用来从新建立一个经典的三维角。





当咱们使用正交投影把虚拟坐标变换回归化设备坐标时,实际上定义了三维世界内部的一个区域。在这个区域内的全部东西都会显示在屏幕上,而区域外的全部东西都会被剪裁掉。


利用正交投影矩阵改变立方体的大小,以使咱们能够在屏幕上看到或多或少的场景。咱们也能改变立方体的形状弥补屏幕的宽高比的影响。



4.线性代数基础



OpenGL大量使用了向量和矩阵,矩阵的最重要的用途之一就是创建正交和透视投影。其缘由之一是,从本质上来讲,使用矩阵作投影只涉及对一组数据按顺序执行大量的加法和乘法,这些运算在现代GPU上执行的很是快。

 


4.1向量



一个向量是一个有多个元素的一维数组。在OpenGL里,一个位置一般是一个四元素向量,颜色也同样。咱们使用的大多数向量通常都有四个元素。在下面的例子中, 咱们可看到一个位置向量,它有一个X,一个Y,一个Z,一个W份量。




4.2矩阵



一个矩阵是一个有多个元素的二维数组。在OpenGL里,咱们通常使用矩阵做向量投影,如正交或者透视投影,而且也用它们旋转物体,平移物体以及缩放物体。咱们把矩阵与每一个要变换的向量相乘可实现这些变换。下面就是一个矩阵:


 



4.3矩阵与向量的乘法



要让矩阵乘以一个向量,咱们把矩阵放在左边,向量放在右边。以下:


 



规则就是矩阵第一行乘以向量第一列,以第一行为例:矩阵第一行第一个元素乘以向量第一列第一个元素,加上矩阵第一行第二个元素乘以向量第一列第二个元素,加上矩阵第一行第三个元素乘以向量第一列第三个元素,加上矩阵第一行第四个元素乘以向量第一列第四个元素。矩阵二,三,四行同理。



4.4单位矩阵



单位矩阵以下:


 


因此被称为单位矩阵,是由于这个矩阵乘以任何向量老是获得与原来相同的向量。就像把任何数字乘以1会获得原来的数字同样。



4.5平移矩阵



 

既然理解了单位矩阵,让咱们看一个很是简单的矩阵类型---平移矩阵。它在OpenGL里十分经常使用。使用这种类型的矩阵,咱们能够把一个物体沿着指定的距离移动。这个矩阵和单位矩阵差很少,但在右侧指定了三个额外的元素:



 

让咱们盾一个位置(2,2)的例子,这个位置Z默认是0,W默认是1.咱们把这个向量沿X轴平移3,沿Y轴也平移3,所以,把Xtranslation赋值为3,Ytranslation赋值为3。结果以下:


 


个位置正是咱们所指望和(5,5)。



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);

相关文章
相关标签/搜索