OpenGL ES 學習筆記(1)-基本使用

1. OpenGL ES 基礎

1.1. OpenGL 坐標

屏幕坐標系中橫軸是x軸,縱軸是y軸,分別向右是正方向,向下是正方向.原點在左上角
數學坐標系中原點在左下角,向上是y軸正方向.
在OpenGL3D坐標系中,稍有不同,使用的是笛卡爾坐標系,表示空間坐標位置.和數學坐標系相似,左下角是原點,向右和向上是正值.還要添加第三個量就是z軸.z軸方向指向你.
下圖是OpenGL ES笛卡爾坐標系.

OpenGL ES笛卡爾坐標系

實際上我們有幾種坐標系和空間,在OpenGL中,每個空間位置會按照如下變換:

  • 對象空間,相對于每個對象自身
  • 相機,眼睛,空間是針對視點
  • 投影,剪裁,空間是平面的屏幕或者是圖像上的視口.

1.2. 視景體

也叫平截頭體(frustum),是一個棱臺,表示的是照相機能夠捕捉到畫面的一個區域.
下圖為視景體


視景體

視景體數學模型

定義視景體非常簡單通過定義一個幾何體就可以了,空間中,任何平截頭體內的物體都能夠在屏幕上找到相應的位置,只要沒有被其他物體覆蓋.
Frustum也用于指定你的field-of-view(FOV),比如相機的廣角與長焦鏡頭.角度越大看到的景象越多,成像就越小.
可以使用gl.glMatrixMode(GL_MODELVIEW)方法對模型視圖矩陣進行平移和旋轉操作.還需要定義操縱投影矩陣(projection matrix).
透視投影是我們觀察世界的正常形勢,物體越遠越小,如果對frustum內的這種效果移除,我們就會得到正交投影,就是不論物體遠近,他們的大小不發生改變,這主要用于機械制圖等.
經常需要控制操縱的是哪個矩陣,可通過gl.glMatrixMode()實現,如果不知道操縱的是那個矩陣很容易出錯.

投影類型:

  • 透視投影:有深度,越遠越小.
  • 正投影:沒有深度,相同大小.
    矩陣模式,openGL是基于狀態的,操縱很多矩陣,通過該函數指定使用哪個矩陣.
    常用的矩陣有:
  • GL10.GL_PROJECTION:投影矩陣.
  • GL10.GL_MODELVIEW:模型視圖矩陣.
    指定使用哪個矩陣之后,需要先加載單位矩陣(使用gl.loadIdentity()方法,類似于矩陣歸零).

1.3. 定義視口

gl.glViewPort(...);需要注意的是,坐標從左下角開始.

視口

1.4. 使用正交投影

gl.glOrtho(..)
//使用正交矩陣棧,需要重新設置坐標系,告訴OpenGL,以后所有變換都會影響該矩陣.
gl.glMatrixMode(GL_PROJECTION);
gl.glLoadIdentity();

1.5. 雙緩沖

所有圖形程序最重要的特性之一就是雙緩沖.他允許在一個屏幕之外的緩沖區中執行繪制代碼,然后使用一條交換命令把繪制完成的圖形立即顯示在屏幕上.
雙緩沖用途兩個:
一是一些復雜繪圖時間較長,不希望在屏幕上看到每個步驟.雙緩沖可以合成一幅圖像完成后再顯示.
用戶不會看到不完整的圖像.
二是用于動畫.每個幀都在屏幕之外的緩沖區繪制,完成后快速交換到屏幕上.

1.6. OpenGL狀態機

繪制3D圖形是一項復雜任務.狀態機是一個抽象的模型,表示一組狀態變量的集合.每個狀態變量可以有各種不同的值,打開關閉等.OpenGL繪圖時,每次指定這些變量明顯不合適.因此OpenGL給出了一中狀態模型(狀態機)來跟蹤所有的OpenGL狀態變量.一個狀態變量被設置后,以后一直保持這個狀態,直到下次對他進行修改.使用如下命令可進行狀態變
量的切換.
gl.glEnable(xx);//gl.glDisable(xx);
//例如光照
gl.glEnable(GL_LIGHGTING);

