- WebGL 3D開發實戰詳解(第2版)
- 吳亞峰 于復興 索依娜
- 5160字
- 2020-07-07 17:17:05
2.3 著色器與渲染管線
2.2節已經通過三角形的案例為讀者介紹了如何使用WebGL 2.0進行3D場景的開發,相信讀者已經對開發步驟有了初步的了解,但是要想真正地掌握WebGL 2.0,必須要了解著色器和渲染管線的相關知識,本節將向讀者詳細介紹這方面的內容。
2.3.1 WebGL 2.0的渲染管線
渲染管線有時也稱為渲染流水線,一般是由顯示芯片(GPU)內部處理圖形信號的并行處理單元組成。這些并行處理單元之間是相互獨立的,不同型號的硬件上獨立處理單元的數量也有很大的差異。一般越高端的硬件,獨立處理單元的數量也就越多。
WebGL 2.0中渲染管線實質上指的是一系列的繪制過程。向程序中輸入待渲染3D物體的相關描述信息數據,經過渲染管線處理后,輸出的是一幀想要的圖像。WebGL 2.0中的渲染管線如圖2-7所示。

圖2-7 WebGL 2.0渲染管線
1.基本處理
該階段設定3D空間中物體的頂點坐標、頂點對應的顏色、頂點的紋理坐標等屬性,并且指定繪制方式,如點繪制、線段繪制或者三角形繪制等。
2.頂點緩沖對象
對于在整個場景中頂點基本數據不變的情況,這部分在程序中是可選的??梢栽诔跏蓟A段將頂點數據經過基本處理后送入頂點緩沖對象,這樣在繪制每一幀想要的圖像時就省去了輸入/輸出頂點數據的麻煩,直接從頂點緩沖對象中獲得頂點數據即可。相比于每次繪制時單獨將頂點數據送入GPU的方式,它可以一定程度上節省GPU的I/O帶寬,提高渲染效率。
3.頂點著色器
頂點著色器是一個可編程的處理單元,功能為執行頂點的變換、光照、材質的應用與計算等與頂點相關的操作,每個頂點執行一次。其工作過程為首先將頂點的原始幾何信息及其他屬性傳送到頂點著色器中,頂點著色器處理后產生相應的紋理坐標、顏色、點位置等后繼流程需要的各項頂點屬性信息,然后將其傳遞給圖元裝配階段。
開發人員可以在開發過程中根據實際需求自行開發頂點變換、光照等功能,這大大增加了程序的靈活性。但凡事有利皆有弊,增加靈活性的同時也增加了開發的難度。在WebGL 2.0中頂點著色器的工作原理如圖2-8所示。

圖2-8 WebGL 2.0頂點著色器工作原理
? 頂點著色器的輸入主要為與待處理頂點相對應的輸入變量、Uniforms(一致)變量、采樣器以及臨時變量,輸出主要為經過頂點著色器后生成的輸出變量及一些內建輸出變量。
? 頂點著色器中的輸入變量指的是3D物體中每個頂點不同信息所屬的變量,一般頂點的位置、顏色、法向量等不同的信息都是以輸入變量的方式傳入頂點著色器的。例如,2.2節中頂點著色器里的aPosition(頂點位置)和aColor(頂點顏色)變量等。
? Uniforms變量指的是對于由同一組頂點組成的單個3D物體中所有頂點都相同的量,一般為場景中當前的光源位置、當前的攝像機位置、投影系列矩陣等。例如,2.2節中頂點著色器里的uMVPMatrix(總變換矩陣)變量等。
? 頂點著色器中的輸出變量是從頂點著色器計算產生并傳遞到片元著色器的數據變量。頂點著色器可以使用輸出變量來傳遞需要插值或不需要插值到片元的顏色、法向量、紋理坐標等值。例如,2.2節中由頂點著色器傳入片元著色器中的vColor變量。
? 對于內建輸出變量gl_Position、gl_PointSize以及內建輸入變量gl_VertexID、gl_InstanceID來說,gl_Position是經過矩陣變換、投影后的頂點最終位置;gl_PointSize指的是點的大小;gl_VertexID用來記錄頂點的整數索引;gl_InstanceID是指實例ID,它只在頂點著色器中使用,對于指定的每一組圖元,該ID相應遞增。
輸出變量在頂點著色器賦值后并不是直接將值傳遞到后繼片元著色器對應的輸入變量中,在此存在兩種情況。
? 如果out限定符之前含有smooth限定符或者不含任何限定符,則傳遞到與后繼片元著色器對應輸入變量的值是在光柵化階段產生。產生時由管線根據片元所屬圖元各個頂點對應的頂點著色器對此輸出變量的賦值情況及片元與各頂點的位置關系插值產生,圖2-9說明了這個問題。

