Android OpenGL ES 2.0 入門第二課

本文目錄

一. 前言
二. OpenGL ES中的形狀
三. 渲染管線的流程
四. 讀取頂點(diǎn)數(shù)據(jù) Vertex Data
五. 頂點(diǎn)著色器 Vertex Shader
五. 組裝圖元 Shape Assembly
五. 光柵化 Rasterization
六. 片段著色器 Fragment Shader
七. 測(cè)試與混合 Tests and Blending
八. 幀緩存 / 渲染緩存

前言

在上一課的學(xué)習(xí)里面锉罐,我們簡(jiǎn)單的認(rèn)識(shí)了OpenGL ES2.0程序的基本寫法,必須要有GLSurfaceView渲染表面和GLSurfaceView.Renderer渲染器才可以實(shí)現(xiàn)寸士。同時(shí)我們也清楚知道OpenGLES繪畫的內(nèi)容主要在Rederer里面去編寫权悟,那么我們這次就學(xué)習(xí)實(shí)現(xiàn)二維的形狀——三角形锌订。在學(xué)習(xí)之前也要學(xué)習(xí)一些知識(shí)點(diǎn):管線渲染的流程、著色器編程GLSL急膀,它們的作用非常大沮协,所以一定要好好學(xué)。


OpenGL ES中的形狀

在OpenGL ES中只有點(diǎn)卓嫂、直線三角形慷暂,點(diǎn)和直線可以用于某些效果,但是晨雳,只有三角形才能用來構(gòu)建復(fù)雜的對(duì)象和紋理的場(chǎng)景行瑞。具體使用是將點(diǎn)放到一個(gè)組里構(gòu)建出三角形,再告訴OpenGL ES 如何連接這些點(diǎn)餐禁。如果想要構(gòu)建出更復(fù)雜的圖形血久,例如拱形,圓球等等帮非,那么我們就需要足夠的點(diǎn)擬合這樣的曲線氧吐。

三角形繪制球體

渲染管線的流程

在 OpenGL ES 的 3D 空間中讹蘑,屏幕和窗口卻是 2D 像素?cái)?shù)組,這就導(dǎo)致 OpenGL ES 的大部分工作都是關(guān)于把 3D 坐標(biāo)轉(zhuǎn)變?yōu)檫m應(yīng)屏幕的 2D 像素筑舅。3D 坐標(biāo)轉(zhuǎn)為 2D 坐標(biāo)的處理過程是由 OpenGL ES 的圖形渲染管線(Graphics Pipeline座慰,實(shí)際上指的是一堆原始圖形數(shù)據(jù)途經(jīng)一個(gè)輸送管道,期間經(jīng)過各種變化處理最終出現(xiàn)在屏幕的過程)管理的豁翎。圖形渲染管線可以被劃分為兩個(gè)主要部分:

  • 第一部分是把 3D 坐標(biāo)轉(zhuǎn)換成 2D 坐標(biāo)角骤;
  • 第二部分是將 2D 坐標(biāo)轉(zhuǎn)換成有顏色的像素

2D 坐標(biāo)和像素的區(qū)別:2D 坐標(biāo)精確表示一個(gè)點(diǎn)在 2D 空間中的位置心剥,而 2D 像素是這個(gè)點(diǎn)的近似值,2D 像素受到屏幕/窗口分辨率的限制背桐。

OpenGL ES 采用C/S編程模型优烧,客戶端運(yùn)行在 CPU 上,服務(wù)端運(yùn)行在 GPU 上链峭,調(diào)用 OpenGL ES 函數(shù)的時(shí)畦娄,由客戶端發(fā)送至服務(wù)器端,并被服務(wù)端轉(zhuǎn)換成底層圖形硬件支持的繪制命令弊仪。

渲染管線的流程

讀取頂點(diǎn)數(shù)據(jù) Vertex Data

為了渲染一個(gè)三角形熙卡,我們以數(shù)組的形式傳遞3個(gè) 3D 坐標(biāo)作為圖形渲染管線的輸入,用來表示一個(gè)三角形励饵,這個(gè)數(shù)組叫做頂點(diǎn)數(shù)據(jù)(Vertex Data)驳癌;