2. 使用OpenGL編碼步驟

2.1. 創建GLSurfaceView對象

寫一個繼承自GLSurfaceView的類.

2.2. 創建GLSurfaceView.renderer實現類.

實現GLSurfaceView.Renderer接口,創建渲染器.

2.3. 設置activity的contentView,以及設置view的render對象.

將2.1中的GLSurfaceView設置2.2創建的渲染器,并設置到ContentView中.

2.4. 實現Renderer類的過程

2.4.1. onSurfaceCreate()方法

設置清屏的顏色和啟用頂點緩沖區
//設置清屏色
gl.glClearColor(0, 0, 0, 1);
//啟用頂點緩沖區.
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

2.4.2. onSurfaceChanged()方法

  1. 設置viewport(視口) 輸出畫面的區域(從左下角開始)
    gl.glViewport(0, 0, width, height);
  2. 操縱投影矩陣,設置平截頭體(比例通常和視口比例相同,否則輸出畫面會走樣)
    //矩陣模式,投影矩陣,openGL基于狀態機
    gl.glMatrixMode(GL10.GL_PROJECTION);
    //加載單位矩陣
    gl.glLoadIdentity();
    //平截頭體 zNear:近平面 zFar:遠平面
    gl.glFrustumf(-ratio, ratio, -1f, 1f, 3, 7);

2.4.3. onDrawFrame()方法:

  1. 清除顏色緩沖區
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  2. 操縱模型視圖矩陣,設置眼球的參數
    gl.glMatrixMode(GL10.GL_MODELVIEW);
    gl.glLoadIdentity();//加載單位矩陣
    //eyeX, eyeY, eyeZ :放置眼球的坐標
    //centerX, centerY, centerZ : 眼球的觀察點
    //upx, upy, upz : 指定眼球向上的向量(此處比較危險)
    GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);
  3. 定義圖形頂點坐標值數組
    float[] coords = {
    0f,0.5f,0f,
    -0.5f,-0.5f,0f,
    0.5f,-0.5f,0f
    };
  4. 將頂點坐標轉換成緩沖區數據
    //分配字節緩存區空間,存放頂點坐標數據(浮點數4個字節)
    ByteBuffer ibb = ByteBuffer.allocateDirect(coords.length * 4);
    //設置的順序(本地順序, 跟操作系統有關)
    ibb.order(ByteOrder.nativeOrder());
    //放置頂點坐標數組
    FloatBuffer fbb = ibb.asFloatBuffer();
    fbb.put(coords);
    //定位指針的位置,從該位置開始讀取頂點數據
    ibb.position(0);
  5. 設置繪圖顏色
    gl.glColor4f(1f, 0f, 0f, 1f);
  6. 指定頂點緩沖區指針
    //3:3維點,使用三個坐標值表示一個點
    //type:每個點的數據類型
    //stride:0,跨度.
    //ibb:指定頂點緩沖區
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, ibb);
  7. 繪圖
    //mode: 繪制類型
    //first: 起始點
    //count: 點的個數
    gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);

2.5. 封裝通用的字節緩沖區轉換方法

(持續更新該Utils類)

public class BufferUtil {
    /**
     * 將浮點數組轉換成字節緩沖區
     * @param arr 輸入待轉換的浮點數組
     * @return 字節緩沖區
     */
    public static ByteBuffer arr2ByteBuffer(float[] arr) {
        //分配字節緩存區空間, 存放定點坐標數據
        // 一個字節 8位  浮點數4個字節
        ByteBuffer ibb = ByteBuffer.allocateDirect(arr.length * 4);
        //設置順序(本地順序,跟操作系統有關)
        ibb.order(ByteOrder.nativeOrder());
        //放置頂點坐標數據
        FloatBuffer fbb = ibb.asFloatBuffer();
        fbb.put(arr);
        //定位指針的位置, 從該位置開始讀取頂點數據
        ibb.position(0);
        return ibb;
    }
 
