Android OpenGLES2.0等腰直角三角形和彩色的三角形(三)
上一篇博客中我們已經(jīng)繪制出了一個直角三角形,雖然我們相對于坐標(biāo),我們設(shè)置的直角三角形的兩腰是相等的,但是實際上展示出來的卻并不是這樣,雖然通過計算,我們可以把三角形的兩腰計算一下比例,使它們在坐標(biāo)上不等,但是現(xiàn)實出來相等,但是當(dāng)繪制的圖形比較復(fù)雜的話,這個工作量對我們來說實在太龐大了。那么我們怎么做呢?答案是,使用變換矩陣,把計算交給OpenGL。
矩陣
在數(shù)學(xué)中,矩陣(Matrix)是一個按照長方陣列排列的復(fù)數(shù)或?qū)崝?shù)集合 ,最早來自于方程組的系數(shù)及常數(shù)所構(gòu)成的方陣。這一概念由19世紀(jì)英國數(shù)學(xué)家凱利首先提出。
矩陣常被用于圖像處理、游戲開發(fā)、幾何光學(xué)、量子態(tài)的線性組合及電子學(xué)等多種領(lǐng)域。我們現(xiàn)在相當(dāng)于是在圖像處理或者游戲開發(fā)的領(lǐng)域來使用矩陣。在大學(xué)的高數(shù)課中,有學(xué)習(xí)到矩陣,很多人在大學(xué)學(xué)習(xí)高數(shù)、線性代數(shù)之類的課程時,總是覺得學(xué)習(xí)這些東西沒什么用(我也是)。實際上,這些這是對于程序員,尤其是需要做游戲開發(fā)、圖像視頻處理的程序員來說是非常重要的。扯偏了。。。
在三維圖形學(xué)中,一般使用的是4階矩陣。在DirectX中使用的是行向量,如[xyzw],所以與矩陣相乘時,向量在前矩陣在后。OpenGL中使用的是列向量,如[xyzx]T,所以與矩陣相乘時,矩陣在前,向量在后。關(guān)于矩陣的具體知識,博客中不詳細講解,需要了解的同學(xué)可以自行查閱。
如果要自己去寫變換的矩陣,然后把矩陣交給OpenGL處理,也是一個比較麻煩的事情,那么怎么辦呢?這時候需要用到相機和投影,生成需要的矩陣。
相機和投影
相機
根據(jù)現(xiàn)實生活中的經(jīng)歷我們指導(dǎo),對一個場景,隨著相機的位置、姿態(tài)的不同,拍攝出來的畫面也是不相同。將相機對應(yīng)于OpenGL的世界,決定相機拍攝的結(jié)果(也就是最后屏幕上展示的結(jié)果),包括相機位置、相機觀察方向以及相機的UP方向。
- 相機位置:相機的位置是比較好理解的,就是相機在3D空間里面的坐標(biāo)點。
- 相機觀察方向:相機的觀察方向,表示的是相機鏡頭的朝向,你可以朝前拍、朝后拍、也可以朝左朝右,或者其他的方向。
- 相機UP方向:相機的UP方向,可以理解為相機頂端指向的方向。比如你把相機斜著拿著,拍出來的照片就是斜著的,你倒著拿著,拍出來的就是倒著的。
在Android OpenGLES程序中,我們可以通過以下方法來進行相機設(shè)置:
Matrix.setLookAtM (float[] rm, //接收相機變換矩陣 int rmOffset, //變換矩陣的起始位置(偏移量) float eyeX,float eyeY, float eyeZ, //相機位置 float centerX,float centerY,float centerZ, //觀測點位置 float upX,float upY,float upZ) //up向量在xyz上的分量
投影
用相機看到的3D世界,最后還需要呈現(xiàn)到一個2D平面上,這就是投影了。在Android OpenGLES2.0(一)——了解OpenGLES2.0也有提到關(guān)于投影。Android OpenGLES的世界中,投影有兩種,一種是正交投影,另外一種是透視投影。
使用正交投影,物體呈現(xiàn)出來的大小不會隨著其距離視點的遠近而發(fā)生變化。在Android OpenGLES程序中,我們可以使用以下方法來設(shè)置正交投影:
Matrix.orthoM (float[] m, //接收正交投影的變換矩陣 int mOffset, //變換矩陣的起始位置(偏移量) float left, //相對觀察點近面的左邊距 float right, //相對觀察點近面的右邊距 float bottom, //相對觀察點近面的下邊距 float top, //相對觀察點近面的上邊距 float near, //相對觀察點近面距離 float far) //相對觀察點遠面距離
使用透視投影,物體離視點越遠,呈現(xiàn)出來的越小。離視點越近,呈現(xiàn)出來的越大。。在Android OpenGLES程序中,我們可以使用以下方法來設(shè)置透視投影:
Matrix.frustumM (float[] m, //接收透視投影的變換矩陣 int mOffset, //變換矩陣的起始位置(偏移量) float left, //相對觀察點近面的左邊距 float right, //相對觀察點近面的右邊距 float bottom, //相對觀察點近面的下邊距 float top, //相對觀察點近面的上邊距 float near, //相對觀察點近面距離 float far) //相對觀察點遠面距離
使用變換矩陣
實際上相機設(shè)置和投影設(shè)置并不是真正的設(shè)置,而是通過設(shè)置參數(shù),得到一個使用相機后頂點坐標(biāo)的變換矩陣,和投影下的頂點坐標(biāo)變換矩陣,我們還需要把矩陣傳入給頂點著色器,在頂點著色器中用傳入的矩陣乘以坐標(biāo)的向量,得到實際展示的坐標(biāo)向量。注意,是矩陣乘以坐標(biāo)向量,不是坐標(biāo)向量乘以矩陣,矩陣乘法是不滿足交換律的。
而通過上面的相機設(shè)置和投影設(shè)置,我們得到的是兩個矩陣,為了方便,我們需要將相機矩陣和投影矩陣相乘,得到一個實際的變換矩陣,再傳給頂點著色器。矩陣相乘:
Matrix.multiplyMM (float[] result, //接收相乘結(jié)果 int resultOffset, //接收矩陣的起始位置(偏移量) float[] lhs, //左矩陣 int lhsOffset, //左矩陣的起始位置(偏移量) float[] rhs, //右矩陣 int rhsOffset) //右矩陣的起始位置(偏移量)
等腰直角三角形的實現(xiàn)
在上篇博客的基礎(chǔ)上,我們需要做以下步驟即可實現(xiàn)繪制一個等腰直角三角形:
1.修改頂點著色器,增加矩陣變換:
attribute vec4 vPosition; uniform mat4 vMatrix; void main() { gl_Position = vMatrix*vPosition; }
2.設(shè)置相機和投影,獲取相機矩陣和投影矩陣,然后用相機矩陣與投影矩陣相乘,得到實際變換矩陣:
@Override public void onSurfaceChanged(GL10 gl, int width, int height) { //計算寬高比 float ratio=(float)width/height; //設(shè)置透視投影 Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 7); //設(shè)置相機位置 Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f); //計算變換矩陣 Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0); }
3.將變換矩陣傳入頂點著色器:
@Override public void onDrawFrame(GL10 gl) { //將程序加入到OpenGLES2.0環(huán)境 GLES20.glUseProgram(mProgram); //獲取變換矩陣vMatrix成員句柄 mMatrixHandler= GLES20.glGetUniformLocation(mProgram,"vMatrix"); //指定vMatrix的值 GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0); //獲取頂點著色器的vPosition成員句柄 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); //啟用三角形頂點的句柄 GLES20.glEnableVertexAttribArray(mPositionHandle); //準(zhǔn)備三角形的坐標(biāo)數(shù)據(jù) GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); //獲取片元著色器的vColor成員的句柄 mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); //設(shè)置繪制三角形的顏色 GLES20.glUniform4fv(mColorHandle, 1, color, 0); //繪制三角形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); //禁止頂點數(shù)組的句柄 GLES20.glDisableVertexAttribArray(mPositionHandle); }
運行即可得到一個等腰直角三角形:
彩色的三角形
老顯示一個白色的三角形實在太單調(diào)了,我們需要讓這個三角形變成彩色的。該怎么做?
Android OpenGLES2.0(一)——了解OpenGLES2.0中也提到過,頂點著色器是確定頂點位置的,針對每個頂點執(zhí)行一次。片元著色器是針對片元顏色的,針對每個片元執(zhí)行一次。而在我們的片元著色器中,我們是直接給片元顏色賦值,外部我們也只傳入了一個顏色值,要使三角形呈現(xiàn)為彩色,我們需要在不同的片元賦值不同的顏色。為了處理簡單,我們在上個等腰三角形的實例中修改頂點著色器,保持片元著色器不變,達到讓三角形呈現(xiàn)為彩色的目的:
attribute vec4 vPosition; uniform mat4 vMatrix; varying vec4 vColor; attribute vec4 aColor; void main() { gl_Position = vMatrix*vPosition; vColor=aColor; }
可以看到我們增加了一個aColor(頂點的顏色)作為輸入量,傳遞給了vColor。vColor的前面有個varying。像attribute、uniform、varying都是在OpenGL的著色器語言中表示限定符,attribute一般用于每個頂點都各不相同的量。uniform一般用于對同一組頂點組成的3D物體中各個頂點都相同的量。varying一般用于從頂點著色器傳入到片元著色器的量。還有個const表示常量。關(guān)于著色器語言,在后續(xù)博客中將為單獨介紹。
然后,我們需要傳入三個不同的頂點顏色到頂點著色器中:
//設(shè)置顏色 float color[] = { 0.0f, 1.0f, 0.0f, 1.0f , 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f }; ByteBuffer dd = ByteBuffer.allocateDirect( color.length * 4); dd.order(ByteOrder.nativeOrder()); FloatBuffer colorBuffer = dd.asFloatBuffer(); colorBuffer.put(color); colorBuffer.position(0); //獲取片元著色器的vColor成員的句柄 mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor"); //設(shè)置繪制三角形的顏色 GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glVertexAttribPointer(mColorHandle,4, GLES20.GL_FLOAT,false, 0,colorBuffer);
運行得到一個彩色的等腰三角形:
源碼
所有的代碼全部在一個項目中,托管在Github上——Android OpenGLES 2.0系列博客的Demo
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開發(fā)之BroadcastReceiver用法實例分析
這篇文章主要介紹了Android開發(fā)之BroadcastReceiver用法,實例分析了Android中廣播的相關(guān)使用技巧,需要的朋友可以參考下2015-05-05Android編程實現(xiàn)短信收發(fā)及語音播報提示功能示例
這篇文章主要介紹了Android編程實現(xiàn)短信收發(fā)及語音播報提示功能,結(jié)合實例形式分析了Android實現(xiàn)短信的接收、發(fā)送以及相應(yīng)的語音播報提示功能相關(guān)操作技巧,需要的朋友可以參考下2017-08-08Android Studio 合并module到統(tǒng)一文件夾的方法
這篇文章主要介紹了Android Studio 合并module到統(tǒng)一文件夾的方法,補充介紹了android studio關(guān)于同名資源文件的合并技巧,需要的朋友可以參考下2018-04-04Android如何實現(xiàn)一個DocumentProvider示例詳解
這篇文章主要為大家介紹了Android如何實現(xiàn)一個DocumentProvider示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12ionic監(jiān)聽android返回鍵實現(xiàn)“再按一次退出”功能
本篇文章主要介紹了ionic監(jiān)聽android返回鍵實現(xiàn)“再按一次退出”功能,非常具有實用價值,需要的朋友可以參考下2018-02-02okhttp3.4.1+retrofit2.1.0實現(xiàn)離線緩存的示例
本篇文章主要介紹了okhttp3.4.1+retrofit2.1.0實現(xiàn)離線緩存的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12Android中ViewPager實現(xiàn)滑動條及與Fragment結(jié)合的實例教程
ViewPager類主要被用來實現(xiàn)可滑動的視圖功能,這里我們就來共同學(xué)習(xí)Android中ViewPager實現(xiàn)滑動條及與Fragment結(jié)合的實例教程,需要的朋友可以參考下2016-06-06