頂點(diǎn)數(shù)據(jù)
//設(shè)置三角形顏色和透明度(r,g,b,a),綠色不透明
float color[] = {0.0f, 1.0f, 0.5f, 1.0f};
//設(shè)置三角形頂點(diǎn)數(shù)組,默認(rèn)按逆時(shí)針方向繪制
static float triangleCoords[] = {
        0.0f, 0.5f, 0.0f, // 頂點(diǎn)
        -0.5f, -0.5f, 0.0f, // 左下角
        0.5f, -0.5f, 0.0f  // 右下角
};

頂點(diǎn)數(shù)據(jù)是一系列頂點(diǎn)的集合。一個(gè)頂點(diǎn)(Vertex)是一個(gè) 3D 坐標(biāo)的數(shù)據(jù)的集合役听。而頂點(diǎn)數(shù)據(jù)是用頂點(diǎn)屬性(Vertex Attribute)表示颓鲜,它可以包含任何我們想用的數(shù)據(jù),但是簡(jiǎn)單起見典予,我們假定每個(gè)頂點(diǎn)只由一個(gè) 3D 位置和一些顏色值組成甜滨。

public static FloatBuffer floatBuffer;
public static FloatBuffer fBuffer(float[] a) {
    //先初始化buffer,數(shù)組的長(zhǎng)度*4,因?yàn)橐粋€(gè)float占4個(gè)字節(jié)
    ByteBuffer mbb = ByteBuffer.allocateDirect(a.length * 4);
    //數(shù)組排列用nativeOrder,設(shè)置字節(jié)順序?yàn)楸镜夭僮飨到y(tǒng)順序
    mbb.order(ByteOrder.nativeOrder());
    //轉(zhuǎn)換為浮點(diǎn)型緩沖
    floatBuffer = mbb.asFloatBuffer();
    //在緩沖區(qū)內(nèi)寫入數(shù)據(jù)
    floatBuffer.put(a);
    //設(shè)置緩沖區(qū)起始位置
    floatBuffer.position(0);
    return floatBuffer;
}

以上就是讀取頂點(diǎn)數(shù)據(jù)的代碼瘤袖,在 allocateDirect 方法分配了內(nèi)存并指定了大小之后衣摩,下一步就是 order 告訴 ByteBuffer 按照本地字節(jié)序組織它的內(nèi)容。本地字節(jié)序是指捂敌,當(dāng)一個(gè)值占用多個(gè)字節(jié)時(shí)艾扮,比如 32 位整型數(shù),字節(jié)按照從最重要位到最不重要位或者相反順序排列黍匾。接下來 asFloatBuffer 方法可以得到一個(gè)反映底層字節(jié)的 FloatBuffer 類實(shí)例栏渺,避免直接操作單獨(dú)的字節(jié),而是使用浮點(diǎn)數(shù)锐涯。最后磕诊,通過 put 方法就可以把數(shù)據(jù)從 Java 層內(nèi)存復(fù)制到緩沖區(qū),當(dāng)進(jìn)程結(jié)束時(shí),這塊內(nèi)存就會(huì)被釋放掉霎终。


頂點(diǎn)著色器Vertex Shader

著色器(Shader):是在GPU上運(yùn)行的小程序滞磺,此程序使用OpenGL ES SL語言來編寫,它是一個(gè)描述頂點(diǎn)或像素特性的簡(jiǎn)單程序莱褒。

在渲染管線中傳輸?shù)拿總€(gè)頂點(diǎn)坐標(biāo)位置击困,OpenGL ES都會(huì)調(diào)用頂點(diǎn)著色器對(duì)每個(gè)頂點(diǎn)執(zhí)行一次運(yùn)算,它還可以使用頂點(diǎn)數(shù)據(jù)來計(jì)算該頂點(diǎn)的坐標(biāo)广凸、顏色阅茶、光照和紋理坐標(biāo)等。

頂點(diǎn)著色器

