什么是WebGL?
WebGL是一種可以在網(wǎng)頁(yè)上繪制渲染3D圖形,并允許用戶與之交互的技術(shù)。瀏覽器內(nèi)置了WebGL,可以搭配HTML5和JavaScript使用。
WebGL的優(yōu)勢(shì)?
充分利用前端開發(fā)與瀏覽器的優(yōu)勢(shì),不需要復(fù)雜的開發(fā)環(huán)境,可以輕松開發(fā)可移植性強(qiáng)、兼容性強(qiáng)的三維圖形程序。WebGL程序使用三種語(yǔ)言開發(fā):HTML、JavaScript、GLSL ES(OpenGL ES)著色器代碼內(nèi)嵌在JavaScript中,以字符串形式在JavaScript中編寫。
例2.1 DrawRectangle 繪制一個(gè)實(shí)心藍(lán)色矩形
目標(biāo): 了解canvas標(biāo)簽與繪圖上下文
步驟:
- 獲取canvas元素
var canvas = document.getElementById('canvas');
- 向該元素請(qǐng)求二維圖形的繪圖上下文
var ctx = canvas.getContext('2d');
- 使用繪圖上下文的繪圖函數(shù),繪制二維圖形
// 設(shè)置填充顏色為藍(lán)色
ctx.fillStyle = 'rgba(0, 0, 255, 1.0)';
// 使用填充顏色填充指定大小和位置的矩形
ctx.fillRect(120, 10, 150, 150);
fillRect函數(shù)前兩個(gè)參數(shù)是位置,后兩個(gè)參數(shù)是寬高。

例2.2 HelloCanvas 清空/使用指定顏色填充canvas
目標(biāo): 使用指定顏色清空畫布
步驟:
獲取canvas元素
獲取WebGL繪圖上下文
var gl = getWebGLContext(canvas);
- 設(shè)置背景色,設(shè)置的背景色會(huì)常駐系統(tǒng),待下一次調(diào)用前不會(huì)改變
gl.clearColor(0.0, 0.0, 1.0, 1.0);
- 清空canvas,用背景色填充,擦除已經(jīng)繪制的內(nèi)容
gl.clear(gl.COLOR_BUFFER_BIT);
gl.COLOR_BUFFER_BIT指的是顏色緩沖區(qū)(color buffer),除此之外還有深度緩沖區(qū)和模板緩沖區(qū)。
例2.3 HelloPoint1 繪制一個(gè)點(diǎn)(版本1)
目標(biāo): 學(xué)習(xí)編寫著色器代碼,初始化著色器并繪制圖形
著色器是WebGL的繪圖機(jī)制,著色器包括頂點(diǎn)著色器和片元著色器兩種,頂點(diǎn)著色器是用于描述頂點(diǎn)特性(如位置、顏色等)的程序,頂點(diǎn)指的是圖形的端點(diǎn)和交點(diǎn),片元著色器是進(jìn)行逐片元處理過(guò)程(如光照等)的程序,片元是WebGL術(shù)語(yǔ),可以理解為像素。
步驟:
獲取canvas元素
獲取WebGL繪圖上下文
初始化著色器
initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)

著色器初始化之前,頂點(diǎn)和片元著色器都是空白的,函數(shù)將字符串形式的著色器代碼從JavaScript傳給WebGL系統(tǒng)并建立著色器。

設(shè)置背景色
清空canvas
繪制一個(gè)點(diǎn)
gl.drawArrays(gl.POINTS, 0, 1);

調(diào)用該函數(shù),頂點(diǎn)著色器被執(zhí)行count次,每次處理一個(gè)頂點(diǎn)。
著色器代碼:
// 頂點(diǎn)著色器程序
var VSHADER_SOURCE =
'void main() {\n' +
' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + // 設(shè)置坐標(biāo)
' gl_PointSize = 10.0;\n' + // 設(shè)置尺寸
'}\n';
// 片元著色器程序
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 設(shè)置顏色
'}\n';
在著色器代碼中,gl_Position必填,指定頂點(diǎn)位置,vec4表示4個(gè)浮點(diǎn)數(shù)矢量,gl_PointSize為頂點(diǎn)尺寸,gl_FragColor為片元顏色。

WebGL坐標(biāo)為右手坐標(biāo)系統(tǒng),并且在二維平面中與canvas坐標(biāo)系統(tǒng)也不一致。


