Android OpenGLES2.0(三)——等腰直角三角形和彩色的三角形

上一篇博客中我們已經(jīng)繪制出了一個(gè)直角三角形,雖然我們相對(duì)于坐標(biāo),我們?cè)O(shè)置的直角三角形的兩腰是相等的,但是實(shí)際上展示出來的卻并不是這樣,雖然通過計(jì)算,我們可以把三角形的兩腰計(jì)算一下比例,使它們?cè)谧鴺?biāo)上不等,但是現(xiàn)實(shí)出來相等,但是當(dāng)繪制的圖形比較復(fù)雜的話,這個(gè)工作量對(duì)我們來說實(shí)在太龐大了。那么我們?cè)趺醋瞿兀看鸢甘牵褂米儞Q矩陣,把計(jì)算交給OpenGL。

矩陣

在數(shù)學(xué)中,矩陣(Matrix)是一個(gè)按照長方陣列排列的復(fù)數(shù)或?qū)崝?shù)集合 ,最早來自于方程組的系數(shù)及常數(shù)所構(gòu)成的方陣。這一概念由19世紀(jì)英國數(shù)學(xué)家凱利首先提出。

矩陣常被用于圖像處理、游戲開發(fā)、幾何光學(xué)、量子態(tài)的線性組合及電子學(xué)等多種領(lǐng)域。我們現(xiàn)在多是在圖像處理或者游戲開發(fā)的領(lǐng)域來使用矩陣。
在三維圖形學(xué)中,一般使用的是4階矩陣。在DirectX中使用的是行向量,如[xyzw],所以與矩陣相乘時(shí),向量在前矩陣在后。OpenGL中使用的是列向量,如[xyzx]T,所以與矩陣相乘時(shí),矩陣在前,向量在后。關(guān)于矩陣的具體知識(shí),博客中不詳細(xì)講解,需要了解的同學(xué)可以自行查閱。
如果要自己去寫變換的矩陣,然后把矩陣交給OpenGL處理,也是一個(gè)比較麻煩的事情,那么怎么辦呢?這時(shí)候需要用到相機(jī)和投影,生成需要的矩陣。

相機(jī)和投影

相機(jī)

根據(jù)現(xiàn)實(shí)生活中的經(jīng)歷我們指導(dǎo),對(duì)一個(gè)場景,隨著相機(jī)的位置、姿態(tài)的不同,拍攝出來的畫面也是不相同。將相機(jī)對(duì)應(yīng)于OpenGL的世界,決定相機(jī)拍攝的結(jié)果(也就是最后屏幕上展示的結(jié)果),包括相機(jī)位置、相機(jī)觀察方向以及相機(jī)的UP方向。

相機(jī)位置:相機(jī)的位置是比較好理解的,就是相機(jī)在3D空間里面的坐標(biāo)點(diǎn)。
相機(jī)觀察方向:相機(jī)的觀察方向,表示的是相機(jī)鏡頭的朝向,你可以朝前拍、朝后拍、也可以朝左朝右,或者其他的方向。
相機(jī)UP方向:相機(jī)的UP方向,可以理解為相機(jī)頂端指向的方向。比如你把相機(jī)斜著拿著,拍出來的照片就是斜著的,你倒著拿著,拍出來的就是倒著的。

在Android OpenGLES程序中,我們可以通過以下方法來進(jìn)行相機(jī)設(shè)置:

Matrix.setLookAtM (float[] rm,      //接收相機(jī)變換矩陣
                int rmOffset,       //變換矩陣的起始位置(偏移量)
                float eyeX,float eyeY, float eyeZ,   //相機(jī)位置
                float centerX,float centerY,float centerZ,  //觀測點(diǎn)位置
                float upX,float upY,float upZ)  //up向量在xyz上的分量

投影

用相機(jī)看到的3D世界,最后還需要呈現(xiàn)到一個(gè)2D平面上,這就是投影了。在Android OpenGLES2.0(一)——了解OpenGLES2.0也有提到關(guān)于投影。Android OpenGLES的世界中,投影有兩種,一種是正交投影,另外一種是透視投影

  • 使用正交投影,物體呈現(xiàn)出來的大小不會(huì)隨著其距離視點(diǎn)的遠(yuǎn)近而發(fā)生變化。在Android OpenGLES程序中,我們可以使用以下方法來設(shè)置正交投影:
