Android OpenGL ES(六)----進入三維在代碼中建立投影矩陣和旋轉矩陣

咱們如今準備好在代碼中添加透視投影了。Android的Matrix類爲它準備了兩個方法------frustumM()和perspectiveM()。不幸的是,frustumM()的個缺陷,它會影響某些類型的投影,而perspectiveM()只是從Android的ICS版本開始才被引入,在早期的Android版本里並無這個方法。咱們能夠簡單地支持ICS及其以上的版本,可是這樣會丟掉很大一部分市場,一些用戶依然運行早期的Android版本。數組


做爲替代,咱們能夠建立咱們本身的方法來實現投影矩陣。函數



1.建立本身的perspectiveM


 

在工具包中建立新的類MatrixHelper,在開始處加入以下方法簽名:工具

 

public static void perspectiveM(float[] m,float yFovInDegress,float aspect,float n,float f){測試


計算焦距編碼


咱們要作的第一件事就是計算焦距,這將基於在Y軸上的視野。就在方法簽名以後加入以下代碼:spa

 

final float angleInRadians=(float)(yFovInDegress*Math.PI/180.0);
final float a=(float)(1.0/Math.tan(angleInRadians/2.0));
ip

 

咱們使用Java的Math類計算那個正切函數,由於它須要弧度角,因此咱們把視野從度轉換爲弧度。接着計算焦距。數學


輸出矩陣it


咱們如今能夠更具上一節的數學證實直接寫出矩陣,增長以下代碼:io

 

m[0]=a/aspect;
m[1]=0f;
m[2]=0f;
m[3]=0f;


m[4]=0f;
m[5]=a;
m[6]=0f;
m[7]=0f;


m[8]=0f;
m[9]=0f;
m[10]=-((f+n)/(f-n));
m[11]=-1f;


m[12]=0f;
m[13]=0f;
m[14]=-((2f*f*n)/(f-n));
m[15]=0f;


這就是把矩陣數據存到了參數m定義的浮點數組中,這個數組須要至少16個元素。OpenGL把矩陣數據按照以列爲主的順序存儲,這就意味着咱們一次寫一列數據,而不是一次寫一行。前四個值是第一列,下一組四個數是第二列,以此類推。



2.開始使用投影矩陣



咱們如今將轉而使用那個透視投影矩陣了。打開你的渲染類,並從onSurfaceChanged()去掉全部的代碼,只保留glViewPort()調用,加入以下代碼:


MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f);


這會用45度的視野建立一個透視投影。 這個視椎體從Z值爲-1的位置開始,在Z值爲-10的位置結束。


加入MatrixHelper的導入後,繼續前面的程序運行,你能夠會發現桌面不見了,由於咱們沒有給桌子指定Z的位置,默認狀況下Z在爲0的位置。由於這個視椎體是從Z值爲-1的位置開始的,除非把它移動到那個距離內,不然咱們沒法看到桌子。


不要硬編碼Z的值,在使用投影矩陣進行投影以前,讓咱們使用一個平移矩陣把桌子移出來。依照慣例,咱們把這個矩陣稱爲模型矩陣。


利用模型矩陣移動物體


在類的頂部加入以下矩陣的定義:


private final float[] modelMatrix=new float[16];


咱們要使用這個矩陣把桌面移動到那個距離內。在onSurfaceChanged()結尾處,加入以下代碼:


Matrix.setIdentityM(modelMatrix, 0);
Matrix.translateM(modelMatrix,0,0f,0f,-2f);


這就是把模型矩陣設爲單位矩陣,在沿着Z軸平移-2。當咱們把桌面的座標與這個矩陣相乘的時候,那些座標最終會沿着Z軸負方向移動2個單位。


相乘一次仍是相乘兩次


咱們如今要作一個選擇:咱們依然須要把這個矩陣應用於每一個頂點,所以第一個選項是給頂點着色器新增一個額外的矩陣。咱們把每一個頂點都與這個模型矩陣相乘,讓它們沿着Z軸負方向移動2個單位,接下來把每一個頂點與投影矩陣相乘。這樣,OpenGL就能夠作透視除法,並把這些頂點變換到歸一化設備座標了。


若是不想這麼麻煩,還有一個更好的方式:咱們能夠把模型矩陣與投影矩陣相乘,獲得一個矩陣,而後把這個矩陣傳遞給頂點着色器。經過這種方式咱們就能夠在着色器中僅保留一個矩陣。


選擇適當的順序


爲了弄清楚咱們應該使用那種順序,讓咱們看一下只使用投影矩陣的數學運算:



