NO.1

上一篇文章說到我從客戶端轉前端的歷程,短短一年的時間就打開了前端世界的大門,簡直就是有無窮多的東西可玩,以前酷愛Java的我終於見識到什麼都可以寫的JavaScript的厲害了,不僅僅可以寫Web,客戶端,後端,系統應用,還可以在神經網絡、物聯網,甚至嵌入式都可以,簡直就是一個萬能的語言,可以說能編程的地方理論上都可以用JS來寫!

轉前端後,我就發現了不少新奇好玩的東西,WebGL,WebAssembly(後面再起這一個系列),前端還能幹這麼東西啊,完全出乎我意料,一直以爲Web就是性能不好,所以才一直在Native上玩。以前我玩客戶端,爲了追求極致的性能,巧合的就幹了這裏兩件事情,第一件就是自學了ARM,用匯編來實現我的功能(這就和WebAssembly契合),第二件就是自學了OpenGLES,利用GPU來加速優化我的代碼(這就和WebGL契合)。既然來到了前端大陸,爲什麼我就不玩玩這兩個東西呢?

由於平時上班實在太忙了,不能一下子肝出一大篇乾貨了,而且這貨也是不簡單呢,一篇文章也學不深入,於是就計劃分開幾彈來一起玩玩這貨。其實我也是剛學,不敢說教啥的,也就是藉助下班業餘時間憑藉興趣學習翫玩,總結一下心得罷了,和大家一起交流交流。曾經在Android上用OpenGLES寫了一個紅藍3D播放器和實現了彈幕SDK,那麼也就以此爲目標,學習一下WebGL,然後寫一個網頁版的紅藍3D播放器和實現彈幕SDK,雖然不知道能否實現,反正理論上來說是可以,就朝着這個方向去努力嘗試就好了。

NO.2

什麼是WebGL?

那麼到底啥是WebGL?當我們要學習或者瞭解一個東西的時候,通常做的第一件事情就是使用搜索引擎,找找資料。這裏給你摘了百科上的介紹:

WebGL(全寫Web Graphics Library)是一種3D繪圖協議,這種繪圖技術標準允許把JavaScript和OpenGL ES 2.0結合在一起,通過增加OpenGL ES 2.0的一個JavaScript綁定,WebGL可以爲HTML5 Canvas提供硬件3D加速渲染,這樣Web開發人員就可以藉助系統顯卡來在瀏覽器裏更流暢地展示3D場景和模型了,還能創建複雜的導航和數據視覺化。顯然,WebGL技術標準免去了開發網頁專用渲染插件的麻煩,可被用於創建具有複雜3D結構的網站頁面,甚至可以用來設計3D網頁遊戲等等。

乍一看上面的描述,JavaScript誰不會啊?Canvas也會啊(除了我這種剛入門前端的小白以爲,不過在客戶端也是有Canvas的哦),剩下也就是一個OpenGL ES 2.0罷了,那麼只要學習這個就可以了。再看百科描述:

WebGL 1.0基於OpenGL ES 2.0,並提供了3D圖形的API。它使用HTML5Canvas並允許利用文檔對象模型接口。WebGL 2.0基於OpenGL ES 3.0,確保了提供許多選擇性的WebGL 1.0擴展,並引入新的API。可利用部分Javascript實現自動存儲器管理。

原來WebGL就是基於OpenGLES的嘛,那太好了,我以前學習的不就是OpenGLES了麼?這就已經事半功倍了哈哈哈!我的學習風格是慢慢的,循序漸進的,從最基本的理解入門,再到最簡單的helloworld,最後纔是用一個實際的例子來練習;而不是上來就直接教你怎麼使用了,可能我這樣會比較慢,但是我覺得理解確實最深刻的,而且對於沒基礎的我來說更加容易上手,而不會入門就放棄了!畢竟我還是覺得慢慢培養對這貨的興趣最重要。

NO.3

圖形引子

其實在我學習OpenGLES的時候,確實有點痛苦,明明我就是學習計算機科班出身的,怎麼我就這麼難理解呢?怎麼那麼多的概念和我以前學習的不太一樣呢?確實需要顛覆一下之前的慣性思維,最好就是拋開重新學習。還是一步一步的來,先理解什麼是圖形編程唄。

理解圖形編程

