你們好,下面和大學一塊兒學習Android OpenGL ES 2.0的入門Hello World,在個人github上有一個項目OpenGLES2.0SamplesForAndroid
,我會不斷地編寫學習樣例,文章和代碼同步更新,歡迎關注,連接:github.com/kenneycode/…java
下面開始咱們的Hello World之旅,咱們將渲染一個三角形,爲何要渲染一個三角形呢?三角形在OpenGL中是很重要的,實際上咱們看到的那些複雜圖形,它們在OpenGL裏都 是經過多個三角形組合而成的,所以咱們先來學習如何渲染一個三角形~git
要在Android上進行OpenGL渲染,首先要有GL環境,什麼是GL環境?後面我會寫文章解析,如今只須要知道有這回事就好了。爲了簡單起見,咱們直接使用Android的GLSurfaceView,它就自帶了GL環境。github
咱們在layout
中寫一個GLSurfaceView
而後find出來,這是Android的常規操做,就很少作解釋了。而後給GLSurfaceView
作一些配置,如今暫時不用管這些配置的用途,後面也會有文章解析~ide
val glSurfaceView = findViewById<GLSurfaceView>(R.id.glsurfaceview)
// 設置RGBA顏色緩衝、深度緩衝及stencil緩衝大小
// Set the size of RGBA、depth and stencil buffer
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 0, 0)
// 設置GL版本,這裏設置爲2.0
// Set GL version, here I set it to 2.0
glSurfaceView.setEGLContextClientVersion(2)
複製代碼
而後給GLSurfaceView
設置Renderer
,這個Renderer
就是用於作渲染的,能夠把GLSurfaceView
理解成就是一塊畫板,具體怎麼畫,是在Renderer
裏作的學習
glSurfaceView.setRenderer(SampleHelloWorld())
複製代碼
咱們讓SampleHelloWorld
實現GLSurfaceView.Renderer
接口,將渲染邏輯寫在SampleHelloWorld
中,共有3個方法須要實現:ui
class SampleHelloWorld : GLSurfaceView.Renderer {
override fun onDrawFrame(gl: GL10?) {
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
}
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
}
}
複製代碼
其中: onDrawFrame()
是渲染時的回調,咱們的渲染邏輯就是在這裏面寫 onSurfaceChanged()
是在GLSurfaceView
寬高改變時會回調,通常能夠在這裏記錄最新的寬高 onSurfaceCreated()
在GLSurfaceView
建立好時會回調,通常能夠在裏面寫一些初始化邏輯 咱們先在onSurfaceChanged()
中加上一些邏輯記錄最新的寬高:spa
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 記錄GLSurfaceView的寬高
// Record the width and height of the GLSurfaceView
glSurfaceViewWidth = width
glSurfaceViewHeight = height
}
複製代碼
接下來重點看onSurfaceCreated()
和onDrawFrame()
,咱們先在onSurfaceCreated()
作一些初始化邏輯而後在onDrawFrame()
中執行渲染code
1.調用GLES20.glCreateProgram()
建立OpenGL程序,咱們將獲得一個programId
cdn
// 建立GL程序
// Create GL program
val programId = GLES20.glCreateProgram()
複製代碼
2.加載、編譯vertex shader
和fragment shader
什麼是vertex shader
和fragment shader
?vertex shader
是頂點着色器,OpenGL執行渲染時會對每一個頂點執行一次,咱們能夠在其中作一些頂點變換等操做,fragment shader
就片元着色器,OpenGL執行渲染時會在柵格化後對每一個片元執行一次片元着色器,咱們能夠在其中決定每一個像素輸出的顏色 關於vertex shader
和fragment shader
,後面也會有文章解析,這裏就大概瞭解就好了~ 咱們寫兩個最簡單的shader
:blog
private val vertexShaderCode =
"precision mediump float;\n" +
"attribute vec4 a_Position;\n" +
"void main() {\n" +
" gl_Position = a_Position;\n" +
"}"
private val fragmentShaderCode =
"precision mediump float;\n" +
"void main() {\n" +
" gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);\n" +
"}"
複製代碼
在vertex shader
中咱們將輸入的頂點原樣輸出,不作任何變換,在fragment shader
中將輸出顏色設置爲RGBA
值爲(0.0, 0.0, 1.0, 1.0)
的顏色,即無透明度的藍色 shader
代碼和咱們的普通程序代碼相似,也是須要編譯的,下面是具體的代碼:
// 加載、編譯vertex shader和fragment shader
// Load and compile vertex shader and fragment shader
val vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
val fragmentShader= GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
GLES20.glShaderSource(vertexShader, vertexShaderCode)
GLES20.glShaderSource(fragmentShader, fragmentShaderCode)
GLES20.glCompileShader(vertexShader)
GLES20.glCompileShader(fragmentShader)
複製代碼
3.將shader
程序附着到GL程序上
// 將shader程序附着到GL程序上
// Attach the compiled shaders to the GL program
GLES20.glAttachShader(programId, vertexShader)
GLES20.glAttachShader(programId, fragmentShader)
複製代碼
4.連接GL程序
// 連接GL程序
// Link the GL program
GLES20.glLinkProgram(programId)
複製代碼
至此,咱們就在GPU中建立好了一個具備簡單功能的GL程序
5.應用GL程序 建立好了GL程序,但並無在用它,咱們在作一些操做的時候, 好比向它的shader中傳遞數據,先要讓OpenGL知道咱們是對哪一個GL程序在操做,所以須要先應用GL程序
// 應用GL程序
// Use the GL program
GLES20.glUseProgram(programId)
複製代碼
6.設置三角形頂點數據 咱們的目標是渲染一個三角形,下面向shader
中的a_Position
變量傳遞三角形頂點,首先構造一個buffer用於承載頂點數據,數據是按x、y、x、y...
這樣排列的
// 三角形頂點數據
// The vertex data of a triangle
val vertexData = floatArrayOf(0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f)
// 將三角形頂點數據放入buffer中
// Put the triangle vertex data into the buffer
val buffer = ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
buffer.put(vertexData)
buffer.position(0)
複製代碼
而後獲取a_Position
在shader
中的位置location
,這個location
能夠理解爲就是GPU中表明a_Position
一個位置標識,以後操做a_Position
都是經過這個location
// 獲取字段a_Position在shader中的位置
// Get the location of a_Position in the shader
val location = GLES20.glGetAttribLocation(programId, "a_Position")
複製代碼
獲取到location
後要啓動這個位置,這樣才能生效
// 啓動對應位置的參數
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(location)
複製代碼
而後就能夠將頂點數據設置給a_Position
了,這裏glVertexAttribPointer()
的第2個參數表示每一個頂點由幾個float
組成,在咱們這個例子中是2個,即x、y
// 指定a_Position所使用的頂點數據
// Specify the vertex data of a_Position
GLES20.glVertexAttribPointer(location, 2, GLES20.GL_FLOAT, false,0, buffer)
複製代碼
這樣咱們就完成了初始化工做,渲染一個三角形須要的數據都準備好了
7.執行渲染 下面咱們在onDrawFrame()
中寫渲染邏輯,咱們先設置清屏顏色並清屏,這樣背景就會變成咱們設置的顏色,這裏將背景清成灰色,這一步在這個例子中不是必須的,若是不作清屏,那麼背景就是黑色的,若是要繪製可變的效果,好比一個運動的圖形,就須要清屏,不然以前所繪製的效果會殘留在上面
// 設置清屏顏色
// Set the color which the screen will be cleared to
GLES20.glClearColor(0.9f, 0.9f, 0.9f, 1f)
// 清屏
// Clear the screen
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
複製代碼
視口大小,渲染到GLSurfaceView
上,能夠經過設置視口來控制渲染到GLSurfaceView
的什麼區域中,這裏咱們渲染到整個GLSurfaceView
上
// 設置視口,這裏設置爲整個GLSurfaceView區域
// Set the viewport to the full GLSurfaceView
GLES20.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)
複製代碼
而後調用draw
方法進行渲染,這裏咱們使用GL_TRIANGLES
的方式進行渲染,這個在後面的文章也會解析,如今能夠簡單地理解爲它是以每三個點爲一個獨立三角形的方式來渲染
// 調用draw方法用TRIANGLES的方式執行渲染,頂點數量爲3個
// Call the draw method with GL_TRIANGLES to render 3 vertices
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3)
複製代碼
這樣,咱們就渲染出來一個藍色三角形:
至此,咱們的Hello World之旅館就圓滿結束了,可能其中還有許多的疑問,畢竟OpenGL入門仍是有點難度的,不要緊,萬事開頭難
代碼在我github的OpenGLES2.0SamplesForAndroid
項目中,本文對應的是SampleHelloWorld
,項目連接:github.com/kenneycode/…
感謝閱讀!