看到不少 WebGL 文章都是講 three.js
, 我的以爲拋開第三方庫使用 WebGL 仍是蠻好玩的. 前端可能沒有時間去學 OpenGL ES 這些東西, 因此剛上手 WebGL 會以爲有點奇怪. 由於除了 JavaScript 還要會 GLSL 語法. WebGL 也是有多個版本的, 有 1.0 版本跟 2.0 版本, 1.0 版本對應的是 OpenGL ES 2.0, 對應的 GLSL 語法基本上沒區別.html
項目用的是 TypeScript, 不想本身配 ts 項目的直接克隆下來使用吧. git 地址前端
HTML 直接放個 div
做爲 canvas
的容器.git
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= htmlWebpackPlugin.options.title %></title>
<link rel="icon" href="/favicon.ico" type="image/x-icon"/>
</head>
<body>
<div class="canvas-area">
</div>
</body>
</html>
複製代碼
直接在腳本里生成 canvas
加載到 HTML 去.web
const canvas = document.createElement('canvas');
canvas.className = 'canvas';
canvas.id = "webgl-canvas";
canvas.width = innerWidth;
canvas.height = innerHeight;
$('.canvas-area').appendChild(canvas);
複製代碼
canvas
是 WebGL 活動的場所, 因此須要把這個東西加載上 HTML 上, 而後就能夠開始活動了. 通常使用 canvas
都是用 canvas.getContext('2d')
獲取上下文對象來進行一系列操做, WebGL 也逃不出 canvas 的基礎套路.typescript
let gl = canvas.getContext('webgl');
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
...
複製代碼
WebGL 除了用上上下文的一些函數, 還有個很重要的東西, 那就是 shader
, 這玩意是 GPU 執行的, 稍微瞭解過一點 OpenGL, 應該知道 OpenGL 的渲染流程有不少環節都是 shader
處理, 寫 WebGL 應用其實咱們基本上只要關注 vertex shader 跟 fragment shader. 一個是描述圖形在 canvas 的位置信息, 一個是描述圖形的片斷信息. 在一條流水線上, 某一處是 vertex shader, 它處理結束後把東西交給 fragment shader 處理. 一個 WebGL 應用, 要包含這兩個 shader, 因此咱們要寫一點東西加載處理一下 shader.canvas
function shader(gl, type: GLenum, source: string): void | WebGLShader {
const s: WebGLShader = gl.createShader(type);
gl.shaderSource(s, source);
gl.compileShader(s);
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
console.log('Failed to compile shaders', gl.getShaderInfoLog(s));
gl.deleteShader(s);
return null;
}
return s;
}
function link(gl, vs: string, fs: string): WebGLProgram | void {
const vertex = shader(gl, gl.VERTEX_SHADER, vs);
const fragment = shader(gl, gl.FRAGMENT_SHADER, fs);
if (!!vertex && !!fragment) {
const program: WebGLProgram = gl.createProgram();
gl.attachShader(program, vertex);
gl.attachShader(program, fragment);
gl.linkProgram(program);
return program;
}
return null;
}
function program(gl, vs: string, fs: string): void {
const program = link(vs, fs);
if (!!program) {
gl.useProgram(program);
}
}
複製代碼
有這些東西, 再寫點 GLSL 語句就行了.app
const vertex_shader = `
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
`;
const fragment_shader = `
void main() {
gl_FragColor = vec4(0.3, 0.7, 0.6, 1.0);
}
`;
複製代碼
而後調用以前的函數, 加載 shader 加載完了把東西畫出來函數
program(gl, vertex_shader, fragment_shader);
gl.drawArrays(gl.POINTS, 0, 1);
複製代碼
刷新頁面, 應該能看到中心一個有顏色的點, 可是很不明顯, 能夠在 vertex_shader
main 函數內加一句 gl_PointSize = 10.0;
如今就能看到放大十倍的點了. drawArrays
函數能夠用來將 webgl 上下文綁定的數據畫成圖, 只要給定要畫的類型, 譬如這裏給的是 gl.POINTS
類型.webgl
上面說了咱們主要關注 vertex_shader
fragment_shader
這兩個東西. vertex_shader
文件寫好的代碼最後是在 GPU 上執行的, 一些矩陣向量運算, 其實所有的運算交給 CPU 處理也是能夠的, 可是 GPU 蠻擅長處理這些運算, 專業的事交給專業的人, 如今這邊沒作什麼運算, 只是把圖形的位置給寫上去了, 加上後面放大 point 尺寸的代碼, gl_Position
是 WebGL 內置的一個四份量的向量類型變量, 因此咱們要給它賦值就寫一個 vec4
類型的值, 每一個份量依次表明的是 x
, y
, z
, w
. 咱們用 WebGL 來渲染 3D 空間內的圖形的, 能夠想到用一個三份量的向量來描述圖形在 3D 空間的位置, 爲何這個內置的 gl_Position
是一個四份量的向量, 由於咱們還要考慮矩陣運算, 因此引入了一個齊次座標, 齊次座標的概念就是一個 n 維向量用 n + 1 維向量來表示, vec4(x, y, z, w)
就跟 vec3(x / w, y / w, z / w)
相等. w 份量必須是要大於 0 的, w 越接近 0 就表示圖形離咱們越遠, 因此 w 的值要比 0 大咱們纔看獲得圖形. 這裏 gl_Position
除了 w
份量其餘都是 0.0
, 由於 WebGL 當前咱們認爲使用的是笛卡爾座標系, x
, y
, z
, 都是 0.0
的話, 畫出來的圖就在 canvas
正中間, 其實就是座標原點. 其實還有不少其餘座標概念, 可是目前就處理一些 2D 圖形, 不用考慮那麼多.ui
而後就是 fragment_shader
, 這個 shader
目前是拿來給內置的顏色變量 gl_FragColor
賦值, 這個變量也是個四份量的向量, 也就是 rgba 四個通道的顏色表示.
反正就這樣吧, GLSL 學一下, 學點 webgl 上下文函數, 僞裝本身入門了.