我們知道計算機的發展歷史,從最簡單的加法器,到圖靈機,到馮諾依曼計算機,再到今天的智能機,相信你和我一樣對此很癡迷,非常崇拜兩位偶像,比爾蓋茨和喬布斯,並讀過所有關於他們的故事,想必就清楚知道圖形發展的重要性了。蘋果電腦的MacOS、Windows系統的絢麗UI,還有今天各種絢麗的畫面,遊戲,等等。最簡單的,我們肯定知道組裝電腦的時候,要選一塊好的顯卡,才能玩更好的遊戲。而實際上,我們對計算機的瞭解並不夠多。

還沒寫過一行代碼的我就已經玩過了無數絢麗畫面的遊戲了,然而,當我學習編程的時候,最想解密的就是一個軟件、一個這麼牛逼的遊戲畫面,到底是怎麼通過這些代碼寫出來的呢?還記得剛學習C語言的第一個代碼嗎?盯着控制檯,看到輸出“Hello world!”,人人都說會爲這一偉大的成就歡呼。然而,這不過是幾十年前的技術罷了;我想要知道的是,到底怎麼樣編寫圖形界面,而不是在控制檯輸出文字!

#include<stdio.h>
void main()
{
    printf("Hello World!\n");
}


admindeMacBook-Pro-191:develop fuxing$ gcc hello_world.c
admindeMacBook-Pro-191:develop fuxing$ ./a.out
Hello World!
不管是學習哪一門語言,C、Java,還是JS,實際上我們都是針對CPU來編程,所有的

邏輯 計算都是由CPU來計算完的。 在我們學習計算機組成原理的時候,我們知道這些計算

其實都 是CPU的指令集在操作寄存器而已。 對於輸出的結果顯示,基本上都是文字字母,

簡單的在 陰極射線管顯示器即可顯示。

對CPU的操作和對輸出結果的顯示,這一切都是操作系統和編譯器完成的事情,簡單的說,

編譯器把高級語言編譯成了01指令,而操作系統則是把底層硬件管理給封裝成了系統接口

調用,我們只需要調用系統接口就可以了,所以我們調用一下printf()就可以在顯示器上

進行展示了。

(圖片來自於網絡)

而其實系統對大部分硬件的操作都是通過驅動程序完成的,每一個硬件都有對應的驅動程序,驅動程序要遵循統一的規範和接口,然後實現即可。所以每次我們重裝完系統,都要安裝各種各樣的驅動程序。對於硬件的開發,是嵌入式的領域了,雖然不用深入探討,但是從這裏可以理解到,硬件也是有處理單元的!

(圖片來自於網絡)

我們終於知道了其實顯卡是有圖形處理單元的,也就是GPU(Graphic Process Unit),和CPU一樣的意義,用於渲染畫面。那麼問題又來了,圖形是怎麼繪製的呢?其實我們都知道,那就是像素,我們現在是電子設備爆炸的年代,誰不知道分辨率這東西呢?一個顯示器的分辨率是1920x1080,意思就是橫向有1920個像素,縱向有1080個像素,我們可以理解,像素就是一個很小很小的發光點,所以說,屏幕其實是匯聚了超密集的像素點,當然,這會涉及到材料學領域了,如液晶屏等等,我們只要抽象來學習理解就好。學過物理,我們也知道三原色,只要紅綠藍即可調出所有顏色,也就是說,其實每個像素點都是有不同量的紅綠藍三色。當然啦,顯示器肯定不是那麼簡單,原生的數據是YUV,這又涉及到深入圖形學領域了,我們現在還是隻要抽象理解就好了。

在計算機裏面一個字節是8位,取值範圍也就是0-255,如果用一個字節表示一種原色量的取值範圍,那麼紅綠藍就是三個字節,再加上一個透明度Alpha,RGBA剛好是四個字節,通常是用一個整形int來表示。這樣就可以把一個圖形給量化成數字,既然已經是數字化了,就可以給計算機處理了。現在我們就能理解到,實際上一張二維的圖片,就是一個二維整形矩陣,這些都是我們在CPU和內存都可以操作的邏輯了。最後操作系統把數據交換給顯卡,通常是複製到顯存,交給了GPU來渲染顯示,本質上GPU也是處理了這些數據,根據這些數據來控制顯示器在不同的點放射不同量度的原色,這樣就能展示圖形了。

圖形編程API

通過上面的歷史瞭解,我們十分清晰,圖形領域是十分重要和可發展的,介於操作系統和硬件(驅動)的中間層,可以做很多事情,提供重要的圖形編程接口,方便開發二維和三維的圖形。於是,這個世界基本上又出現了兩大陣型,OpenGL和DirectX。

(圖片來自於網絡)

