一.canvas 2D 繪製矩形
用 canvas 2D 繪製一個矩形很容易,幾行代碼輕鬆搞定:
<!-- html -->
<h5>canvas2d</h5>
<canvas id="canvas2d" width="400" height="400">
Please use a browser that supports "canvas"
</canvas>
// js
// 獲取元素引用
var canvas2d = document.getElementById('canvas2d');
// 獲取 2D context
var ctx = canvas2d.getContext('2d');
// 設置填充色
ctx.fillStyle = 'rgba(255, 0, 255, 0.75)';
// 繪製矩形塊
ctx.fillRect(100, 100, 200, 200);
實際效果就是一個 200x200px 淡紫色矩形塊,周圍一圈寬度 100px 的透明環。從代碼的角度來看,應該是:400x400px 的透明 canvas 中心被塗上了一個不透明度為 0.75 的 200x200px 的紫色矩形塊
從 ctx.fillRect(100, 100, 200, 200); 可以看出 canvas 2D 的坐標系與屏幕坐標系一樣,左上角是 (0, 0),x 軸向右為正,y 軸向下為正
從 ctx.fillStyle = 'rgba(255, 0, 255, 0.75)'; 可以看出 rgba() 與 CSS 一樣,rgb 值為 0255,a 為 01 表示不透明度
二.WebGL 繪製矩形
1.獲取 context
類似於 canvas2d.getContext('2d'),WebGL 需要獲取專用的 context:
var webgl = document.getElementById('webgl');
var gl = webgl.getContext('webgl');
這裡習慣把 WebGL 的 context 命名為 gl,是為了以後調用 API 時與 OpenGL 調用方式保持一致,嗯,就圖個好看
2.清空 canvas
類似於 canvas 2D 提供的 clearRect 方法,WebGL 也有橡皮擦一樣的東西,而且更強大,可以指定擦除後的顏色:
// 指定清空 canvas 的顏色
// 參數是 rgba,範圍 0.0~1.0
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空 canvas
// gl.COLOR_BUFFER_BIT 顏色緩存,默認清空色 rgba(0.0, 0.0, 0.0, 0.0) 透明黑色,通過 gl.clearColor 指定
// gl.DEPTH_BUFFER_BIT 深度緩存,默認深度 1.0,通過 gl.clearDepth 指定
// gl.STENCIL_BUFFER_BIT 模板緩存,默認值 0,通過 gl.clearStencil() 指定
gl.clear(gl.COLOR_BUFFER_BIT);
對於本例而言,清空 canvas 不是必須的,但一定要會用這種方法,不然就瘋了,因為在 canvas 2D 中,如果要設置黑色背景,我們會這樣做:
// 黑色背景
ctx.fillStyle = 'rgba(0, 0, 0, 1)';
ctx.fillRect(0, 0, canvas2d.width, canvas2d.height);
同樣的思路,畫一個黑塊鋪滿整個 canvas,在 WebGL 中當然也可以,但會相當麻煩
3.繪製矩形
為了繪製一個矩形,我們需要先做 8 件事情:
- 編寫著色器源程序
使用著色器語言(GLSL ES)編寫著色器源程序,再以字符串形式傳入 WebGL 系統內部編譯執行:
// 0.著色器源程序
// 頂點著色器源程序
var vsSrc = 'void main() {' +
'gl_Position = vec4(0.0, 0.0, 0.0, 1.0);' + // 設置坐標
'gl_PointSize = 200.0;' + // 設置尺寸
'}';
// 片元著色器源程序
var fsSrc = 'void main() {' +
'gl_FragColor = vec4(1.0, 0.0, 1.0, 0.75);' + // 設置顏色
'}';
GLSL ES 的語法和 C 語言比較像,具體語法規則在以後的筆記裡再說
- 創建著色器對象
沒什麼好說的,遊戲規則
// 1.創建著色器對象
var vs = gl.createShader(gl.VERTEX_SHADER);
var fs = gl.createShader(gl.FRAGMENT_SHADER);
// 檢查創建結果
if (vs === null) {
log('gl.createShader(gl.VERTEX_SHADER) failed');
}
if (fs === null) {
log('gl.createShader(gl.FRAGMENT_SHADER) failed');
}
3. 填充源程序
有了著色器對象,就可以把著色器源程序塞進去了
// 2.填充源程序
gl.shaderSource(vs, vsSrc);
gl.shaderSource(fs, fsSrc);
4. 編譯
有了源碼,趕緊編譯,看報不報錯
// 3.編譯
gl.compileShader(vs);
gl.compileShader(fs);
// 檢查編譯錯誤
if (!gl.getShaderParameter(vs, gl.COMPILE_STATUS)) {
log('gl.compileShader(vs) failed');
log(gl.getShaderInfoLog(vs)); // 輸出錯誤信息
}
if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
log('gl.compileShader(fs) failed');
log(gl.getShaderInfoLog(fs)); // 輸出錯誤信息
}
5. 創建程序對象
除了著色器對象,還需要程序對象,js 與 WebGL 內部交流主要通過這個程序對象,先做一個出來
// 4.創建程序對象
var prog = gl.createProgram();
// 檢查創建結果
if (prog === null) {
log('gl.createProgram() failed');
}
注意:上面 4 步都是一個頂點 XX 和一個片元 XX,而這一步只需要 1 個 prog 對象
- 為程序對象分配著色器
每個 prog 都需要 2 個著色器(1 個頂點著色器,1 個片元著色器),所以檢查分配錯誤時要與 2 比較,看 prog 身上是不是綁了 2 個 shader
// 5.為程序對象分配著色器
gl.attachShader(prog, vs);
gl.attachShader(prog, fs);
// 檢查分配錯誤
if (gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) !== 2) {
log('gl.getProgramParameter(prog, gl.ATTACHED_SHADERS) failed');
}
7. 連接程序對象
類似於 C 語言程序的 compile -> link -> run
// 6.連接程序對象
gl.linkProgram(prog);
// 檢查連接錯誤
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
log('gl.linkProgram(prog) failed');
log(gl.getProgramInfoLog(prog));
}
8. 使用程序對象
不做錯誤檢查,因為沒有返回值
// 7.使用程序對象
gl.useProgram(prog);
至此,為繪製一個矩形做的準備工作就完成了
最後當然是 draw 點什麼
// 繪製矩形(一個點,但點的尺寸略大)
gl.drawArrays(gl.POINTS, 0, 1);
三.DEMO
包含上述代碼的完整的例子,請查看:http://www.ayqy.net/temp/webgl/hoho/index.html
四.WebGL 與 canvas 2D 的差異
1.坐標系不同
在 WebGL 中,canvas 的中心是 (0, 0),x 軸向右為正,y 軸向上為正,z 軸從屏幕射出指向臉為正(右手坐標系)
2.坐標值不同
WebGL 中,整個畫布是 Rect(-1.0, 1.0, 2, 2),也就是說 canvas 左上角坐標是 (-1.0, 1.0),右上角坐標為 (1.0, 1.0),畫布尺寸是 2x2,如果 canvas 本身是 400x400px 的,那麼 WebGL 中坐標值的單位就是 200px
注意:對於非坐標值屬性,比如上面著色器源程序中的 gl_PointSize,其單位仍然是 px
3.rgb 值不同
片元著色器中有用到色值,vec4(r, g, b, a),與 CSS 的 rgba() 不同,WebGL 中 rgb 值範圍是 0.0~1.0,而且必須是帶小數的形式,因為 vec4 構造函數只接受 float 型參數,int不會被隱式轉換為 float
4.顏色疊加規則不同
運行 DEMO 能夠發現 canvas 2D 與 WebGL 畫出的紫色矩形顏色不同,canvas 2D 中我們先繪製了黑色背景,再畫上了紫色矩形,紫色矩形有 0.25 的透明,最終顯示的顏色是紫色與黑色的疊加。
而 WebGL 中,紫色矩形的顏色沒有與黑色疊加(body 背景色就是我們給紫色矩形指定的顏色,能清楚地看到 WebGL 中沒有發生紫黑疊加)。嘗試在紫色矩形背後(z 坐標為 -0.1)再繪製一個黑色矩形,發現顏色仍然沒有疊加,這需要特別注意
五.總結
從 API 使用步驟上來看 WebGL 就是這麼麻煩,畫一個矩形都要寫這麼多代碼,實際上我們只繪製了一個點(略大只的點),繪製矩形還要更麻煩些,需要用 4 個頂點確定 2 個三角片,再分別填色
至於頂點著色器,片元著色器是什麼?有什麼作用?需要一個稍微複雜一點的例子來說明,我們在以後的筆記裡再解釋
參考資料
- 《WebGL 編程指南》
暫無評論,快來發表你的看法吧