OpenGL ES學習總結一:基礎知識簡介

OpenGL ES學習總結一:基礎知識簡介

什麼是OpenGL ES?

  • OpenGL ES (爲OpenGL for Embedded System的縮寫) 爲適用於嵌入式系統的一個免費二維和三維圖形庫。
  • 爲桌面版本OpenGL 的一個子集。

OpenGL ES管道(Pipeline)

OpenGL ES 1.x 的工序是固定的,稱爲Fix-Function Pipeline,可以想象一個帶有很多控制開關的機器,儘管加工的工序是固定的,但是可以通過打開或關閉開關來設置參數或者打開關閉某些功能。OpenGL ES 2.0 允許提供編程來控制一些重要的工序,一些“繁瑣”的工序比如柵格化等仍然是固定的。(這些開關被就是state,注意應該儘量少的改變state,以免影響性能)
  • 管道“工序”大致可以分爲 Transformation Stage 和 Rasterization Stage兩大步。
  • OpenGL ES 支持的基本圖形爲 點Point, 線Line, 和三角形Triangle ,其它所有複製圖形都是通過這幾種基本幾何圖形組合而成。
  • 在發出繪圖指令後,會對頂點(Vertices)數組進行指定的座標變換或光照處理。
  • 頂點處理完成後,通過Rasterizer 來生成像素信息,稱爲”Fragments“ 。
  • 對於Fragment 在經過Texture Processing, Color Sum ,Fog 等處理並將最終處理結果存放在內存中(稱爲FrameBuffer)。
  • OpenGL 2.0可以通過編程來修改上述紅色的部分的步驟,稱爲Programmable Shader.