DirectX太熟悉了,我們打遊戲,一定要安裝這個東西,童年啊!它就是用於Windows的遊戲開發,一統天下!而OpenGL是跨平臺的,不管哪裏都能用(不然怎麼會有今天的WebGL呢)!並且不僅用於遊戲開發,幾乎什麼領域都可以用。當然了,很少人會直接用這兩個東西直接開發遊戲,而是用來開發遊戲引擎,然後基於引擎開發遊戲,例如Unity3D!

OpenGL的全稱是Open Graphics Library,關於OpenGL的開發,我們用到的是着色語言(Shading Language),只是一個新語言而已,學習語法就好了,只不過我們重點要理解的是渲染的流程是怎麼樣的,不然我們也不知道怎麼用着色語言進行編寫呀。

既然圖形編程都需要用到OpenGL和DirectX,那麼爲啥我們日常開發寫那麼多的UI,卻從來沒有涉及到這兩貨的開發呢?我們回想一下,日常的開發中涉及UI有哪些?Image、Button、Text、EditText、InputText等等,基本上都是利用這些組件進行拼裝,似乎很少涉及到複雜的圖形,最多也就是涉及到一些動畫效果而已。其實這些組件,或者說我們日常用到的這些API,都是已經封裝好了底層怎麼去渲染,對於我們來說,根本不需要了解底層怎麼去渲染,或許其實都是在CPU上進行處理,再從內存複製到顯存進行了顯示了,又或許可以開啓硬件加速,其實所有的渲染處理都是丟給了GPU去處理。再說日常我們開發中,並不需要那麼高的性能,壓根就不需要去寫什麼GL的東西啦。

曾經在Windows上玩遊戲,如果沒有安裝DirectX,其實也是可以玩的,只不過可能沒那麼流暢,這就說明遊戲開發者也不會直接調用DirectX的API,而是基於上層的遊戲引擎開發遊戲,有DirectX就說明有了硬件加速,沒有的話,也不會遊戲的運行,還是可以用CPU進行渲染處理。

當我們發現日常的開發中,涉及到圖像相關的,CPU的處理已經不行了,性能成爲了瓶頸,那麼我們就要自己去實現底層的渲染邏輯,這時候就要去寫GL了。例如,播放一個視頻,每一幀都要實時去處理某些像素,這個時候如果用CPU去處理的話,就會很慢了,我們可以用GPU來處理像素邏輯,而CPU儘管處理播放同步邏輯就好了。

NO.4

什麼是OpenGL ES

我們已經瞭解到要學習WebGL,其實就是要學習OpenGLES了,可以理解爲WebGL就是在用JS調用OpenGLES的API,那麼OpenGLES又是啥呢?學習這個之前是否又需要先學習OpenGL呢?答案是不需要的。

由於移動設備的快速發展,於是出現了針對這些嵌入式設備的一套API子集出臺了,OpenGL for Embedded Systems。顯然,因爲是子集,所以就是對於OpenGL進行了功能的裁剪。然後OpenGL ES現在已經發展到了3.0版本,每一個版本都是巨大的飛躍。1.0和2.0之間的巨大區別在於,2.0是支持自定義渲染管線編程的,也就是可以支持着色語言了,意思就是,我們除了在高級語言那裏調用OpenGL的API,還能在管線裏面編寫着色語言程序!這就是2.0的最大區別,而3.0基於2.0並沒有巨大的變化,因此兩者的學習遞進即可,不需要轉變。

OpenGL ES 1.0的渲染管線

GPU內部有許多處理圖形信號的並行處理單元,所以它比CPU的串行執行效率高很多。我們理解的CPU的能力無非就是有很多的指令集,如果我們要調用這些指令集的話,就需要編寫彙編語言,而在高級語言層面調用的API就是系統或者平臺爲我們封裝好的能力。而GPU,也是會有相應的指令,硬件開發商會開發相應的驅動程序,提供標準的API供系統調用。OpenGL和DirectX就是基於此提供了相應的渲染能力,實際上,它們兩個纔是標準大佬,反推硬件提供相應的能力。 渲染管線實際上就是通過處理單元處理的一系列繪製過程。 管線如下圖所示:

(圖片來自於網絡)

重點理解幾點:

  • 什麼是圖元,其實就是圖像單元;OpenGL繪製圖形的時候,是有一個個的圖元組合而成的。繪製方式有點、線和三角形,分別對應三種圖元。

  • 什麼是光柵化,圖元在數學上是連續的量,但是在顯示器就是離散的像素,所以,光柵化就是把頂點數據轉換爲片元的過程。

  • 什麼是片元,爲什麼不叫像素?像素是屏幕上的點,那是二維的,但是一個屏幕上的像素在三維中,可能覆蓋了很多個像素,於是在三維中不能叫像素,應該叫片元。