例2.4 HelloPoint2 繪制一個(gè)點(diǎn)(版本2)
目標(biāo): 使用attribute變量,使用attribute或uniform變量將位置信息從JavaScript程序中傳給頂點(diǎn)著色器,attribute變量傳輸與頂點(diǎn)相關(guān)的數(shù)據(jù),uniform變量傳輸與頂點(diǎn)無(wú)關(guān)的數(shù)據(jù)
步驟:
- 在頂點(diǎn)著色器代碼中,聲明attribute變量,并賦值給gl_Position變量
'attribute vec4 a_Position;\n' +
···
' gl_Position = a_Position;\n' + // 設(shè)置坐標(biāo)
- 著色器初始化后,獲取attribute變量的存儲(chǔ)位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');

- 向attribute變量傳遞數(shù)據(jù)
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);

gl.vertexAttrib?f(?=1/2/3/4)的任務(wù)是從JavaScript向頂點(diǎn)著色器中的attribute變量傳值。
例2.5 ClickedPoints 通過(guò)鼠標(biāo)點(diǎn)擊繪點(diǎn)
目標(biāo): 加入鼠標(biāo)交互,通過(guò)鼠標(biāo)點(diǎn)擊繪點(diǎn),了解瀏覽器、canvas、webgl坐標(biāo)系統(tǒng)與相互轉(zhuǎn)換
步驟:
- 為鼠標(biāo)點(diǎn)擊事件綁定回調(diào)函數(shù)click
canvas.onmousedown = function(ev) {
click(ev, gl, canvas, a_Position);
}
- 通過(guò)event可以獲得鼠標(biāo)點(diǎn)擊的位置(瀏覽器頁(yè)面坐標(biāo)),該坐標(biāo)需要轉(zhuǎn)換到canvas坐標(biāo)系統(tǒng)下,再轉(zhuǎn)換到webgl坐標(biāo)系統(tǒng)下
var x = ev.clientX; // 鼠標(biāo)點(diǎn)擊處的x坐標(biāo)
var y = ev.clientY; // 鼠標(biāo)點(diǎn)擊處的y坐標(biāo)

var rect = ev.target.getBoundingClientRect();
ev.target表示canvas元素,getBoundingClientRect用于獲取某個(gè)元素相對(duì)于視窗的位置集合,返回包含top、left、bottom、right、width和height屬性的對(duì)象。

坐標(biāo)轉(zhuǎn)換代碼如下:
x = ((x - rect.left) - canvas.width/2)/(canvas.width/2);
y = (canvas.height/2 - (y - rect.top))/(canvas.height/2);

- 清空canvas,根據(jù)數(shù)組的每個(gè)元素,在相應(yīng)位置繪制點(diǎn):點(diǎn)位置傳遞給變量,并繪制
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
var len = g_points.length;
for (var i = 0; i < len; i += 2) {
// 將點(diǎn)的位置傳遞到變量a_Position中
gl.vertexAttrib3f(a_Position, g_points[i], g_points[i+1], 0.0);
// 繪制點(diǎn)
gl.drawArrays(gl.POINTS, 0, 1);
}
存儲(chǔ)點(diǎn)坐標(biāo)的數(shù)組會(huì)存儲(chǔ)所有點(diǎn)坐標(biāo),每次點(diǎn)擊后指定canvas背景顏色(設(shè)置顏色緩沖區(qū)),再把數(shù)組中的所有點(diǎn)繪制出來(lái)。若注釋清空canvas代碼,在第一次點(diǎn)擊后canvas背景色變?yōu)榘咨?,因?yàn)槔L制點(diǎn)后,顏色緩沖區(qū)會(huì)被重置為默認(rèn)顏色。
例2.6 ColoredPoints 改變點(diǎn)的顏色
目標(biāo): 熟悉uniform變量的使用
步驟:
- 在片元著色器中定義并使用uniform變量,注意在片元著色器中使用變量時(shí)需要使用精度限定詞指定變量的范圍和精度,否則程序會(huì)報(bào)錯(cuò)
var FSHADER_SOURCE =
'precision mediump float;\n' + // 精度限定詞
'uniform vec4 u_FragColor;\n' + // uniform變量
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' + // 設(shè)置顏色
'}\n';
- 獲取uniform變量地址
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');

- 向uniform變量傳遞數(shù)據(jù)
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