在頂點(diǎn)著色器中最主要的任務(wù)是執(zhí)行頂點(diǎn)坐標(biāo)變換谅海,我們?cè)O(shè)定的圖元坐標(biāo)是一種本地坐標(biāo)脸哀,但GL是無法識(shí)別出的,所以可以在頂點(diǎn)著色器中對(duì)本地坐標(biāo)執(zhí)行模型視圖變換扭吁,將本地坐標(biāo)轉(zhuǎn)化為裁剪坐標(biāo)系的坐標(biāo)值撞蜂。

頂點(diǎn)著色器的另一個(gè)功能是向后面的片段著色器提供一組易變變量(varying)。易變變量會(huì)在圖元裝配階段(簡(jiǎn)單說侥袜,圖元裝配之后蝌诡,所有 3D 的圖元將被轉(zhuǎn)化為屏幕上 2D 的圖元。)之后被執(zhí)行插值計(jì)算枫吧,如果是單重采樣浦旱,其插值點(diǎn)為片段的中心,如果多重采樣由蘑,其插值點(diǎn)可能為多個(gè)采樣片段中的任意一個(gè)位置闽寡。易變變量可以用來保存插值計(jì)算片段的顏色,紋理坐標(biāo)等信息尼酿。

想要定義一個(gè)著色器程序爷狈,還要通過一種特殊的語言去編寫:OpenGL Shading Language,簡(jiǎn)稱GLSL裳擎。GLSL語言類似于 C 語言或者 Java 語言涎永,它的程序入口也是一個(gè)名為 main 的函數(shù)。關(guān)于 GLSL 的部分鹿响,完全可以單獨(dú)寫一篇博客了羡微,暫時(shí)先不詳細(xì)闡述。

"attribute vec4 vPosition;" +
        "void main() {" +
        "  gl_Position = vPosition;" +
        "}";

首先我們定義了 a_Position 變量惶我,它是 vec4 類型的妈倔,而 attribute 只能存在于頂點(diǎn)著色器中,一般用于保存頂點(diǎn)數(shù)據(jù)绸贡,它可以在數(shù)據(jù)緩沖區(qū)中讀取數(shù)據(jù)盯蝴。然后毅哗,數(shù)據(jù)緩存區(qū)中的頂點(diǎn)坐標(biāo)會(huì)賦值給 a_Position ,a_Position 會(huì)傳遞給最終坐標(biāo) gl_Position捧挺。


組裝圖元Shape Assembly

在頂點(diǎn)著色器程序輸出頂點(diǎn)坐標(biāo)之后虑绵,各個(gè)頂點(diǎn)被按照繪制命令中的圖元類型參數(shù),以及頂點(diǎn)索引數(shù)組被組裝成一個(gè)個(gè)圖元闽烙。通過組裝圖元翅睛,所有 3D 的圖元已經(jīng)被轉(zhuǎn)化為屏幕上 2D 的圖元。

組裝圖元

光柵化 Rasterization

在光柵化階段黑竞,基本圖元被轉(zhuǎn)換為供片段著色器使用的片段捕发,片段是由很多小的像素組成,它們包含位置摊溶、顏色和紋理坐標(biāo)等信息爬骤,這些值是由圖元的頂點(diǎn)信息進(jìn)行插值計(jì)算得到的。隨后這些片段會(huì)送到片段著色器中處理莫换,也就是這個(gè)階段:從頂點(diǎn)數(shù)據(jù)到可渲染在顯示設(shè)備上的像素的質(zhì)變過程。

光柵化過程

另外骤铃,在片段著色器運(yùn)行之前會(huì)執(zhí)行裁切(Clipping)拉岁。裁切會(huì)丟棄超出你的視圖以外的所有像素,用來提升執(zhí)行效率惰爬。

直線片段光柵化過程

片元在成為像素之前喊暖,還會(huì)做多種測(cè)試(比如深度測(cè)試、透明度測(cè)試撕瞧、模板測(cè)試等陵叽,這些測(cè)試目前接觸到的一般在3D圖像中更常使用,比如深度測(cè)試進(jìn)行物體的遮擋效果的渲染丛版,模板測(cè)試可以用于描邊等巩掺,2D中應(yīng)用較少)以決定其最終是否會(huì)被顯示為像素。所以页畦,嚴(yán)格來說胖替,“片元”和“像素”并不是一一對(duì)應(yīng)的。


