咱們如今至關熟悉這樣一個事實,在OpenGL裏,咱們要渲染的一切物體都要映射到X軸和Y軸上[-1,1]的範圍內,對於Z軸也同樣。這個範圍內的座標被稱爲歸一化設備座標,其獨立於屏幕實際尺寸或形狀。android
不幸的是,由於它們獨立於實際的屏幕尺寸,若是直接使用它們,咱們就會遇到問題,例如在橫屏模式下被壓扁的桌子。數組
假設實際的設備分辨率以像素爲單位是1280*720,這在新的Android設備上是一個經常使用的分辨率。爲了使討論更加容易,讓咱們也暫時假定OpenGL佔用整個顯示屏。spa
若是設備是在豎屏模式下,那麼[-1,1]的範圍對應1280像素高,卻只有720像素寬。圖像會在X軸顯得扁平,若是在橫屏模式,一樣的問題也會發生在Y軸上。翻譯
歸一化設備座標假定座標空間是一個正方形,以下圖所示:orm
然而,由於實際的視口可能不是一個正方形,圖像就會在一個方向上被拉伸,在另外一個方向上被壓扁。在一個豎屏設備上,歸一化設備座標上定義的圖像看上去就是在水平方向上被壓扁了:遊戲
在橫屏模式下,一樣的圖像就在另外一個方向上看起來被壓扁的。it
咱們須要調整座標空間,以使它把屏幕的形狀考慮在內,可行的一個方法是把較小的範圍固定在[-1,1]內,而按屏幕尺寸的比例調整較大的範圍。io
舉例來講,在豎屏狀況下,其寬度是720,而高度是1280,所以咱們能夠把寬度範圍限定在[-1,1],並把高度範圍調整爲[-1280/720,1280/720]或[-1.78,1.78]。同理在橫屏模式狀況下,把高度範圍設爲[-1.78,1.78],而把高度範圍設爲[-1,1]。form
經過調整已有的座標空間,最終會改變咱們可用的空間。基礎
經過這個方法,不管是豎屏模式仍是橫屏模式,物體看起來就都同樣了。
調整座標空間,以便咱們把屏幕方向考慮進來,咱們須要中止直接在歸一化設備座標上工做,遙開始在虛擬座標空間裏工做。接下來,咱們須要找到某種能夠把虛擬空間座標轉化迴歸依化設備座標的方法,讓OpenGL能夠正確的渲染它們。這種轉換應該把屏幕方向計算在內,以使圖像在豎屏模式和橫屏模式看上去都同樣。
咱們想要進行的操做叫做正交投影。使用正交投影,無論多遠或者多近,全部物體看上去大小老是相同的。爲了更高的理解這種投影是作什麼的,想象一下,在咱們的場景中有一個火車軌道,直接從空中俯瞰,這些軌道看起來是這樣的:
還有一種特殊類型的正交投影,被稱爲等軸測投影,它是從側角觀察一種正交投影。正如在一些城市模擬和策略遊戲中看到的,這種類型的投影能用來從新建立一個經典的三維角。
當咱們使用正交投影把虛擬座標變換回歸化設備座標時,實際上定義了三維世界內部的一個區域。在這個區域內的全部東西都會顯示在屏幕上,而區域外的全部東西都會被剪裁掉。
利用正交投影矩陣改變立方體的大小,以使咱們能夠在屏幕上看到或多或少的場景。咱們也能改變立方體的形狀彌補屏幕的寬高比的影響。
OpenGL大量使用了向量和矩陣,矩陣的最重要的用途之一就是創建正交和透視投影。其緣由之一是,從本質上來講,使用矩陣作投影只涉及對一組數據按順序執行大量的加法和乘法,這些運算在現代GPU上執行的很是快。
一個向量是一個有多個元素的一維數組。在OpenGL裏,一個位置一般是一個四元素向量,顏色也同樣。咱們使用的大多數向量通常都有四個元素。在下面的例子中, 咱們可看到一個位置向量,它有一個X,一個Y,一個Z,一個W份量。
一個矩陣是一個有多個元素的二維數組。在OpenGL裏,咱們通常使用矩陣做向量投影,如正交或者透視投影,而且也用它們旋轉物體,平移物體以及縮放物體。咱們把矩陣與每一個要變換的向量相乘可實現這些變換。下面就是一個矩陣:
要讓矩陣乘以一個向量,咱們把矩陣放在左邊,向量放在右邊。以下:
規則就是矩陣第一行乘以向量第一列,以第一行爲例:矩陣第一行第一個元素乘以向量第一列第一個元素,加上矩陣第一行第二個元素乘以向量第一列第二個元素,加上矩陣第一行第三個元素乘以向量第一列第三個元素,加上矩陣第一行第四個元素乘以向量第一列第四個元素。矩陣二,三,四行同理。
單位矩陣以下:
之因此被稱爲單位矩陣,是由於這個矩陣乘以任何向量老是獲得與原來相同的向量。就像把任何數字乘以1會獲得原來的數字同樣。
既然理解了單位矩陣,讓咱們看一個很是簡單的矩陣類型---平移矩陣。它在OpenGL裏十分經常使用。使用這種類型的矩陣,咱們能夠把一個物體沿着指定的距離移動。這個矩陣和單位矩陣差很少,但在右側指定了三個額外的元素:
讓咱們盾一個位置(2,2)的例子,這個位置Z默認是0,W默認是1.咱們把這個向量沿X軸平移3,沿Y軸也平移3,所以,把Xtranslation賦值爲3,Ytranslation賦值爲3。結果以下:
這個位置正是咱們所指望和(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座標的負值會愈來愈小。之因此這樣徹底是歷史和傳統的緣由。
爲了更好的理解Z軸問題,咱們須要理解左手座標系統與右手座標系統之間的區別。想知道一個座標系統是左手的仍是右手的,你拿出一隻手,把大拇指指向X軸正值方向,而後把食指指向Y軸正值方向。
如今,把你的中指指向Z軸。若是你須要用左手作這些,那你看到的就是一個左手座標系統;若是你須要用右手,那你看到的就是一個右手座標系統。把你的中指指向Z軸,記住要把大拇指指向X軸方向,食指指向Y軸正值方向。以下圖:
其實左手仍是右手選擇都不要緊,只是一個簡單的轉換。歸一化設備座標使用的是左手座標系統,而在OpenGL的早期版本,默認使用的確實右手座標系統,其使用Z的負值增長表示距離增長。這就是爲何Android的Matrix會默認生成反轉Z的矩陣。
修改前一章的頂點着色器中的代碼以下:
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;
咱們沒有傳遞數組中定義的位置,而是傳遞那個位置與一個矩陣的乘積。它意味着頂點數組不用再被翻譯爲歸一化設備的座標了,其將被理解爲存在於這個矩陣所定義的虛擬座標空間中。這個矩陣會把座標從虛擬座標空間變化迴歸一化設備座標。
打開上一節的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);
更新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。
咱們首先計算了寬高比,它使用寬和高中的較大值除以寬和高的較小值。不論是豎屏仍是橫屏,這個值都同樣。
在LYJRenderer中的onDrawFrame()中,咱們在glClear()調用以後加入以下代碼:
GLES20.glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);