圖2-9 易變變量的工作原理
? 如果out限定符之前含有flat限定符,則傳遞到與后繼片元著色器對應的輸入變量的值不是在光柵化階段插值產生的,而是由圖元最后一個頂點對應的頂點著色器對此輸出變量賦值來決定的,此種情況下圖元中每個片元的值均相同。
說明
有一定數學知識的讀者可能會想到一個問題,對每個片元進行一次插值計算將會非常耗時,嚴重影響性能。幸運的是,WebGL 2.0的設計者也考慮到了這個問題,這些插值操作都是由GPU中的專用硬件來實現的,因此速度很快,不影響性能。
4.圖元裝配
在這個階段主要有兩個任務,一個是圖元組裝,另一個是圖元處理。所謂圖元組裝是指頂點數據根據設置的繪制方式被結合成完整的圖元。例如,在點繪制方式下每個圖元僅需要一個單獨的頂點,在此方式下每個頂點為一個圖元;在線段繪制方式每個圖元則需要兩個頂點,在此方式下每兩個頂點構成一個圖元;在三角形繪制方式下需要3個頂點構成一個圖元。
圖元處理最重要的工作是剪裁,其任務是消除位于半空間(half-space)之外的部分幾何圖元,這個半空間是由一個剪裁平面定義的。例如,點剪裁就是簡單地接受或者拒絕頂點,線段或多邊形剪裁可能需要增加額外的頂點,這具體取決于直線或多邊形與剪裁平面之間的位置關系,如圖2-10所示。

圖2-10 剪裁三角形3個頂點生成6個新的頂點
說明
圖2-10給出了一個三角形圖元(圖中為點劃線繪制)被4個剪裁平面剪裁的情況。4個剪裁平面分別為:上面、左側面、右側面、后面。
要進行剪裁是因為隨著觀察位置、角度的不同,不能總看到(這里可以簡單地理解為顯示到設備屏幕上)特定3D物體上某個圖元的全部。例如,當觀察一個正四面體但它離某個三角形面很近時,可能只能看到此面的一部分,這時在屏幕上顯示的就不再是三角形了,而是經過裁剪后形成的多邊形,如圖2-11所示。

圖2-11 從不同角度、距離觀察正四面體
剪裁時,若圖元完全位于視景體以及自定義剪裁平面的內部,則將圖元傳遞到后續步驟進行處理;如果其完全位于視景體或者自定義剪裁平面的外部,則丟棄該圖元;如果其有一部分位于內部,另一部分位于外部,則需要剪裁該圖元。
提示
關于視景體剪裁的問題會在介紹投影的部分進行詳細介紹,這里簡單了解即可。
5.光柵化
雖然虛擬3D世界中的幾何信息是三維的,但由于目前用于顯示的設備都是二維的,因此在真正執行光柵化工作之前,需要將虛擬3D世界中的物體投影到視平面上。需要注意的是,由于觀察位置的不同,同一個3D場景中的物體投影到視平面上時可能會產生不同的效果,如圖2-12所示。

圖2-12 光柵化階段投影到視口
另外,由于在虛擬3D世界中物體的幾何信息一般采用連續的量來表示,因此投影的平面結果也是用連續量來表示的。但目前顯示設備屏幕都是離散化的(由一個個的像素組成),因此還需要將投影結果離散化,將其分解為一個個離散化的小單元,這些小單元一般稱為片元,具體效果如圖2-13所示。