片段著色器 Fragment Shader

片段著色器的主要作用是計(jì)算每一個(gè)片段最終的顏色值(或者丟棄該片段)豫缨。

片段著色器

在片段著色器之前的階段独令,渲染管線都只是在和頂點(diǎn)、圖元打交道好芭。在 3D 圖形程序開發(fā)中燃箭,貼圖是最重要的部分,程序可以通過 GL 命令上傳紋理數(shù)據(jù)至 GL 內(nèi)存中舍败,這些紋理可以被片段著色器使用招狸。片段著色器可以根據(jù)頂點(diǎn)著色器輸出的頂點(diǎn)紋理坐標(biāo)對(duì)紋理進(jìn)行采樣敬拓,以計(jì)算該片段的顏色值。

另外瓢颅,片段著色器也是執(zhí)行光照等高級(jí)特效的地方恩尾,比如可以傳給片段著色器一個(gè)光源位置和光源顏色,可以根據(jù)一定的公式計(jì)算出一個(gè)新的顏色值挽懦,這樣就可以實(shí)現(xiàn)光照特效翰意。

"precision mediump float;" +
        "uniform vec4 vColor;" +
        "void main() {" +
        "  gl_FragColor = vColor;" +
        "}";

第一行的 mediump 指的就是片段著色器的精度了,有三種可選信柿,這里用中等精度就行了冀偶。uniform 則表示該變量是不可變的了,也就是固定顏色了渔嚷,目前顯示固定顏色就好了进鸠。

gl_FragColor 變量就是 OpenGL 最終渲染出來的顏色的全局變量,而u_Color 就是我們定義的變量形病,通過在 Java 層綁定到 u_Color 變量并給它賦值客年,就會(huì)傳遞到 Native 層的 gl_FragColor 中。


測(cè)試與混合 Tests and Blending

像素所有權(quán)測(cè)試用來判斷幀緩沖區(qū)中該位置的像素是否屬于當(dāng)前 OpenGL ES漠吻,例如在窗口系統(tǒng)中該位置可能會(huì)被其他應(yīng)用程序窗口遮擋量瓜,此時(shí)該像素則不會(huì)被顯示。

在片段測(cè)試之后途乃,片段要么被丟棄绍傲,要么每個(gè)片段對(duì)應(yīng)的顏色,深度耍共,模板值會(huì)被寫入幀緩沖區(qū)烫饼,最終呈現(xiàn)在設(shè)備屏幕上。幀緩沖區(qū)中的顏色值也可以被讀回到客戶端應(yīng)用程序中试读,這樣可以實(shí)現(xiàn)繪制到紋理的效果杠纵。

至此,OpenGL ES 渲染管道最終將每個(gè)像素點(diǎn)的顏色鹏往,深度淡诗,模板等數(shù)據(jù)輸送到幀緩存中(Framebuffer)。


幀緩存 / 渲染緩存

總的來說伊履,幀緩存是接收渲染結(jié)果的緩沖區(qū)韩容,為GPU指定存儲(chǔ)渲染結(jié)果的區(qū)域。它存儲(chǔ)著 OpenGL ES 繪制每個(gè)像素點(diǎn)最終的所有信息:顏色唐瀑,深度和模板值群凶。更通俗點(diǎn),可以理解成存儲(chǔ)屏幕上最終顯示的一幀畫面的區(qū)域哄辣。

渲染緩存則存儲(chǔ)呈現(xiàn)在屏幕上的渲染圖像请梢,它也被稱作顏色緩沖區(qū)赠尾,因?yàn)樗举|(zhì)上是存儲(chǔ)要顯示的顏色。多個(gè)紋理對(duì)象或多個(gè)渲染緩存對(duì)象毅弧,可通過連接點(diǎn)(attachment points)連接到幀緩存對(duì)象上气嫁。