    /**
     * 將List集合轉換成字節緩沖區
     * @param list 輸入待轉換的浮點數組
     * @return 字節緩沖區
     */
    public static ByteBuffer list2ByteBuffer(List<Float> list) {
        //分配字節緩存區空間, 存放定點坐標數據
        // 一個字節 8位  浮點數4個字節
        ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);
        //設置順序(本地順序,跟操作系統有關)
        ibb.order(ByteOrder.nativeOrder());
        //放置頂點坐標數據
        FloatBuffer fbb = ibb.asFloatBuffer();
        for (Float aFloat : list) {
            fbb.put(aFloat);
        }
        //定位指針的位置, 從該位置開始讀取頂點數據
        ibb.position(0);
        return ibb;
    }
}

2.6. 渲染模式

OpenGL ES有兩種渲染模式:
GLSurfaceView.RENDERMODE_CONTINUOUSLY: 持續渲染(默認)
GLSurfaceView.RENDERMODE_WHEN_DIRTY: 臟渲染, 命令渲染(需要使用myGLSurfaceView.requestRender();方法主動請求渲染)

2.7. 坐標系旋轉

//angle: 旋轉角度
//x,y,z: 沿著哪個軸旋轉(向量) 迎面軸正方向看去, 順時針: 負值 逆時針: 正值
gl.glRotatef(xrotate, 1, 0, 0);//繞x軸旋轉
gl.glRotatef(yrotate, 0, 1, 0);//繞y軸旋轉

2.8. 緩沖區

緩沖區分為:

  • 頂點緩沖區(VertexBuffer)
  • 顏色緩沖區(ColorBuffer)

使用方式:
gl.glColorPointer(xx);//指定顏色緩沖區
gl.glVertexPointer(xx);//指定頂點指針
注意:不要忘記在onSurfaceCreated() 方法中啟用兩個緩沖區
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);//啟用頂點緩沖區
gl.glEnableClientState(GL10.GL_COLOR_ARRAY);//啟動顏色緩沖區

3. 空間繪圖

OpenGL ES支持三種繪圖模式:點, 線和三角形.

3.1. 點

像素是計算機監視器上最小的元素.在彩色系統中,像素可以是許多可用顏色的一種.屏幕上的一個位置對應一個點,并為之指定顏色.然后,在此基礎上,用最擅長的計算機語言生成直線,多邊形,圓形以及其他圖形,甚至整個GUI.
下圖把可視區域看成是三維畫布,可使用OpenGL命令和函數進行繪圖.


畫點:
gl.glDrawArray(GL_POINTS,..)
設置點大小:
gl.glPiontSize(xx);
示例(點變化大小的螺旋):

//計算點坐標
float r = 0.5f;//半徑
//  旋轉3圈
float x = 0f, y = 0f, z = 1f;
float zstep = 0.01f;//z的步長
float psize   = 1.0f;//點的大小
float pstep = 0.5f;//點的步長
//循環繪制
for (float alpha = 0f; alpha < Math.PI * 6; alpha = (float) (alpha + Math.PI/32)) {
    x = (float) (r * Math.cos(alpha));
    y = (float) (r * Math.sin(alpha));
    z = z - zstep;
    //設置點的大小
    gl.glPointSize(psize = psize + pstep);
    //指定頂點指針
    gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtil.arr2ByteBuffer(new float[]{x, y, z}));
    //畫數組
    gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
}
preview

3.2. 線

畫線分為三類:

  • lines: 線集(線段集合)
  • line_strip: 線帶(首尾相接不閉環)
  • line_loop: 線環(首尾相接閉環)

線帶

線環

畫線:
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fbb);
gl.glDrawArrays(GL10.GL_LINES, 0, 4);
設置直線寬度:
gl.glLinewidth(xx);
示例(使用線帶畫螺旋線):

float r = 0.5f;//半徑
List<Float> coordsList = new ArrayList<>();//坐標點集合
//  旋轉3圈
float x = 0f, y = 0f, z = 1f;
float zstep = 0.01f;//步長
for (float alpha = 0f; alpha < Math.PI * 6; alpha = (float) (alpha + Math.PI/32)) {
    x = (float) (r * Math.cos(alpha));
    y = (float) (r * Math.sin(alpha));
    z = z - zstep;
    coordsList.add(x);
    coordsList.add(y);
    coordsList.add(z);
}
//指定頂點指針
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtil.list2ByteBuffer(coordsList));
//畫數組
gl.glDrawArrays(GL10.GL_LINE_STRIP, 0, coordsList.size() / 3);
preview