VerteXeye表明場景中的頂點與投影矩陣相乘以前的位置。咱們一旦加入模型矩陣來移動那個桌子,這個數學運算看起來就像這樣。



VerteXmodel表明頂點在模型矩陣放進場景中以前的位置。把這兩個表達式合併在一塊兒,最後獲得的公式以下:



了使用一個矩陣替換這兩個矩陣,咱們不得不把投影矩陣乘以模型矩陣,就是把投影矩陣放在左邊,把模型矩陣放在右邊。


更新代碼使用一個矩陣


讓咱們把這個新的矩陣代碼封裝一下,把下面的代碼加到onSurfaceChanged()中的translate()調用後面:


final float[] temp=new float[16];
Matrix.multiplyMM(temp,0,projectionMatrix,0,modelMatrix,0);
System.arraycopy(temp, 0, projectionMatrix, 0, temp.length);


不論何時把兩個矩陣相乘,都須要一個臨時變量來存儲其結果。若是嘗試直接寫入這個結果,這個結果將是未定義的。


咱們首先建立了一個臨時的浮點數組用來存儲其臨時結果;而後調用multiplyMM()把投影矩陣和模型矩陣相乘,其結果存進這個臨時數組。下一步,咱們調用System.arraycopy()把結果存回projectMatrix,它如今包含模型矩陣與投影矩陣的組合效應。


若是咱們如今運行這個程序,會發現這依然是個平面的桌面。



3.增長旋轉



既然咱們已經有一個配置好的投影矩陣和一個能夠移動的桌子的模型矩陣,那麼咱們須要作的就是旋轉這張桌子,以即可以從某個角度觀察它。若是使用旋轉矩陣,咱們只須要一行代碼就能夠作到。咱們還歷來沒有用過旋轉,如今花一些時間瞭解一下這些旋轉是怎麼工做的。


須要弄清楚一件時是咱們須要圍繞哪一個軸旋轉以及旋轉多少度。讓咱們再看下圖:



搞清楚一個物體是怎麼圍繞一個給定的軸旋轉,咱們將使用右手座標規則:伸出你的右手,握拳,讓大拇指指向正軸方向。假若是一個正角度的旋轉,捲曲的手指會告訴你一個物體是怎麼圍繞那個軸旋轉的。觀察上圖,當你把大拇指指向X軸正方向時,看看旋轉的方向是怎麼跟隨手指的繞軸線捲曲的。

 

分別用X軸,Y軸和Z軸試一下這個旋轉。若是繞Y軸旋轉,桌子會繞着它的頂端和低端水平旋轉。若是繞着Z軸旋轉,桌子會在一個圓圈內旋轉。咱們想要作的是讓桌子繞着X軸向後旋轉,由於這會讓桌子看起來更有層次。


旋轉矩陣


咱們將使用一個旋轉矩陣去作實際的旋轉。旋轉矩陣使用正弦和餘弦三角函數把旋轉轉換成縮放因子。下面就是繞X軸旋轉所用矩陣定義:



而後是繞Y軸旋轉所用的矩陣:



最後,還有一個繞Z軸旋轉所用的矩陣:



全部矩陣合併爲一個通用的旋轉矩陣,使其能夠基於任意一個角度和向量旋轉,這也是可能的。


做爲一個測試,讓咱們試試繞着X軸旋轉。咱們從一個點開始,它在原點上面一個單位,也就是Y值是1。把它繞X軸旋轉90度。首先,讓咱們準備這個旋轉矩陣:



把這個矩陣與這個點的位置向量相乘,看看獲得了什麼:



個點從(0,1,0)被移動到了(0,0,1)。若是咱們回過頭來看一下上面那個旋轉座標軸,並對X軸使用右手規則,咱們能夠看到正向旋轉是如何把一個點沿着一個繞X軸的圈移動的。



4.在代碼中加入旋轉



咱們如今準備好把這個旋轉加入代碼了。回到onSurfaceChanged(),調整那個平移矩陣,並加入一個旋轉矩陣,以下:


Matrix.translateM(modelMatrix,0,0f,0f,-2.5f);
Matrix.rotateM(modelMatrix,0,-60f,1f,0f,0f);


咱們把這張桌子放得更遠了一點,由於咱們一旦把它旋轉了,它的底部會距離咱們更近。咱們接着把它繞X軸旋轉-60度,這會讓桌子處於一個很好的角度,就像咱們站在它前面同樣。

 

這張桌子如今看起來應該以下圖所示:


相關文章
相關標籤/搜索