WebGL 入門實例

看到不少 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 上下文函數, 僞裝本身入門了.

相關文章
相關標籤/搜索