跳到主要內容
黯羽輕揚每天積累一點點

用 WebGL 繪製一個矩形_WebGL 筆記 1

免費2015-12-19#WebGL#JS#webgl#WebGL入门#WebGL教程#WebGL指南#webgl实例

本文介紹一個簡單的 WebGL 程序,與 canvas 2D 相比,除了代碼量劇增,需要掌握的相關知識也多了很多

一.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 件事情:

  1. 編寫著色器源程序

使用著色器語言(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. 創建著色器對象

沒什麼好說的,遊戲規則

    // 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 對象

  1. 為程序對象分配著色器

每個 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 編程指南》

評論

暫無評論,快來發表你的看法吧

提交評論