Android OpenGL ES 2.0 手把手教學(xué)(1) - Hello World!

大家好,下面和大學(xué)一起學(xué)習(xí)Android OpenGL ES 2.0的入門(mén)Hello World哥蔚,在我的github上有一個(gè)項(xiàng)目OpenGLES2.0SamplesForAndroid铝量,我會(huì)不斷地編寫(xiě)學(xué)習(xí)樣例钮孵,文章和代碼同步更新强经,歡迎關(guān)注南片,鏈接:https://github.com/kenneycode/OpenGLES2.0SamplesForAndroid

下面開(kāi)始我們的Hello World之旅掺涛,我們將渲染一個(gè)三角形,為什么要渲染一個(gè)三角形呢疼进?三角形在OpenGL中是很重要的薪缆,實(shí)際上我們看到的那些復(fù)雜圖形,它們?cè)贠penGL里都 是通過(guò)多個(gè)三角形組合而成的伞广,因此我們先來(lái)學(xué)習(xí)如何渲染一個(gè)三角形~

要在Android上進(jìn)行OpenGL渲染拣帽,首先要有GL環(huán)境,什么是GL環(huán)境嚼锄?后面我會(huì)寫(xiě)文章解析减拭,現(xiàn)在只需要知道有這回事就行了。為了簡(jiǎn)單起見(jiàn)区丑,我們直接使用Android的GLSurfaceView拧粪,它就自帶了GL環(huán)境。

我們?cè)?code>layout中寫(xiě)一個(gè)GLSurfaceView然后find出來(lái)沧侥,這是Android的常規(guī)操作可霎,就不多做解釋了。然后給GLSurfaceView做一些配置宴杀,現(xiàn)在暫時(shí)不用管這些配置的用途癣朗,后面也會(huì)有文章解析~

val glSurfaceView = findViewById<GLSurfaceView>(R.id.glsurfaceview)
// 設(shè)置RGBA顏色緩沖、深度緩沖及stencil緩沖大小
// Set the size of RGBA旺罢、depth and stencil buffer
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 0, 0)
// 設(shè)置GL版本旷余,這里設(shè)置為2.0
// Set GL version, here I set it to 2.0
glSurfaceView.setEGLContextClientVersion(2)

然后給GLSurfaceView設(shè)置Renderer,這個(gè)Renderer就是用于做渲染的主经,可以把GLSurfaceView理解成就是一塊畫(huà)板荣暮,具體怎么畫(huà),是在Renderer里做的

glSurfaceView.setRenderer(SampleHelloWorld())

我們讓SampleHelloWorld實(shí)現(xiàn)GLSurfaceView.Renderer接口罩驻,將渲染邏輯寫(xiě)在SampleHelloWorld中穗酥,共有3個(gè)方法需要實(shí)現(xiàn):

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()是渲染時(shí)的回調(diào),我們的渲染邏輯就是在這里面寫(xiě)
onSurfaceChanged()是在GLSurfaceView寬高改變時(shí)會(huì)回調(diào),一般可以在這里記錄最新的寬高
onSurfaceCreated()GLSurfaceView創(chuàng)建好時(shí)會(huì)回調(diào)砾跃,一般可以在里面寫(xiě)一些初始化邏輯
我們先在onSurfaceChanged()中加上一些邏輯記錄最新的寬高:

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        // 記錄GLSurfaceView的寬高
        // Record the width and height of the GLSurfaceView
        glSurfaceViewWidth = width
        glSurfaceViewHeight = height
}

接下來(lái)重點(diǎn)看onSurfaceCreated()onDrawFrame()骏啰,我們先在onSurfaceCreated()做一些初始化邏輯然后在onDrawFrame()中執(zhí)行渲染

1.調(diào)用GLES20.glCreateProgram()創(chuàng)建OpenGL程序,我們將得到一個(gè)programId

// 創(chuàng)建GL程序
// Create GL program
val programId = GLES20.glCreateProgram()

2.加載抽高、編譯vertex shaderfragment shader
什么是vertex shaderfragment shader判耕?vertex shader是頂點(diǎn)著色器,OpenGL執(zhí)行渲染時(shí)會(huì)對(duì)每個(gè)頂點(diǎn)執(zhí)行一次翘骂,我們可以在其中做一些頂點(diǎn)變換等操作壁熄,fragment shader就片元著色器,OpenGL執(zhí)行渲染時(shí)會(huì)在柵格化后對(duì)每個(gè)片元執(zhí)行一次片元著色器碳竟,我們可以在其中決定每個(gè)像素輸出的顏色
關(guān)于vertex shaderfragment shader草丧,后面也會(huì)有文章解析,這里就大概了解就行了~
我們寫(xiě)兩個(gè)最簡(jiǎn)單的shader:

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中我們將輸入的頂點(diǎn)原樣輸出莹桅,不做任何變換昌执,在fragment shader中將輸出顏色設(shè)置為RGBA值為(0.0, 0.0, 1.0, 1.0)的顏色,即無(wú)透明度的藍(lán)色
shader代碼和我們的普通程序代碼類(lèi)似诈泼,也是需要編譯的懂拾,下面是具體的代碼:

// 加載、編譯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中創(chuàng)建好了一個(gè)具有簡(jiǎn)單功能的GL程序

5.應(yīng)用GL程序
創(chuàng)建好了GL程序岖赋,但并沒(méi)有在用它,我們?cè)谧鲆恍┎僮鞯臅r(shí)候娶桦, 比如向它的shader中傳遞數(shù)據(jù)贾节,先要讓OpenGL知道我們是對(duì)哪個(gè)GL程序在操作,因此需要先應(yīng)用GL程序

// 應(yīng)用GL程序
// Use the GL program
GLES20.glUseProgram(programId)

