OpenGL ES 繪制基礎圖形
OpenGL ES基本概念
OpenGL ES (Embedded-System) 是專為嵌入式設備(如手機、平板、VR 設備)設計的圖形 API,是 OpenGL 的輕量級版本。
|下面是一個Android使用 OpenGL ES的基本框架
- MainActivity 設置一個 GLSurfaceView 直接顯示
class MainActivity : ComponentActivity() {private lateinit var glSurfaceView: GLSurfaceViewoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)glSurfaceView = GLSurfaceView(this)// 設置渲染器glSurfaceView.setRenderer(GLRender())setContentView(glSurfaceView)}override fun onResume() {super.onResume()glSurfaceView.onResume()}override fun onPause() {super.onPause()glSurfaceView.onPause()}
}
- GLRender 著色器代碼已經其內部函數含義,由于沒有繪制樣式,運行后顯示黑色屏幕,因為onSurfaceCreated時將顏色重制為黑色。
class GLRender : Renderer {/*** 應用程序窗口創建時調用的函數* 一般處理一些全局的設置*/override fun onSurfaceCreated(gl: GL10, config: EGLConfig?) {// 設置需要對透視進行修正gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST)// 設置清理屏幕顏色 RGBA 取值范圍為 0-1之間的float數 0 0 0 就是黑色gl.glClearColor(0F, 0F, 0F, 1F)// 啟動深度緩存 如果不啟動深度緩存效果,后繪制的東西可能會覆蓋先繪制的東西。// 啟動深度緩存后,按照實際Z值來處理遠近效果,更符合3D效果。gl.glEnable(GL10.GL_DEPTH_TEST)}/*** 應用程序窗口改變時調用的函數*/override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {val ratio: Float = width / height.toFloat()// 設置GL場景的大小gl.glViewport(0, 0, width, height)// 設置投影矩陣為投影矩陣// 定義視錐體(View Frustum)和投影方式(透視或正交),決定物體如何從 3D 空間投影到 2D 屏幕上。// 其他矩陣類型還有模型矩陣,紋理矩陣等gl.glMatrixMode(GL10.GL_PROJECTION)// 重制投影矩陣// 作用是將當前矩陣重置為單位矩陣(Identity Matrix)。// 若不調用該函數,矩陣會保留之前的變換狀態,可能導致后續變換疊加在已有變換上,產生非預期的結果gl.glLoadIdentity()// 創建一個透視投影矩陣,設置窗口大小gl.glFrustumf(-ratio, ratio, -1F, 1F, 1F, 10F)}/*** Android在圖形繪制時調用的方法,除非設置手動刷新,否則會一直調用。*/override fun onDrawFrame(gl: GL10) {// 清理緩存 顏色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 設置模型視圖矩陣gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩陣gl.glLoadIdentity()// 視角變換,就是觀察的方向,眼睛的位置,中心點的位置 和 視線的朝向GLU.gluLookAt(gl,0F,0F,3F,0F,0F,0F,0F,1F,0F)}
}
通過 OpenGL ES 完成 2D圖形的繪制。
- OpenGL的坐標向線如下:
繪制三角形
private val one: Int = 0x10000// 正確創建直接緩沖區private val triggerBuffer: IntBuffer by lazy {// 創建直接緩沖區并設置本地字節序val byteBuffer = ByteBuffer.allocateDirect(3 * 3 * 4) // 3個頂點,每個3個分量,每個int占4字節byteBuffer.order(ByteOrder.nativeOrder())// 獲取 IntBuffer 視圖val intBuffer = byteBuffer.asIntBuffer()// 填充頂點數據intBuffer.put(intArrayOf(0, one, 0, // 頂點1-one, -one, 0, // 頂點2one, -one, 0 // 頂點3))// 重置位置到起始點intBuffer.position(0)intBuffer}
override fun onDrawFrame(gl: GL10) {// 清理緩存 顏色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 設置模型視圖矩陣gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩陣gl.glLoadIdentity()// 視角變換,就是觀察的方向,眼睛的位置,中心點的位置 和 視線的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 允許設置頂點gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 給三角形設置頂點數組// size 含義代表每個點有 xyz三個方向數據所以size為3;// GL10.GL_FIXED 為整形數據 還可以設置 GL_FLOAT 浮點型,以及其他類型,甚至是無符號類型// 第三個參數代表相鄰頂點之間的偏移量,0 表示緊密排列。即下一個頂點數據緊跟當前頂點數據。非0的話代表交替存儲頂點數據額外便宜的字節。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 繪制三角形 GL_TRIANGLES 代表三角形,開始0 繪制3個點。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 關閉頂點設置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}
上述代碼繪制了一個三角形如下:
需要注意的是創建三角形頂點使用的buffer必須使用直接緩沖區,提高數據讀取的速度。
- 繪制正方形 , 三角形右側增加繪制正方形。
// 創建正方形直接緩沖區private val squareBuffer: IntBuffer by lazy {// 創建直接緩沖區并設置本地字節序val byteBuffer = ByteBuffer.allocateDirect(4 * 3 * 4) // 4個頂點,每個3個分量,每個int占4字節byteBuffer.order(ByteOrder.nativeOrder())// 獲取 IntBuffer 視圖val intBuffer = byteBuffer.asIntBuffer()// 填充頂點數據intBuffer.put(intArrayOf(one, one, 0, // 頂點1-one, one, 0, // 頂點2one, -one, 0, // 頂點3-one, -one, 0 // 頂點4))// 重置位置到起始點intBuffer.position(0)intBuffer}/*** Android在圖形繪制時調用的方法,除非設置手動刷新,否則會一直調用。*/override fun onDrawFrame(gl: GL10) {// 清理緩存 顏色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 設置模型視圖矩陣gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩陣gl.glLoadIdentity()// 視角變換,就是觀察的方向,眼睛的位置,中心點的位置 和 視線的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 設置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 允許設置頂點gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 給三角形設置頂點數組// size 含義代表每個點有 xyz三個方向數據所以size為3;// GL10.GL_FIXED 為整形數據 還可以設置 GL_FLOAT 浮點型,以及其他類型,甚至是無符號類型// 第三個參數代表相鄰頂點之間的偏移量,0 表示緊密排列。即下一個頂點數據緊跟當前頂點數據。非0的話代表交替存儲頂點數據額外便宜的字節。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 繪制三角形 GL_TRIANGLES 代表三角形,開始0 繪制3個點。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 重制模型矩陣gl.glLoadIdentity()// 繪制正方形 兩個相同的三角形組成正方形,可以采用三角形的帶// 設置模型位置 為了顯示效果更小所以 Z 周弄遠了。Z越大看起來離我們越近顯示效果越大(也可以通過坐標修改)gl.glTranslatef(1F, 0.0F, -6.0F)// 設置頂點數據gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 繪制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4)// 關閉頂點設置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}
繪制三角形以后,重制模型矩陣gl.glLoadIdentity() ,設置模型位置 gl.glTranslatef,設置頂點數據gl.glVertexPointer,繪制正方形。gl.glDrawArrays。
效果如下
在0penGL中繪制2D多邊形常用的函數以及常量:
- glEnableClientState/glDisableClientState:狀態開關
- glVertexPointer:設置頂點數據
- glDrawArrays:繪制函數
- GL_VERTEX_ARRAY:頂點數組
- GL_BYTE/GL_SHORT/GL_FIXED/GL_FLOAT:頂點數據的類理
- GL_LINES:線
- GL_TRIANGLES:三角形
- GL_TRIANGLE_STRIP:三角形帶
如何繪制顏色
// 創建顏色buffer 分為 RGBA private val colorBuffer: IntBuffer by lazy {// 創建直接緩沖區并設置本地字節序val byteBuffer = ByteBuffer.allocateDirect(3 * 4 * 4)byteBuffer.order(ByteOrder.nativeOrder())// 獲取 IntBuffer 視圖val intBuffer = byteBuffer.asIntBuffer()// 填充頂點數據intBuffer.put(intArrayOf(one, 0, 0, one, // 頂點10, one, 0, one, // 頂點20, 0, one, one // 頂點3))// 重置位置到起始點intBuffer.position(0)intBuffer}override fun onDrawFrame(gl: GL10) {// 清理緩存 顏色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 設置模型視圖矩陣gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩陣gl.glLoadIdentity()// 視角變換,就是觀察的方向,眼睛的位置,中心點的位置 和 視線的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 設置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 允許設置頂點gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 允許設置顏色數組gl.glEnableClientState(GL10.GL_COLOR_ARRAY)gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)// 給三角形設置頂點數組// size 含義代表每個點有 xyz三個方向數據所以size為3;// GL10.GL_FIXED 為整形數據 還可以設置 GL_FLOAT 浮點型,以及其他類型,甚至是無符號類型// 第三個參數代表相鄰頂點之間的偏移量,0 表示緊密排列。即下一個頂點數據緊跟當前頂點數據。非0的話代表交替存儲頂點數據額外便宜的字節。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 繪制三角形 GL_TRIANGLES 代表三角形,開始0 繪制3個點。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 繪制完成禁用顏色數組繪制gl.glDisableClientState(GL10.GL_COLOR_ARRAY)// 重制模型矩陣gl.glLoadIdentity()// 繪制正方形 兩個相同的三角形組成正方形,可以采用三角形的帶// 設置模型位置 為了顯示效果更小所以 Z 周弄遠了。Z越大看起來離我們越近顯示效果越大gl.glTranslatef(1F, 0.0F, -6.0F)// 設置頂點數據gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 設置顏色,R G B A 的色值gl.glColor4f(1F, 0F, 0F, 1F)// 繪制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)// 關閉頂點設置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)}
// 設置顏色,R G B A 的色值
gl.glColor4f(1F, 0F, 0F, 1F) 這里只給紅色和透明度設置成1 顯示效果為紅色
給正方形上色,直接采用設置RGBA的方式,單一的顏色
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer) 給三角形繪制平滑過渡的顏色,需要開啟設置顏色數組,使用結束后關閉。
效果如下:
- 顏色所需要的常用函數
常用函數及常量
- glColor4f:設置單一顏色
- glColorPointer:設置顏色數組
- GL_COLOR_ARRAY:顏色數組(通過狀態開關函數
- glDisableClientState 來操作)
- GL_COLOR_BUFFER_BIT:顏色緩存
圖形的變換
OpenGLES有三種不同類型的變換,它們分別是:轉移(Translate):在3D空間中移動物體旋轉(Rotate):繞X,Y,或者 Z 軸進行旋轉縮放(Scale):改變物體的大小
- 為了防止上述三角形和正方形的位置重疊,進行了 gl.glTranslatef(1F, 0.0F, -6.0F) 這就是平移操作。
gl.glTranslatef(1F, 0.0F, -6.0F) 代表分別在 x y z上分別平移多少距離。
- 旋轉操作
/*** 定義旋轉角度*/var rotate1: Float = 0Fvar rotate2: Float = 0F/*** Android在圖形繪制時調用的方法,除非設置手動刷新,否則會一直調用。*/override fun onDrawFrame(gl: GL10) {// 清理緩存 顏色和深度gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)// 設置模型視圖矩陣gl.glMatrixMode(GL10.GL_MODELVIEW)// 重制矩陣gl.glLoadIdentity()// 視角變換,就是觀察的方向,眼睛的位置,中心點的位置 和 視線的朝向GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)// 設置模型位置gl.glTranslatef(-2.0F, 0.0F, -4.0F)// 設置旋轉角度 設置成了繞Y軸旋轉gl.glRotatef(rotate1, 0F, 1F, 0F)// 允許設置頂點gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)// 允許設置顏色數組gl.glEnableClientState(GL10.GL_COLOR_ARRAY)gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)// 給三角形設置頂點數組// size 含義代表每個點有 xyz三個方向數據所以size為3;// GL10.GL_FIXED 為整形數據 還可以設置 GL_FLOAT 浮點型,以及其他類型,甚至是無符號類型// 第三個參數代表相鄰頂點之間的偏移量,0 表示緊密排列。即下一個頂點數據緊跟當前頂點數據。非0的話代表交替存儲頂點數據額外便宜的字節。gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)// 繪制三角形 GL_TRIANGLES 代表三角形,開始0 繪制3個點。gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)// 繪制完成禁用顏色數組繪制gl.glDisableClientState(GL10.GL_COLOR_ARRAY)// 重制模型矩陣gl.glLoadIdentity()// 繪制正方形 兩個相同的三角形組成正方形,可以采用三角形的帶// 設置模型位置 為了顯示效果更小所以 Z 周弄遠了。Z越大看起來離我們越近顯示效果越大gl.glTranslatef(1F, 0.0F, -6.0F)// 設置繞X軸旋轉gl.glRotatef(rotate2, 1F, 0F, 0F)// 設置頂點數據gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)// 設置顏色,R G B A 的色值gl.glColor4f(1F, 0F, 0F, 1F)// 繪制正方形gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)// 關閉頂點設置gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)// 改變旋轉角度rotate1 += 0.5Frotate2 += 0.5F}
上端代碼實際上增加了 gl.glRotatef(rotate2, 1F, 0F, 0F) ,參數依次是 角度,x軸 y軸 z軸旋轉。看上圖效果實際上是動旋轉,三角Y軸旋轉,正方形一直繞x軸旋轉。然后通過改變第一個參數的角度,一直旋轉。
- 放大
// 增加如下代碼即可 x y z 放大2倍
gl.glScalef(2.0F,2.0F,2.0F)
總結
2D基本圖形的繪制;坐標系;著色方式包括單一著色,平滑著色;圖形的平移、旋轉、縮放。