Matrix.orthoM (float[] m,           //接收正交投影的變換矩陣
                int mOffset,        //變換矩陣的起始位置(偏移量)
                float left,         //相對(duì)觀察點(diǎn)近面的左邊距
                float right,        //相對(duì)觀察點(diǎn)近面的右邊距
                float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
                float top,          //相對(duì)觀察點(diǎn)近面的上邊距
                float near,         //相對(duì)觀察點(diǎn)近面距離
                float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離
  • 使用透視投影,物體離視點(diǎn)越遠(yuǎn),呈現(xiàn)出來的越小。離視點(diǎn)越近,呈現(xiàn)出來的越大。。在Android OpenGLES程序中,我們可以使用以下方法來設(shè)置透視投影:
Matrix.frustumM (float[] m,         //接收透視投影的變換矩陣
                int mOffset,        //變換矩陣的起始位置(偏移量)
                float left,         //相對(duì)觀察點(diǎn)近面的左邊距
                float right,        //相對(duì)觀察點(diǎn)近面的右邊距
                float bottom,       //相對(duì)觀察點(diǎn)近面的下邊距
                float top,          //相對(duì)觀察點(diǎn)近面的上邊距
                float near,         //相對(duì)觀察點(diǎn)近面距離
                float far)          //相對(duì)觀察點(diǎn)遠(yuǎn)面距離

使用變換矩陣

實(shí)際上相機(jī)設(shè)置和投影設(shè)置并不是真正的設(shè)置,而是通過設(shè)置參數(shù),得到一個(gè)使用相機(jī)后頂點(diǎn)坐標(biāo)的變換矩陣,和投影下的頂點(diǎn)坐標(biāo)變換矩陣,我們還需要把矩陣傳入給頂點(diǎn)著色器,在頂點(diǎn)著色器中用傳入的矩陣乘以坐標(biāo)的向量,得到實(shí)際展示的坐標(biāo)向量。注意,是矩陣乘以坐標(biāo)向量,不是坐標(biāo)向量乘以矩陣,矩陣乘法是不滿足交換律的
而通過上面的相機(jī)設(shè)置和投影設(shè)置,我們得到的是兩個(gè)矩陣,為了方便,我們需要將相機(jī)矩陣和投影矩陣相乘,得到一個(gè)實(shí)際的變換矩陣,再傳給頂點(diǎn)著色器。矩陣相乘:

Matrix.multiplyMM (float[] result, //接收相乘結(jié)果
                int resultOffset,  //接收矩陣的起始位置(偏移量)
                float[] lhs,       //左矩陣
                int lhsOffset,     //左矩陣的起始位置(偏移量)
                float[] rhs,       //右矩陣
                int rhsOffset)     //右矩陣的起始位置(偏移量)

等腰直角三角形的實(shí)現(xiàn)

在上篇博客的基礎(chǔ)上,我們需要做以下步驟即可實(shí)現(xiàn)繪制一個(gè)等腰直角三角形:

1.修改頂點(diǎn)著色器,增加矩陣變換:

attribute vec4 vPosition;
uniform mat4 vMatrix;
void main() {
    gl_Position = vMatrix*vPosition;
}

2.設(shè)置相機(jī)和投影,獲取相機(jī)矩陣和投影矩陣,然后用相機(jī)矩陣與投影矩陣相乘,得到實(shí)際變換矩陣:

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    //計(jì)算寬高比
    float ratio=(float)width/height;
    //設(shè)置透視投影
    Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 7);
    //設(shè)置相機(jī)位置
    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
    //計(jì)算變換矩陣
    Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
}

3.將變換矩陣傳入頂點(diǎn)著色器:

@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);
    //獲取頂點(diǎn)著色器的vPosition成員句柄
    mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
    //啟用三角形頂點(diǎn)的句柄
    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);
    //禁止頂點(diǎn)數(shù)組的句柄
    GLES20.glDisableVertexAttribArray(mPositionHandle);
}

運(yùn)行即可得到一個(gè)等腰直角三角形:


彩色的三角形

老顯示一個(gè)白色的三角形實(shí)在太單調(diào)了,我們需要讓這個(gè)三角形變成彩色的。該怎么做?
Android OpenGLES2.0(一)——了解OpenGLES2.0中也提到過,頂點(diǎn)著色器是確定頂點(diǎn)位置的,針對(duì)每個(gè)頂點(diǎn)執(zhí)行一次。片元著色器是針對(duì)片元顏色的,針對(duì)每個(gè)片元執(zhí)行一次。而在我們的片元著色器中,我們是直接給片元顏色賦值,外部我們也只傳入了一個(gè)顏色值,要使三角形呈現(xiàn)為彩色,我們需要在不同的片元賦值不同的顏色。為了處理簡單,我們?cè)谏蟼€(gè)等腰三角形的實(shí)例中修改頂點(diǎn)著色器,保持片元著色器不變,達(dá)到讓三角形呈現(xiàn)為彩色的目的:

attribute vec4 vPosition;
uniform mat4 vMatrix;
varying  vec4 vColor;
attribute vec4 aColor;
void main() {
  gl_Position = vMatrix*vPosition;
  vColor=aColor;
}

可以看到我們?cè)黾恿艘粋€(gè)aColor(頂點(diǎn)的顏色)作為輸入量,傳遞給了vColor。vColor的前面有個(gè)varying。像attribute、uniform、varying都是在OpenGL的著色器語言中表示限定符,attribute一般用于每個(gè)頂點(diǎn)都各不相同的量。uniform一般用于對(duì)同一組頂點(diǎn)組成的3D物體中各個(gè)頂點(diǎn)都相同的量。varying一般用于從頂點(diǎn)著色器傳入到片元著色器的量。還有個(gè)const表示常量。關(guān)于著色器語言,在后續(xù)博客中將為單獨(dú)介紹。
然后,我們需要傳入三個(gè)不同的頂點(diǎn)顏色到頂點(diǎn)著色器中:

//設(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);

運(yùn)行得到一個(gè)彩色的等腰三角形:


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,452評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,818評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,997評(píng)論 0 290
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,292評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,510評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,721評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評(píng)論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,235評(píng)論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,480評(píng)論 2 379