圖2-13 投影后圖元離散化
其實每個片元都對應于幀緩沖中的一個像素,之所以不直接稱為像素是因為3D空間中的物體是可以相互遮擋的。一個3D場景最終顯示到屏幕上雖然是一個整體,但每個3D物體的每個圖元都是獨立處理的。這就可能出現以下情況,系統先處理的是位于離觀察點較遠的圖元,其光柵化成為一組片元,暫時送入幀緩沖的對應位置。但在后面繼續處理離觀察點較近的圖元時也光柵化出了一組片元,兩組片元又對應到幀緩沖中同一個位置,這時距離近的片元將覆蓋距離遠的片元(如何檢測覆蓋是在深度檢測階段完成的)。因此某個片元就不一定能成為最終屏幕上的像素,這樣稱為像素就不準確了,可以將其理解為候選像素。
提示
每個片元包含對應的頂點坐標、頂點顏色、頂點紋理坐標以及頂點深度等信息,這些信息是系統根據投影前此片元對應3D空間中的位置及與此片元相關的圖元中各頂點信息進行插值計算而生成的。
6.片元著色器
片元著色器是處理片元值及相關數據的可編程單元,可以執行紋理采樣、顏色匯總、計算霧顏色等操作,每個片元執行一次。片元著色器的主要功能為通過重復執行(每個片元一次),將3D物體中的圖元光柵化后每個片元產生的顏色等屬性計算出來并送入后繼階段,如剪裁測試、深度測試及模板測試等。
WebGL 2.0中的片元著色器與頂點著色器類似,需要開發人員用著色器語言編程。這在提高靈活性的同時也增加了開發的難度。其基本工作原理如圖2-14所示。

圖2-14 片元著色器工作原理
? in0~in(n)指的是從頂點著色器傳遞到片元著色器的變量。如前面所述,它由系統在頂點著色器后的光柵化階段自動產生,其個數不定,取決于具體的需要。例如,2.2節中片元著色器里的vColor變量。
? 輸出變量一般指的是由片元著色器計算完成的片元顏色值的變量。在片元著色器的最后都需要對其進行賦值,最后將其送入渲染管線的后繼階段以進行處理。例如,2.2節中片元著色器里創建的fragColor變量。
提示
原來在WebGL 1.0中片元著色器的內建輸出變量gl_FragColor在WebGL 2.0中不存在了,如果需要輸出顏色值,則需要聲明out(類型為vec4)變量,用聲明的變量替代gl_FragColor。在開發中,應盡量減少片元著色器的運算量,可以將一些復雜運算放在頂點著色器中執行。
7.剪裁測試
如果程序中啟用了剪裁測試,則程序會檢查每個片元在幀緩沖中的對應位置。若對應位置在剪裁窗口中則將此片元送入下一階段,否則丟棄此片元。
8.深度測試和模板測試
? 深度測試是指將輸入片元的深度值與幀緩沖中存儲的對應片元位置的深度值進行比較,若輸入片元的深度值小,則將輸入片元送入下一階段,準備覆蓋幀緩沖中的原片元或與幀緩沖中的原片元進行混合,否則丟棄輸入片元。
? 模板測試的主要功能為將繪制區域限定在一定范圍內。它一般用在湖面倒影、鏡像等場合,后面的章節會詳細介紹。
9.顏色緩沖混合
若程序中開啟了Alpha混合,則根據混合因子會將上一階段送來的片元與幀緩沖中對應位置的片元進行Alpha混合;否則送入的片元將覆蓋幀緩沖中對應位置的片元。
10.抖動
抖動是一種簡單的操作,允許使用少量的顏色模擬出更寬的顏色顯示范圍,從而使顏色視覺效果更加豐富。例如,可以使用白色以及黑色模擬出一種過渡的灰色。
但使用抖動也是有缺點的,那就是會損失一部分分辨率,因此對于主流原生顏色已經很豐富的顯示設備來說,一般是不需要啟用抖動的。
提示
一些系統雖然在API方面支持開啟抖動,但這僅僅是為了API的兼容,可能根本不會執行事實上的抖動操作。
11.幀緩沖
WebGL 2.0中的物體繪制并不是直接在屏幕上進行的,而是預先在幀緩沖中進行繪制,每繪制完一幀將繪制結果交換到屏幕上。因此,在每次繪制新幀時都需要清除緩沖中的相關數據,否則有可能產生不正確的繪制結果。
同時需要了解的是為了應對不同方面的需要,幀緩沖是由一套組件組成的,主要包括顏色緩沖、深度緩沖以及模板緩沖,各組件的具體用途如下所示。
? 顏色緩沖用于存儲每個片元的顏色值,每個顏色值包括R、G、B、A(紅、綠、藍、透明度)4個色彩通道,應用程序運行時在屏幕上看到的就是顏色緩沖中的內容。
? 深度緩沖用來存儲每個片元的深度值。所謂深度值是指以特定的內部格式表示的從片元處到觀察點(攝像機)的距離。在啟用深度測試的情況下,新片元若想進入幀緩沖則需要將自己的深度值與幀緩沖中對應位置片元的深度值進行比較,若結果為小則有可能進入緩沖,否則被丟棄。
? 模板緩沖用來存儲每個片元的模板值,供模板測試使用。模板測試是幾種測試中最為靈活和復雜的一種,后面將由專門的章節進行介紹。
提示
本節只是對渲染管線中的每一個模塊進行了簡單的介紹,更為具體的內容會在后繼章節進行更為詳細的討論,讀者只要在概念上有個整體的把握即可。
2.3.2 WebGL 2.0中立體物體的構建
前面向讀者介紹了WebGL 2.0的渲染管線,同時也給出了一個非常簡單的旋轉三角形案例。到目前為止,讀者可能還是不太清楚虛擬3D世界中的立體物體是如何搭建出來的。其實這與現實世界搭建建筑物并沒有本質區別,請讀者觀察圖2-15和圖2-16中國家大劇院遠景和近景的照片。