3.3. 三角形

使用畫線方式繪制的圖形的線框樣式的,即都是直線無法進行
顏色填充,多邊形是閉合形狀.可以選擇顏色進行填充.
有三種模式:

3.3.1. 環繞(GL_TRIANGLES)

注意連接頂點的箭頭方向,第一個三角形的順序是v0->v1->v2->v0,該順序是有定點的指定次序決定的.該順序為順時針方向.第二個三角形也是一樣.頂點的指定次序以及方向的組合成為環繞(winding).左圖看成是順時針環繞,如果將v4和v5兩點互換,得到逆時針環繞.如右圖:


3.3.2. 三角形帶(GL_TRIANGLES_STRIP)

GL_TRIANLTE_STRIP,可繪制一串相鄰的三角形,這些頂點不是按照他們指定的順興進行遍歷的,這是為了保持每個三角形的環繞方向(逆時針).他的繪制模式是v0->v1->v2,接著v2->v1->v3,然后是v2->v3->v4,依次類推.

3.3.3. 三角形扇(GL_TRIANGLES_FAN)

圍繞一個中心點的相連三角形.


3.3.4. 示例(正方形)

float r = 0.5f;//半徑
float[] coords = {
        -r, r, 0,
        -r, -r, 0,
        r, r, 0,
        r, -r, 0,
};
 
//指定頂點指針
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, BufferUtil.arr2ByteBuffer(coords));
//畫數組
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, coords.length/ 3);

3.4 實心物體

3.4.1. 設置顏色

我們知道畫點畫線或者三角形都要指定頂點緩沖區,同樣的也會顏色緩沖區.
需要注意的是, 頂點緩沖區中的點與顏色緩沖區中的點進行對應.

顏色緩沖區

頂點的著色模式:

  1. smmoth: 平滑模式,也稱漸變(默認)
  2. flat: 單調模式以三角形最后一個點的顏色值為準
    設置著色模式:
    gl.glShadeMode(GL_FLAT|GL_SMOOTH);//單調|平滑
    注意: 三角形面設置的顏色, 跟最后一個頂點的顏色相同.

3.4.2. 深度測試

深度測試是一種有效地的用于隱藏表面消除的技巧,當一個像素繪制時,他將被設置一個值(稱為z值),來表示他和觀察者之間的距離.以后,當該位置需要繪制另一個像素時,新像素的z值和原先的值進行比較.如果z值越高,距離觀察者越近,就位于以前像素的前面.反之位于后面.內部是通過深度緩沖區實現的.他存儲了屏幕上每個像素的深度值.


使用深度測試

不使用深度測試

啟用深度測試:(不要忘記在繪圖開始時清除深度緩沖區)
gl.glClear(GL10.GL_DEPTH_BUFFER_BIT);//清除深度緩沖區
gl.glEnable(GL10.GL_DEPTH_TEST);//啟用深度測試

3.4.3. 剔除

使用深度測試,性能上需要有開銷,因為每次繪制像素都需要比較.如果知道那個面肯定不能繪制,就可以使用剔除這種技巧了.消除已經知道肯定不會繪制的幾何圖形.也不會向OpenGL驅動程序發送這個幾何圖形,性能能夠顯著提升.
剔除技巧之一就是背面剔除,消除一個表面的背面.
//啟用表面剔除
gl.glEnable(GL10.GL_CULL_FACE);
//指定正面
//ccw: counter clock wise -> 逆時針(默認)
//cw: clock wise -> 順時針
gl.glFrontFace(GL10.GL_CCW);
//剔除背面
//GL10.GL_FRONT:正面
//GL10.GL_BACK背面
gl.glCullFace(GL10.GL_BACK);

3.4.4. 使用剪刀進行裁剪

不在ViewPort整個視口進行渲染.
使用方式:
//啟用剪裁測試
gl.glEnable(GL_SCISSOR_TEST);
//剪裁: x, y: 剪裁距離 width,height:屏幕寬高
gl.glScissor(160, 240, 160, 50);

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內容