OpenGL ES 2.0的渲染管線

2.0的渲染管線如下圖所示:

(圖片來自於網絡)

2.0的最大區別就是多了頂點着色器和片元着色器,方便程序員進行開發,需要學習着色語言了,而1.0就只能調用上層API。

頂點着色器

什麼是頂點?就是幾何圖形的頂點的意思,例如三角形有3個頂點,矩形有4個頂點,線段只有兩個頂點。頂點着色器(Vertex Shader)就是一個可編程的處理單元,圖形的每一個頂點都會經過頂點着色器進行處理轉換,產生紋理座標,顏色,點位置等所需的頂點屬性信息。工作原理圖如下:

(圖片來自於網絡)

其中Attribute是每個頂點各自不同信息所屬的變量,一般包含頂點座標和頂點紋理座標,通過高級語言傳輸下來的;Uniform是全局變量,是通過高級語言傳輸下來的數據;Varying是產生輸出到片元着色器的數據,一般是紋理座標。然後就是你編寫代碼的臨時變量和gl_xxx的內置變量了。

一個頂點着色器例子代碼如下(不需要理解這段代碼的意思,感受即可):

uniform mat4 uMVPMatrix; //總變換矩陣
attribute vec3 aPosition;  //頂點位置
attribute vec2 aTexCoor;    //紋理座標
varying vec2 vTextureCoord;  //用於傳遞給片元着色器的變量
void main()     
{                                   
   gl_Position = uMVPMatrix * vec4(aPosition,1); //根據總變換矩陣計算此次繪製此頂點位置
   vTextureCoord = aTexCoor;//將接收的紋理座標傳遞給片元着色器
}

可以看到,GL語言並不複雜,類似於C語言,這些uniform、attribute、varying都是關鍵字,標識數據的來源,mat4、vec3、vec2是數據類型,分別對應是4維矩陣、3維向量和2維向量的意思。

片元着色器

光柵化後的每個片元都會執行一次片元着色器(Fragment Shader),可以理解爲每個像素都執行一次(二維的角度理解),主要的功能是紋理的採樣和顏色的彙總。

什麼是紋理,直接理解就是圖形的表面、皮膚之類的,也就是圖像、顏色、花紋等等。例如,在Android中,把一張圖片Bitmap直接映射到OpenGLES中成爲一張紋理,這時候紋理就是一張圖片了,Bitmap是可以回收的了,已經傳輸到顯存了。

工作原理圖如下:

(圖片來自於網絡)

其中,Varying數據是從頂點着色器傳來的;Uniform通常是紋理的數據,gl_FragColor就是輸出的結果。

一個片元着色器例子代碼如下:

precision mediump float;//告訴精度是float
varying vec2 vTextureCoord; //接收從頂點着色器過來的參數
uniform sampler2D sTexture;//紋理內容數據
void main()                         
{           
   //給此片元從紋理中採樣出顏色值            
   gl_FragColor = texture2D(sTexture, vTextureCoord); 
}

實際上,繪製一個矩形是通過繪製兩個三角形合成的,也就是有6個頂點,每個頂點執行

一次頂點着色器,然而頂點着色器輸出的用於傳遞給片元着色器的座標變量並沒有直接傳遞給片元着色器,而是在光柵化以後,通過插值計算,得出每個片元的座標再傳遞給片元着色器,於是,片元着色器是執行處理每一個片元(像素)的。例如,把一張圖片繪製滿1920x1080的屏幕,則每個像素都執行一遍片元着色器。

着色語言

學到這裏,基本上已經能夠理解OpenGLES的原理了,着色語言就是又是一大塊知識,學習它就能在上面兩個着色器上編寫代碼了,這裏不可能完全列舉出所有知識,學習這個就跟學習一門語言差不多,而且不難理解。先簡單知道幾個向量類型,mat4是四維矩陣,vec4是4維向量,vec3是3維向量,vec2是2維向量,後續一遍學習一遍總結就可以了。

NO.5

總結

這一彈就先學習這麼多了,主要以介紹和理解WebGL爲主,暫時不上手寫代碼,即使上面有一些例子代碼,主要還是以閱讀理解爲主,先感受感受這個歷史進程,培養一下興趣,一步一個腳印,慢慢學習,目前先重點學習了一下着色器,後續如果要編寫高級的功能,還有各種轉換、數學知識要學習,下一彈再一起上手簡單的例子學習。以上僅是個人的學習心得,難免會有一些錯誤的,對於理解有誤的地方歡迎指出。

相關文章