寫在前面
[上一篇筆記](/articles/7 種 webgl 圖元-webgl 筆記 6/) 中,我們認識了 7 種圖元,改改參數就能畫出不同的東西,但具體細節沒有解釋清楚,這要從著色器的工作原理說起,而 varying 變量作為頂點著色器和片元著色器之間的數據通道,也是不得不說的
一.varying 變量
之前解釋過 2 種著色器變量:attribute 變量、uniform 變量,而 varying 變量是最後一種著色器變量,比前 2 種稍複雜一點,因為有內插(interpolate)的過程
varying 變量的作用是從頂點著色器向片元著色器傳值,varying 變量只能是 float 類型的,只要在片元著色器中也聲明同名 varying 變量,頂點著色器賦給該變量的值就會自動傳入片元著色器(在給片元著色器中的同名 varying 變量賦值之前,有內插的過程,1.0 傳過去不一定是 1.0)
注意:頂點著色器中的 varying 變量值不是直接傳遞,會先進行內插,內插就像補間動畫一樣
二.內插(interpolate)
插值,缺少數據才需要插值,比如想要把一系列散點連成平滑曲線,相鄰已知點之間缺少很多點,此時就需要通過內插填補缺少的數據,最終平滑曲線上除已知點之外的所有點都是插值得到的
例如 Photoshop 的自定義漸變,我們只需要設置幾個點的顏色就能自動生成一整條漸變帶,這幾個點之間的顏色都是通過內置插值算法得到的
varying 變量的值傳遞到片元著色器之前進行的插值過程被稱為內插,同樣,我們也可以利用內插生成漸變
三.漸變三角形
只要片元著色器中聲明與頂點著色器 varying 變量同名的 varying 變量,值就會自動傳遞過去(當然,有內插的過程)
1.著色器源程序
在片元著色器中聲明同名 varying 變量,具體如下:
// 頂點著色器源程序
var vsSrc = 'attribute vec4 a_Position;' +
'attribute vec4 a_Color;' +
'varying vec4 v_Color;' + // 聲明 varying 變量
'void main() {' +
'gl_Position = a_Position;' + // 設置坐標
'gl_PointSize = 7.0;' + // 設置尺寸
'v_Color = a_Color;' + // 給 varying 變量賦值
'}';
// 片元著色器源程序
//!!! 需要聲明浮點數精度,否則報錯 No precision specified for (float)
var fsSrc = 'precision mediump float;' +
'varying vec4 v_Color;' + // 聲明同名 varying 變量
'void main() {' +
'gl_FragColor = v_Color;' + // 設置顏色
'}';
注意:無法直接給 varying 變量賦值,需要藉助 attribute 變量接受外部值,再在頂點著色器內部給 varying 變量賦值
2.純色三角形與漸變三角形
關鍵是三個頂點顏色是否一致,因為實際過程是:
- 讀取頂點信息(坐標、顏色等等)
執行頂點著色器,讀取1 個頂點的相關數據
- 圖形裝配(畫點還是畫線畫三角)
將孤立的頂點坐標裝配成幾何圖形,圖形的類別由 gl.drawArrays 的第一個參數決定,比如 gl.POINTS, gl.TRIANGLES
- 光柵化(確定哪些像素需要著色)
將裝配好的幾何圖形轉化為片元,將矢量的幾何圖形轉變為柵格化的片元(像素)
- 執行片元著色器(著色)
對各個片元著色
- 回到第一步,讀完了就退出,沒完就讀取下一個頂點再來一遍
所以頂點著色器是逐頂點執行的,片元著色器是逐片元執行的,就像一個雙重循環,外層遍歷頂點,內層遍歷片元。以上過程如下圖所示:
[caption id="attachment_913" align="alignnone" width="773"]
webgl-vertex-shader-execution[/caption]
[caption id="attachment_914" align="alignnone" width="738"]
webgl-varying[/caption]
之前的 DEMO 中顏色都是在片元著色器中寫死的,比如 gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75),所以畫出的所有東西都是純色的,但更科學的方式是把頂點顏色傳入頂點著色器,根據頂點顏色內插出每個片元的顏色。比如畫一條孤立線段,2 個同色頂點之間的片元內插後都是該色,2 個不同色頂點之間的片元內插後就出現其它顏色了
3.繪製漸變三角形
把 3 個頂點設置成不同顏色,如下:
var arrVtx = new Float32Array([
// x, y, r, g, b
-0.5, 0.5, 1.0, 0.0, 1.0, 1.0, // 紅色
0.5, 0.5, 0.0, 1.0, 0.0, 1.0, // 綠色
-0.5, -0.5, 0.0, 0.0, 1.0, 1.0 // 藍色
]);
//...頂點數組寫入緩衝區
// 繪製點
gl.drawArrays(gl.POINTS, 0, arrVtx.length / 6);
setTimeout(function() {
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪製三角形
gl.drawArrays(gl.TRIANGLES, 0, arrVtx.length / 6);
}, 2000);
我們先繪製 3 個孤立點(紅綠藍),2s 後繪製三角形,漸變三角形就出現了,這也說明了設置 gl_PointSize 只在 draw point 時有效,繪製三角形會自動忽略該值
四.DEMO
包含上述代碼的完整的例子,請查看:
根據坐標設置片元顏色,驗證片元著色器逐片元執行的過程
注意:片元著色器的內置變量 gl_FragCoord 保存著片元的坐標信息 (gl_FragCoord.x, gl_FragCoord.y),如果能夠輸出的話,就會發現片元著色器逐頂點執行的過程,但 API 沒有提供輸出信息到 console 的方法,我們可以根據片元坐標設置顏色來驗證
五.總結
varying 變量不僅能夠實現漸變效果,還可以利用它內插出各種拿不到的數值,比如片元的世界坐標,片元在光源坐標系中的坐標等等,非常有用
我們現在已經可以繪製彩色圖形了,與顏色有關的另一個特性是貼圖(紋理映射),這是下一篇筆記的內容
參考資料
- 《WebGL 編程指南》
暫無評論,快來發表你的看法吧