6.設(shè)置三角形頂點(diǎn)數(shù)據(jù)
我們的目標(biāo)是渲染一個(gè)三角形衷畦,下面向shader中的a_Position變量傳遞三角形頂點(diǎn)栗涂,首先構(gòu)造一個(gè)buffer用于承載頂點(diǎn)數(shù)據(jù),數(shù)據(jù)是按x祈争、y斤程、x、y...這樣排列的

// 三角形頂點(diǎn)數(shù)據(jù)
// The vertex data of a triangle
val vertexData = floatArrayOf(0f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f)

// 將三角形頂點(diǎn)數(shù)據(jù)放入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_Positionshader中的位置location菩混,這個(gè)location可以理解為就是GPU中代表a_Position一個(gè)位置標(biāo)識(shí)忿墅,之后操作a_Position都是通過(guò)這個(gè)location

// 獲取字段a_Position在shader中的位置
// Get the location of a_Position in the shader
val location = GLES20.glGetAttribLocation(programId, "a_Position")

獲取到location后要啟動(dòng)這個(gè)位置,這樣才能生效

// 啟動(dòng)對(duì)應(yīng)位置的參數(shù)
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(location)

然后就可以將頂點(diǎn)數(shù)據(jù)設(shè)置給a_Position了沮峡,這里glVertexAttribPointer()的第2個(gè)參數(shù)表示每個(gè)頂點(diǎn)由幾個(gè)float組成疚脐,在我們這個(gè)例子中是2個(gè),即x邢疙、y

// 指定a_Position所使用的頂點(diǎn)數(shù)據(jù)
// Specify the vertex data of a_Position
GLES20.glVertexAttribPointer(location, 2, GLES20.GL_FLOAT, false,0, buffer)

這樣我們就完成了初始化工作棍弄,渲染一個(gè)三角形需要的數(shù)據(jù)都準(zhǔn)備好了

7.執(zhí)行渲染
下面我們?cè)?code>onDrawFrame()中寫(xiě)渲染邏輯望薄,我們先設(shè)置清屏顏色并清屏,這樣背景就會(huì)變成我們?cè)O(shè)置的顏色呼畸,這里將背景清成灰色痕支,這一步在這個(gè)例子中不是必須的,如果不做清屏蛮原,那么背景就是黑色的卧须,如果要繪制可變的效果,比如一個(gè)運(yùn)動(dòng)的圖形儒陨,就需要清屏花嘶,否則之前所繪制的效果會(huì)殘留在上面

// 設(shè)置清屏顏色
// 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上框全,可以通過(guò)設(shè)置視口來(lái)控制渲染到GLSurfaceView的什么區(qū)域中察绷,這里我們渲染到整個(gè)GLSurfaceView

// 設(shè)置視口,這里設(shè)置為整個(gè)GLSurfaceView區(qū)域
// Set the viewport to the full GLSurfaceView
GLES20.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)

然后調(diào)用draw方法進(jìn)行渲染津辩,這里我們使用GL_TRIANGLES的方式進(jìn)行渲染,這個(gè)在后面的文章也會(huì)解析容劳,現(xiàn)在可以簡(jiǎn)單地理解為它是以每三個(gè)點(diǎn)為一個(gè)獨(dú)立三角形的方式來(lái)渲染

// 調(diào)用draw方法用TRIANGLES的方式執(zhí)行渲染喘沿,頂點(diǎn)數(shù)量為3個(gè)
// Call the draw method with GL_TRIANGLES to render 3 vertices
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3)

這樣,我們就渲染出來(lái)一個(gè)藍(lán)色三角形:

16996682a1239f0d.jpg

至此竭贩,我們的Hello World之旅館就圓滿結(jié)束了蚜印,可能其中還有許多的疑問(wèn),畢竟OpenGL入門(mén)還是有點(diǎn)難度的留量,沒(méi)關(guān)系窄赋,萬(wàn)事開(kāi)頭難

代碼在我github的OpenGLES2.0SamplesForAndroid項(xiàng)目中,本文對(duì)應(yīng)的是SampleHelloWorld楼熄,項(xiàng)目鏈接:https://github.com/kenneycode/OpenGLES2.0SamplesForAndroid

感謝閱讀忆绰!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市可岂,隨后出現(xiàn)的幾起案子错敢,更是在濱河造成了極大的恐慌,老刑警劉巖缕粹,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件稚茅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡平斩,警方通過(guò)查閱死者的電腦和手機(jī)亚享,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绘面,“玉大人欺税,你說(shuō)我怎么就攤上這事糜芳。” “怎么了魄衅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵峭竣,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我晃虫,道長(zhǎng)皆撩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任哲银,我火速辦了婚禮扛吞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘荆责。我一直安慰自己滥比,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布做院。 她就那樣靜靜地躺著盲泛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪键耕。 梳的紋絲不亂的頭發(fā)上寺滚,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音屈雄,去河邊找鬼村视。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酒奶,可吹牛的內(nèi)容都是我干的蚁孔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼惋嚎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼杠氢!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起瘸彤,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤修然,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后质况,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體愕宋,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年结榄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了中贝。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臼朗,死狀恐怖邻寿,靈堂內(nèi)的尸體忽然破棺而出蝎土,到底是詐尸還是另有隱情,我是刑警寧澤绣否,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布誊涯,位于F島的核電站,受9級(jí)特大地震影響蒜撮,放射性物質(zhì)發(fā)生泄漏暴构。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一段磨、第九天 我趴在偏房一處隱蔽的房頂上張望取逾。 院中可真熱鬧,春花似錦苹支、人聲如沸砾隅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)晴埂。三九已至,卻和暖如春策幼,著一層夾襖步出監(jiān)牢的瞬間邑时,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工特姐, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人黍氮。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓唐含,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沫浆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子捷枯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容