這一章中關於webgl中顏色的使用咱們將深刻研究。咱們將從研究顏色在webgl和essl中如何被組裝和獲取開始。而後咱們討論在物體、光照和場景中顏色的使用。這以後咱們將看到當一個物體在另外一個物體前面是webgl如何來實現物體碰撞,這是經過深度檢測來實現的。相反透明度混合容許咱們結合全部物體的顏色當一個物體與另外一個物體齧合時。咱們將用透明度混合來建立透明物體。web
這一章主要討論:數組
webgl在RGB顏色模型中還包含第四個屬性,這個屬性被稱爲開端通道。擴展後的模型稱爲RGBA模型,A是爲支持alpha。a的取值範圍時0.0-1.0,跟其餘三個同樣。下圖表明顏色空間。橫軸表明結合rgb可以獲得的顏色,縱軸表明alpha通道。函數
alpha爲顏色帶來了額外的信息。這個信息影響顏色渲染在屏幕上的方式。一般來講alpha影響顏色的透明度。通常來講咱們的顏色都是不透明的,但有一些狀況咱們得考慮得到半透明顏色。學習
在webgl 3D場景中咱們處處都在使用顏色:測試
·最終的顏色在片元着色器中經過設置特殊的gl_FragColor來獲得。若是這個物體的全部片元都擁有一樣的顏色,咱們能夠說這個問題有一個常量顏色。不然這個物體有per-vertex 顏色webgl
Constant coloringspa
爲了得到常量顏色咱們把須要獲得的顏色放在一個uniform存儲器中,這個變量直接傳遞給片元着色器。這個uniform一般被稱爲物體的漫反射材料屬性。咱們也能夠結合物體的法線和光源信息來獲取Lambert反射係數。咱們使用蘭伯特係數經過依賴反射光與光源的夾角來改變反射顏色。scala
下圖第一個是沒有結合反射係數的物體,第二個是結合反射係數後的顏色。3d
Per-vertex coloringorm
在醫學和工程可視化應用中,一般可以找到跟他們要渲染的物體的頂點對應的顏色地圖。這些地圖爲每個頂點分配一個單獨的顏色。爲了實現per-vertex 着色咱們須要在頂點着色器中定義一個attribute來存儲頂點的顏色。
attribute vec4 aVertexColor
下一步是將aVertexColor屬性分配給一個varying變量,以便可以進入片元着色器中。varying變量是能夠被自動插值的。所以每個片元的顏色都是根據爲繞它的頂點的顏色按權重分配的。
若是咱們想讓咱們的顏色地圖可以反映光照條件,咱們可讓每個頂點顏色乘以光照的漫反射部分。獲得的結果將被分配到能夠將它傳遞到片元着色器的varying變量中。
左側圖片沒有反映光源位置信息,右側圖片反映了光源位置信息
Per-fragment coloring
咱們能夠爲每個像素分配一個隨機的顏色,可是ESSL中並無一個內置的隨機數函數。咱們能夠經過其餘方式來得到。這裏不展開
注意:當啓用alpha通道時須要的是 "attribute vec4 aVertexColor",而後要啓用混合功能gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
Use of color in lights
顏色也是光照的屬性;在第3章中咱們看到光照屬性的數量取決於爲場景選擇的光照模型。好比使用蘭伯特反射模型咱們只須要一個着色器uniform量:diffuse;相反在Phong反射模型中每個光源須要擁有三個屬性:環境光、漫反射光、鏡面光。
(另外,光源位置一般也在着色器中做爲一個unifrom變量,以便知道光源位置,因此Phong模型在點位置光源方式中由四個uniform:ambient、diffuse、specular、position;對於方向光這個uniform變量表示的是光源的方向)
Using multiple lights and the scalability problem
Phong反射模型下一個光源須要四個uniform變量,若是咱們有多個光源意味着咱們須要的uniform會成倍數上升,而每臺機器可以獲取的最大unifrom數目是有限的;咱們能夠經過gl.getParameter()中傳入gl.MAX_VERTEX_UNIFORM_VECTORS和gl.MAX_FRAGMENT_UNIFORM_VECTORS參數來獲取可以使用的最大uniform數目。
Using uniform arrays to handle multiple lights
將多光源的uniform分別存儲增長代碼量而且難以維護,ESSL中容許咱們使用unifrom 數組。這項技術能夠容許咱們在着色器中引入光照數組來管理多光源。經過這種方式咱們能夠在着色器總經過便利光照數組來計算光照顏色。雖然咱們仍在須要在JavaScript中定義多個光源對象,可是傳遞給着色器時就變得簡單了。
因此在着色器總咱們須要定義:
unifrom vec3 uPositionLight[3]
注意:ESSL中不支持動態初始化unifrom 數組
也就意味着咱們沒法動態建立多光源
而後傳遞給着色器:
directional point lights
方向點光源,建立這種光源的祕訣在於利用頂點法線和光源位置向量作減法生成新的法線向量,將新法線向量傳遞給片元着色器:
全部的顏色都是經過光照模型算出來的
Use of color in the scene
前面咱們提到過alpha通道能夠存放關於被繪製的物體顏色的透明度信息。可是除非alpha混合被激活不然沒法建立透明物體。當咱們在場景中有多個物體時,事情就變得複雜起來。咱們將學會如何在場景中繪製透明物體和非透明物體。
Transparency
得到透明物體的第一步要使用Polygon stepping(多邊形點刻)技術。這項技術會拋棄一些片元因此可以穿過物體看到東西。就像在你的物體表面刻出許多小孔。
在webgl中咱們須要使用ESSL的discard命令在片元着色器中去除掉一些片元。直接設置alpha厚webgl並不能自動產生透明效果。
建立透明物體意味着咱們要改表已經寫入幀緩衝區的片元。好比在一個場景中,這裏有一個透明的物體在一個非透明物體的前面。若是這個場景被正確渲染出來,那麼咱們須要可以透過透明物體看到非透明物體。所以在遠處和近處物體疊蓋處的片元須要以某種方式來建立出透明效果。
爲了實現透明效果咱們須要學習兩個總要概念:深度檢測、alpha混合。
Updated rendering pipeline
對於片元來講一旦他們被片元着色器處理以後,深度檢測和alpha混合就是兩個可選的階段。若是深度檢測沒有被激活,全部的片元對alpha混合來講都自動可用。若是深度檢測開啓了,這些在深度檢測結果爲false的片元都將被渲染管線自動拋棄在其餘操做中不在可用。這意味着被拋棄的片元將不會被渲染。
添加了深度檢測或alpha混合後的渲染管線以下圖所示:
Depth testing
每個被片元着色器處理後的片元都攜帶深度信息。儘管片元顯示在屏幕上是二維的,深度值保留了這個片元距離相機屏幕的距離。深度值被存儲在一個特殊的webgl緩衝區中稱爲深度緩衝區或z-buffer。
若是深度檢測被啓用了,在片元着色器計算完片元以後,下一步將進行深度檢測。開啓深度檢測:
gl.enable(gl.DEPTH_TEST)
深度檢測經過將一個片元色深度信息與相同片元位置處已經保存的在深度緩衝區中的深度值進行比較。深度檢測將決定這個片元是否將進入渲染管線將來的處理中。只有經過深度檢測的片元會被進一步處理,沒有經過深度檢測的片元將被拋棄。
通常狀況下,深度值小的片元將被接受(距離屏幕更近)。
深度測試是一種呈現順序的交換操做。這意味着不管先呈現哪一個對象,只要啓用深度測試,咱們老是有一個一致的場景
開啓深度檢測後,距離屏幕近(深度值)小的物體被呈現出來。距離屏幕遠的物體在這一疊蓋區中被拋棄
默認狀況下深度檢測是禁用的,咱們能夠啓用深度檢測,默認狀況下用gl.LESS方法來作深度測試,咱們能夠經過更改深度檢測的方式來作一些特殊效果:gl.depthFunc(gl.LESS)
Alpha blending
經過深度檢測的片元能夠進入alpha混合階段,然而若是深度檢測被關閉,全部片元均可以進入alpha混合階段。alpha經過:gl.enable(gl.BLEND)開啓。
對於每個合格的片元alpha混合操做讀取在幀緩衝中的當前位置片元的顏色,而後經過在片元着色器預先計算好的顏色和目前幀緩衝中已有的顏色進行線性差值來獲得一個的顏色。
Blending function
啓用alpha混合只有下一步就是定義一個混合函數。這個函數將決定如何將咱們將渲染的物體的顏色與當前已經存在幀緩衝中的顏色進行混合處理。
咱們使用以下公式來作alpha混合
注意渲染順序將決定在上面公式中的source和destination片元;將要渲染到frame buffer中的片元稱爲源,已經在framebuffer中的片元稱爲目標。
Separate blending functions
咱們也能夠將rgb與alpha單獨計算,使用gl.blendFuncSeperate函數,好比咱們能夠爲rgb和alpha使用以下兩個獨立公式
兩者單獨使用不一樣方式來計算:
函數:
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ZERO)
Blend equation
咱們還能夠爲顏色混合使用不一樣的混合公式:
alpha 混合相關API:
gl.enable(gl.BLEND)// 啓用混合
gl.blendFunc(sw, dw) // 決定對顏色模型中哪一部分進行操做
gl.blendFuncSeperate(sw_rgb, dw_rgb, sw_a, dw_a);// 決定如何對rgb和alpha分離計算
gl.blendEquation(mode)// 決定源和目標顏色的結合方式
gl.blendEquationSeparate(modeRGB, modeAlpha)// 決定分離方式中源和目標的結合方式
gl.blendColor設置混合顏色
gl.getParameter(pname)// 獲取混合相關參數
Creating transparent objects
爲了建立透明物體,咱們須要:
對於同一個物體,咱們使用面剔除方法來建立透明度。(由於一個物體沒有其餘物體來作顏色混合,因此處理思路是將一個物體的前景面和後鏡面分別當成兩個單獨物體來繪製,這樣就能夠用兩個物體作alpha混合。)
我的理解
深度檢測經過將一個片元色深度信息與相同片元位置處已經保存的在深度緩衝區中的深度值進行比較。深度檢測將決定這個片元是否將進入渲染管線將來的處理中。只有經過深度檢測的片元會被進一步處理,沒有經過深度檢測的片元將被拋棄。
通常狀況下,深度值小的片元將被接受(距離屏幕更近)。
深度測試是一種呈現順序的交換操做。這意味着不管先呈現哪一個對象,只要啓用深度測試,咱們老是有一個一致的場景
開啓深度檢測後,距離屏幕近(深度值)小的物體被呈現出來。距離屏幕遠的物體在這一疊蓋區中被拋棄
若是沒有開啓深度監測那麼就跟渲染順序相關;後渲染的物體會遮擋住先渲染的物體
確切來講深度監測判斷物體遠近不是以世界座標爲基準來判斷的而是以投影座標來判斷的
顏色混合發生在深度監測以後,只有經過深度檢測的片元纔會進入到顏色混合階段。
若是沒有開啓blend,那麼透明物體也會把不透明物體給遮擋住
若是開啓顏色混合,那麼只有經過深度監測的片元纔會進入顏色混合階段,沒有經過深度監測的片元將會被拋棄
這時候由於先繪製了cone,而在這個矩陣下,cone離着相機更近,因此有sphere部分片元沒有經過深度測試,直接被拋棄
若是先繪製sphere,cone全部片元都會經過深度測試,與已有的顏色緩衝發生顏色混合。
若是開啓顏色混合,物體的繪製順序最終會影響顏色混合的呈現效果
若是關閉深度監測,那麼混合效果徹底跟渲染順序相關(總體效果會跟物體位置相關,以下面兩圖都是先繪製椎體混合顏色是相同的,可是有近大遠小效果,因此球形和椎體的最終呈現效果有所差別)
下面是先繪製球形