OpenGL ES API 命名習慣

 
  • 定義的常量都以GL_爲前綴。比如GL10.GL_COLOR_BUFFER_BIT
  • OpenGL ES 指令以gl開頭 ,比如gl.glClearColor
  • 某些OpenGL指令以3f 或4f結尾,3和4代表參數的個數,f代表參數類型爲浮點數,如gl.glColor4f ,i,x 代表 int如 gl.glColor4x
  • 對應以v結尾的OpenGL ES 指令,代表參數類型爲一個矢量(Vector) ,如 glTexEnvfv
  • 所有8-bit整數對應到byte 類型,16-bit 對應到short類型,32-bit整數(包括GLFixed)對應到int類型,而所有32-bit 浮點數對應到float 類型。
  • GL_TRUE,GL_FALSE 對應到boolean類型
  • C字符串((char*)) 對應到Java 的 UTF-8 字符串。 
  •  

     

     
     
    創建簡單的opengl es實例
     
     
     
     

    基本幾何圖形定義

    OpenGL ES 支持繪製的基本幾何圖形分爲三類:點,線段,三角形。也就是說OpenGL ES 只能繪製這三種基本幾何圖形。任何複雜的2D或是3D圖形都是通過這三種幾何圖形構造而成的。

    OpenGL ES提供了兩類方法來繪製一個空間幾何圖形:

    • public abstract void glDrawArrays(int mode, int first, int count) 使用VetexBuffer 來繪製,頂點的順序由vertexBuffer中的順序指定。
    • public abstract void glDrawElements(int mode, int count, int type, Buffer indices) ,可以重新定義頂點的順序,頂點的順序由indices Buffer 指定。
    mode列表:GL_POINTS 繪製獨立的點、GL_LINE_STRIP繪製一條線段、GL_LINE_LOOP繪製一條封閉線段(首位相連)、GL_LINES繪製多條線段、GL_TRIANGLES繪製多個三角形(兩兩不相鄰)、GL_TRIANGLE_STRIP繪製多個三角形(兩兩相鄰)、GL_TRIANGLE_FAN以一個點爲頂點繪製多個相鄰的三角形
     

    對應頂點除了可以爲其定義座標外,還可以指定顏色,材質,法線(用於光照處理)等。

    glEnableClientState 和 glDisableClientState 可以控制的pipeline開關可以有:GL_COLOR_ARRAY (顏色) ,GL_NORMAL_ARRAY (法線), GL_TEXTURE_COORD_ARRAY (材質), GL_VERTEX_ARRAY(頂點), GL_POINT_SIZE_ARRAY_OES等。

    對應的傳入顏色,頂點,材質,法線的方法如下:

    glColorPointer(int size,int type,int stride,Buffer pointer)
    glVertexPointer(int size, int type, int stride, Buffer pointer)
    glTexCoordPointer(int size, int type, int stride, Buffer pointer)
    glNormalPointer(int type, int stride, Buffer pointer)
     
    OpenGL ES 內部存放圖形數據的Buffer有COLOR ,DEPTH (深度信息)等,在繪製圖形只前一般需要清空COLOR 和 DEPTH Buffer。
     
     

    三維座標系及座標變換初步

    OpenGL ES圖形庫最終的結果是在二維平面上顯示3D物體,這個過程可以分成三個部分:
    • 座標變換,座標變換通過使用變換矩陣來描述,因此學習3D繪圖需要了解一些空間幾何,矩陣運算的知識。三維座標通常使用齊次座標來定義。變換矩陣操作可以分爲視角(Viewing),模型(Modeling)和投影(Projection)操作,這些操作可以有選擇,平移,縮放,正側投影,透視投影等。
    • 由於最終的3D模型需要在一個矩形窗口中顯示,因此在這個窗口之外的部分需要裁剪掉以提高繪圖效率,對應3D圖形,裁剪是將處在剪切面之外的部分扔掉。
    • 在最終繪製到顯示器(2D屏幕),需要建立起變換後的座標和屏幕像素之間的對應關係,這通常稱爲“視窗”座標變換(Viewport) transformation.
    如果我們使用照相機拍照的過程做類比,可以更好的理解3D 座標變換的過程。
    1. 拍照時第一步是架起三角架並把相機的鏡頭指向需要拍攝的場景,對應到3D 變換爲viewing transformation (平移或是選擇Camera )
    2. 然後攝影師可能需要調整被拍場景中某個物體的角度,位置,比如攝影師給架好三角架後給你拍照時,可以要讓你調整站立姿勢或是位置。對應到3D繪製就是Modeling transformation (調整所繪模型的位置,角度或是縮放比例)。
    3. 之後攝影師可以需要調整鏡頭取景(拉近或是拍攝遠景),相機取景框所能拍攝的場景會隨鏡頭的伸縮而變換,對應到3D繪圖則爲Projection transformation(裁剪投影場景)。
    4. 按下快門後,對於數碼相機可以直接在屏幕上顯示當前拍攝的照片,一般可以充滿整個屏幕(相當於將座標做規範化處理NDC),此時你可以使用縮放放大功能顯示照片的部分。對應到3D繪圖相當於viewport transformation (可以對最終的圖像縮放顯示等)
    對於Viewing transformation (平移,選擇相機)和Modeling transformation(平移,選擇模型)可以合併起來看,只是應爲向左移動相機,和相機不同將模型右移的效果是等效的。

    在OpenGL ES 中,

    • 使用GL10.GL_MODELVIEW 來同時指定viewing matrix 和modeling matrix.
    • 使用GL10.GL_PROJECTION 指定投影變換,OpenGL 支持透視投影(3D)和正側投影(2D)。
    • 使用glViewport 指定 Viewport 變換。
     
     

    通用的矩陣變換指令

     
    這裏介紹對應指定的座標系(比如viewmodel, projection或是viewport) Android OpenGL ES支持的一些矩陣運算及操作。
    矩陣本身可以支持加減乘除,對角線全爲1的4X4 矩陣成爲單位矩陣Identity Matrix 。
    • 將當前矩陣設爲單位矩陣的指令 爲glLoadIdentity().
    • 矩陣相乘的指令glMultMatrix*() 允許指定任意矩陣和當前矩陣相乘。
    • 選擇當前矩陣種類glMatrixMode() OpenGL ES 可以運行指定GL_PROJECTION,GL_MODELVIEW等座標系,後續的矩陣操作將針對選定的座標。
    • 將當前矩陣設置成任意指定矩陣glLoadMatrix*()
    • 在棧中保存當前矩陣和從棧中恢復所存矩陣,可以使用glPushMatrix()glPopMatrix()
    • 特定的矩陣變換平移glTranslatef(),旋轉glRotatef() 和縮放glScalef()
    方法public abstract void glTranslatef (float x, float y, float z) 用於座標平移變換。
    方法public abstract void glRotatef(float angle, float x, float y, float z)用來實現選擇座標變換,單位爲角度。 (x,y,z)定義旋轉的參照矢量方向。多次旋轉的順序非常重要。
    方法public abstract void glScalef (float x, float y, float z)用於縮放變換。
     

    在進行平移,旋轉,縮放變換時,所有的變換都是針對當前的矩陣(與當前矩陣相乘),如果需要將當前矩陣回覆最初的無變換的矩陣,可以使用單位矩陣(無平移,縮放,旋轉)。

    public abstract void glLoadIdentity()。

    在棧中保存當前矩陣和從棧中恢復所存矩陣,可以使用

    public abstract void glPushMatrix()

    public abstract void glPopMatrix()。

    在進行座標變換的一個好習慣是在變換前使用glPushMatrix保存當前矩陣,完成座標變換操作後,再調用glPopMatrix恢復原先的矩陣設置。
     
     

    Viewing和Modeling(MODELVIEW) 變換

     

    ndroid OpenGL ES 的GLU包有一個輔助函數gluLookAt提供一個更直觀的方法來設置modelview 變換矩陣:

    void gluLookAt(GL10 gl, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
    • eyex,eyey,eyez 指定觀測點的空間座標。
    • tarx,tary,tarz ,指定被觀測物體的參考點的座標。
    • upx,upy,upz 指定觀測點方向爲“上”的向量。(0,1,0)
     
     
     
    投影變換Projection
    投影變換則對應於調整相機鏡頭遠近來取景。
    下面代碼設置當前Matrix模式爲Projection投影矩陣:
    gl.glMatrixMode(GL_PROJECTION);
    gl.glLoadIdentity();
    OpenGL ES可以使用兩種不同的投影變換:透視投影(Perspective Projection)和正側投影(Orthographic Projection)。
    Android OpenGL ES提供了一個輔助方法gluPerspective()可以更簡單的來定義一個透視投影變換:

    GLU.gluPerspective(GL10 gl, float fovy, float aspect, float zNear, float zFar)

    • fovy: 定義視錐的view angle.
    • aspect:  定義視錐的寬高比。
    • zNear: 定義裁剪面的近距離。就是前面距離原點的距離
    • zFar: 定義創建面的遠距離。就是後面距離原點的距離
    正側投影(Orthographic Projection)
    正側投影,它的視錐爲一長方體,特點是物體的大小不隨到觀測點的距離而變化,投影后可以保持物體之間的距離和夾角。

     

     

    定義3D模型的前面和後面

    下面代碼設置逆時針方法爲面的“前面”:gl.glFrontFace(GL10.GL_CCW);
     
    打開 忽略“後面”設置:gl.glEnable(GL10.GL_CULL_FACE);然後明確指明“忽略“哪個面的代碼如下:
    gl.glCullFace(GL10.GL_BACK);
     

    FrameBuffer、Depth Buffer

    OpenGL ES 中的FrameBuffer 指的是存儲像素的內存空間。對應一個二維圖像,如果屏幕分辨率爲1280X1024 ,如果屏幕支持24位真彩色 (RGB),則存儲這個屏幕區域的內存至少需要1024X1280X3個字節。此外如果需要支持透明度(Alpha),則一個像素需要4個字節。
    在最終OpenGL ES寫入這些Buffer時,OpenGL ES提供一些Mask 函數可以控制Color Buffer 中RGBA通道,是否允許寫入Depth Buffer 等,這些Mask 函數可以打開或是關閉某個通道,只有通道打開後,對應的分量纔會寫入指定Buffer,比如你可以關閉紅色通道,這樣最後寫道Color Buffer中就不含有紅色。
     
    OpenGL ES 中Depth Buffer 保存了像素與觀測點之間的距離信息,在繪製3D圖形時,將只繪製可見的面而不去繪製隱藏的面,這個過程叫”Hidden surface removal” ,採用的算法爲”The depth buffer algorithm”。
    The depth buffer algorithm 在OpenGL ES 3D繪製的過程中這個算法是自動被採用的,但是瞭解這個算法有助於理解OpenGL ES 部分API的使用。

    下面給出了OpenGL ES中與Depth Buffer相關的幾個方法:

    • gl.Clear(GL10.GL_DEPTH_BUFFER_BIT) 清空Depth Buffer (賦值爲1.0)通常清空Depth Buffer和Color Buffer同時進行。
    • gl.glClearDepthf(float depth) 指定清空Depth Buffer是使用的值,缺省爲1.0,通常無需改變這個值,
    • gl.glEnable(GL10.GL_DEPTH_TEST) 打開depth Test
    • gl.glDisable(GL10.GL_DEPTH_TEST) 關閉depth Test
     

    OpenGL光照模型

    爲了能看出3D效果,給場景中添加光源。如果沒有光照,繪出的球看上去和一個二維平面上圓沒什麼差別
    OpenGL 光照模型中最終的光照效果可以分爲四個組成部分:Emitted(光源), ambient(環境光),diffuse(漫射光)和specular(鏡面反射光),最終結果由這四種光疊加而成。

    Emitted : 一般只發光物體或者光源,這種光不受其它光源的影響。

    ambient: 環境光如果射到某個平面,其反射方向爲所有方向。Ambient 沒有位置方向,只有顏色。

    diffuse:當一束平行的入射光線射到粗糙的表面時,因面上凹凸不平,所以入射線雖然互相平行,由於各點的法線方向不一致,造成反射光線向不同的方向無規則地反射,這種反射稱之爲“漫反射”或“漫射”。這個反射的光則稱爲漫射光。漫射光射到某個平面時,其反射方向也爲所有方向。diffuse 只依賴於光源的方向和法線的方向。

    specular : 一般指物體被光源直射的高亮區域,也可以成爲鏡面反射區,如金屬。specular依賴於光源的方向,法線的方向和視角的方向。
     
     
     

    設置光照效果Set Lighting

    OpenGL ES API如何使用光照效果:
    • 設置光源
    • 定義法線
    • 設置物體材料光學屬性

    光源

    OpenGL ES中可以最多同時使用八個光源,分別使用0到7表示。

    OpenGL ES光源可以分爲

    • 平行光源(Parallel light source), 代表由位於無限遠處均勻發光體,太陽可以近似控制平行光源。
    • 點光源(Spot light source)  如燈泡就是一個點光源,發出的光可以指向360度,可以爲點光源設置光衰減屬性(attenuation)或者讓點光源只能射向某個方向(如射燈)。
    下面方法可以打開某個光源,使用光源首先要開光源的總開關:gl.glEnable(GL10.GL_LIGHTING);
    然後可以再打開某個光源如0號光源:gl.glEnable(GL10.GL_LIGHT0);
    設置光源方法如下:
    • public void glLightfv(int light,int pname, FloatBuffer params)
    • public void glLightfv(int light,int pname,float[] params,int offset)
    • public void glLightf(int light,int pname,float param)
    • light 指光源的序號,OpenGL ES可以設置從0到7共八個光源。
    • pname: 光源參數名稱,可以有如下:GL_SPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_ATTENUATION,  GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION, GL_AMBIENT, GL_DIFFUSE,GL_SPECULAR, GL_SPOT_DIRECTION, GL_POSITION
    • params 參數的值(數組或是Buffer類型)。
    其中爲光源設置顏色的參數類型爲上述藍色值,可以分別指定R,G,B,A 的值。
    指定光源的位置的參數爲GL_POSITION,值爲(x,y,z,w):平行光將w 設爲0.0,(x,y,z)爲平行光的方向。
     
    法線
    在場景中設置好光源後,下一步要爲所繪製的圖形設置法線(Normal),只有設置了法線,光源才能在所會物體上出現光照效果。三維平面的法線是垂直於該平面的三維向量。曲面在某點P處的法線爲垂直於該點切平面的向量

     

    打開法線數組gl.glEnableClientState(GL10.GL_NORMAL_ARRAY); 

     
    和設置顏色類似,有兩個方法可以爲平面設置法線,一是public void glNormal3f(float nx,float ny,float nz)

    這個方法爲後續所有平面設置同樣的方向,直到重新設置新的法線爲止。

    爲某個頂點設置法線:public void glNormalPointer(int type,int stride, Buffer pointer)

    • type  爲Buffer 的類型,可以爲GL_BYTE, GL_SHORT, GL_FIXED,或 GL_FLOAT
    • stride: 每個參數之間的間隔。
    • pointer: 法線值。
    規範化法向量,比如使用座標變換(縮放),如果三個方向縮放比例不同的話,頂點或是平面的法線可能就有變化,此時需要打開規範化法線設置:gl.glEnable(GL10.GL_NORMALIZE);
    經過規範化後法向量爲單位向量(長度爲1)。同時可以打開縮放法線設置gl.glEnable(GL10.GL_RESCALE_NORMAL);
     
    設置物體材料光學屬性

    設置物體表面材料(Material)的反光屬性(顏色和材質)的方法如下:

            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, ambient, 0); 
            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, diffuse, 0); 
            gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, specular, 0);  

     

            gl.glEnable(GL10.GL_COLOR_MATERIAL);  

     

    此外,方法glLightModleXX給出了光照模型的參數

    public void glLightModelf(int pname,float param)
    public void glLightModelfv(int pname,float[] params,int offset)
    public void glLightModelfv(int pname,FloatBuffer params)

    • pname: 參數類型,可以爲GL_LIGHT_MODEL_AMBIENT和GL_LIGHT_MODEL_TWO_SIDE
    • params: 參數的值。

    最終頂點的顏色由這些參數(光源,材質光學屬性,光照模型)綜合決定(光照方程計算出)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章