圖2-15 國家大劇院的遠景

圖2-16 國家大劇院的近景
從兩幅照片可以對比出,現實世界中的某些建筑物遠看是平滑的曲面,近看則是由一個個的小平面組成的。3D虛擬世界中的物體也是如此,任何立體物體都是由多個小平面搭建而成的。這些小平面切分得越小,越細致,搭建出來的物體就越平滑。
當然WebGL 2.0的虛擬世界與現實世界還是有區別的,現實世界中可以用任意形狀的多邊形來搭建建筑物,例如,圖2-15中的國家大劇院就是用四邊形搭建的,而WebGL 2.0中僅允許采用三角形來搭建物體。這從構造能力上來說并沒有區別,因為任何多邊形都可以拆分為多個三角形,只需開發時稍微注意一下即可。
圖2-17更加具體地說明了在WebGL 2.0中如何采用三角形來構建立體物體。

圖2-17 用三角形搭建立體物體
說明
從圖2-17中可以看出用三角形可以搭建出任意形狀的立體物體,這里僅給出了幾個簡單的例子,后繼章節中還有很多其他形狀的立體物體。
了解了WebGL 2.0中立體物體的搭建方式后,下面就需要了解WebGL 2.0中的坐標系了。WebGL 2.0采用的是三維笛卡兒坐標系,如圖2-18所示。

圖2-18 WebGL 2.0中的坐標系
從圖2-18中可以看出,WebGL 2.0采用的是右手標架坐標系。一般來說,初始情況下y軸平行于屏幕的豎邊,x軸平行于屏幕的橫邊,z軸垂直于屏幕平面。
提示
空間解析幾何中有左手標架和右手標架兩種坐標系標架。本書并非討論空間解析幾何的專門書籍,因此關于標架的問題不予詳述,需要的讀者可以參考空間解析幾何的書籍或資料。