可以同時(shí)存在很多幀緩存,并且可以通過 OpenGL ES 讓 GPU 把渲染結(jié)果存儲(chǔ)到任意數(shù)量的幀緩存中够坐。但是寸宵,只有將內(nèi)容繪制到視窗體提供的幀緩存中,才能將內(nèi)容輸出到顯示設(shè)備元咙。視圖系統(tǒng)提供的幀緩存通常由兩個(gè)緩存對(duì)象組成梯影,一個(gè)前端緩存,一個(gè)后端緩存庶香。

前幀緩存決定了屏幕上顯示的像素顏色甲棍。程序的渲染結(jié)果通常保存在后幀緩存在內(nèi)的其他幀緩存,當(dāng)渲染后的后幀緩存包含一個(gè)完成的圖像時(shí)赶掖,前后幀緩存會(huì)立即互換感猛,前幀緩存變成新的后幀緩存,后幀緩存變成新的前幀緩存奢赂。


完整的渲染管線的過程

參考文獻(xiàn)

  1. https://juejin.im/post/5af0ebfa518825671d207fa4#heading-4

  2. http://colin1994.github.io/2017/04/01/OpenGLES-Lesson01/#1-_%E9%A1%B6%E7%82%B9%E6%95%B0%E7%BB%84

  3. http://linbinghe.com/2018/b8f62c8f.html

  4. https://blog.csdn.net/srk19960903/article/details/74942401

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唱遭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子呈驶,更是在濱河造成了極大的恐慌,老刑警劉巖疫鹊,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件袖瞻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡拆吆,警方通過查閱死者的電腦和手機(jī)聋迎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來枣耀,“玉大人霉晕,你說我怎么就攤上這事±剔龋” “怎么了牺堰?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)颅围。 經(jīng)常有香客問我伟葫,道長(zhǎng),這世上最難降的妖魔是什么院促? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任筏养,我火速辦了婚禮斧抱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渐溶。我一直安慰自己辉浦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布茎辐。 她就那樣靜靜地躺著宪郊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荔茬。 梳的紋絲不亂的頭發(fā)上废膘,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音慕蔚,去河邊找鬼丐黄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛孔飒,可吹牛的內(nèi)容都是我干的灌闺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼坏瞄,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼桂对!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鸠匀,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤蕉斜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后缀棍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宅此,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年爬范,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了父腕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡青瀑,死狀恐怖璧亮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斥难,我是刑警寧澤枝嘶,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蘸炸,受9級(jí)特大地震影響躬络,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜搭儒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一穷当、第九天 我趴在偏房一處隱蔽的房頂上張望提茁。 院中可真熱鬧,春花似錦馁菜、人聲如沸茴扁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽峭火。三九已至,卻和暖如春智嚷,著一層夾襖步出監(jiān)牢的瞬間卖丸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工盏道, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留稍浆,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓猜嘱,卻偏偏與公主長(zhǎng)得像衅枫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子朗伶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 本文首發(fā)于個(gè)人博客:Lam's Blog - 【OpenGL ES】入門及繪制一個(gè)三角形弦撩,文章由MarkDown語...
    格子林ll閱讀 7,248評(píng)論 2 18
  • 早就聽過大名鼎鼎的 OpenGL,卻遲遲沒有實(shí)踐學(xué)習(xí)论皆,有些慚愧益楼。今天開始通過實(shí)踐+博文方式學(xué)習(xí)掌握 OpenGL。...
    王英豪閱讀 22,478評(píng)論 1 33
  • 1点晴、概述 Android框架提供了大量標(biāo)準(zhǔn)工具偏形,用于創(chuàng)建有吸引力的功能性圖形用戶界面。如果想要更多地控制應(yīng)用程序在...
    高丕基閱讀 3,143評(píng)論 0 7
  • 官方文檔地址 Android包含Open Graphics Library(OpenGL?)庫觉鼻,特別是OpenGL...
    sjj_dot閱讀 2,689評(píng)論 0 10
  • 使用OpenGL ES最關(guān)心的問題 1、如何在iOS上搭建OpenGL ES環(huán)境2队橙、如何鏈接GLSL3坠陈、如何通過G...
    啵啵_long_港閱讀 2,182評(píng)論 0 1