WebGL 的 Hello World
本文整理自 div 俠於 凹凸 2022 年技術分享,簡單介紹了 WebGL 畫一個基礎圖形的流程,希望你瞭解之後,在使用 3d 渲染庫的時候可以少點迷糊。
四種常用的頁面繪圖工具—
關於 h5 頁面的圖形繪製,我們大多談及的是這四種工具:html+css,svg、canvas2d、WebGL。
html+css 是最常見的繪圖工具了,使用 css 繪圖跟平時寫頁面佈局一樣,在製作圖表的時候,我們可以用 css 把圖表的樣式定義好,其他的,就是根據資料的不同 ,給元素新增上不同的屬性。這樣的開發對於圖表元素簡單、資料結點少的場景非常友好。不僅可以減少開發的工具量,而且不用引入多餘的程式碼庫。但是,隨時需要繪製的圖形越來越多, css 程式碼做變得越來越複雜,加上 css 本來沒有邏輯語義,程式碼會變得不易閱讀和維護。
svg 是可縮放向量圖形,他跟 html、css 的結合很緊密,可以把 svg 當做 img 的 src ,也可以用 css 操控 svg 的屬性, svg 和 html 都是文字標記語言, svg 較 html 增加了對非線性圖形的支援,包括圓弧,貝塞爾曲線等。同時,svg 支援
canvas2D 是 canvas 的 2d 繪圖上下文,他提供了一系列方法,用於對 canvas 區域的影象進行修改和繪製,相比於前兩者的開箱即用, canvas2d 很多圖形和顏色都需要自己實現和封裝使得這個工具上手的難度大了不少,但是,如果把這些基礎的事情做好,你將擁有一個功能完全覆蓋前面兩個工具,而且便於擴充套件的繪圖工具。
WebGL 也是 canvas 的繪圖上下文,是 OpenGL es 的 web 實現。最大的特點,就是更低層,可以直接使用 gpu 的並行能力。在處理圖形數量非常多,畫素級處理和 3d 物體的場景下,擁有很高的效能優勢。
四種工具的選擇思路
當我們拿到一個繪圖需求的時候,應該先看看這個需求用到的圖形是不是比較少,而且簡單。如果是的話,可以直接選擇 css 進行快速開發。如果圖形雖然簡單但比較多,或者圖形有一些曲線需求,這個時候 svg 還可以快速應付。如果圖形之間的結構複雜,數量比較多的時候選擇 canvas2d 。而當圖形的數量級大到一定的量,或者需要對每一個畫素進行處理,或者需要大量的 3d 展示的時候,我們得使用 WebGL 了

WebGL 的 Hello World—
WebGL 的 Hello World 不像其他工具一樣可以一兩行程式碼就搞定,而是足足有四十多行程式碼。雖然這串程式碼在各個 3d 渲染庫裡都有對應封裝的方法,基本不用我們自己徒手去寫,但是學習這串程式碼可以讓我們對 WebGL 繪圖過程有一個最基礎的瞭解。
WebGL 繪圖一共有五個步驟:
-
建立 WebGL 繪圖上下文 -
建立著色器程式設計,關聯到 gl 上下文中 (跟第3步並行) -
建立資料,放入緩衝區並把緩衝區關聯到 gl 上下文中(跟第2步並行) -
GPU 載入快取中的資料 -
繪製圖形
建立 WebGL 上下文
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
建立著色器程式
const program = gl.createProgram();
gl.attachShader(program, /*某個著色器(下文的vertexShader)*/);
gl.linkProgram(program);
gl.useProgram(program);
著色器是一段給 gpu 執行的程式,我們用 glCreateProgram 建立一個空的程式物件,然後使用 glAttachShader 給這個程式物件填充編譯後的著色器程式碼。著色器是什麼,怎麼編譯後面再說,這裡可以把他當成某一個函式編譯後的程式碼。把幾個這種編譯後的函式放入程式物件後, GPU 執行這個程式物件,就會把畫素資訊當做入參,依次執行程式物件中的函式。
填充完著色器程式碼後,呼叫 glLinkProgram 把程式關聯到 gl 上下文中,並用 glUseProgram 來啟用這個程式。
接下來,來看一下著色器程式碼怎麼搞出來。
const vertex = `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 1.0, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.compileShader(vertexShader);
首先我們定義了一個變數 vertex 並給他賦值一串其他語言格式的程式碼字串,這個串程式碼是 glsl 程式碼,是一個跟 c 語言很相似的程式碼。程式碼接收一個傳入的二維向量 position ,然後把他執行環境中的全域性變數 gl_Position 設定成一個四維向量,這個四維向量前兩個維度的分量是傳入的二維向量。
接下來用 glCreateShader 建立一個著色器, VERTEX_SHADER 常量說明這個著色器是一個頂點著色器,跟頂點著色器對應的是片元著色器,頂點著色器處理做為確定點的位置。片元著色器則對頂點構成的圖形中的所有位置進行逐個處理,比如兩點畫一個直線,兩點是頂點著色器確定的,直線是片元著色器在確定了兩個點的位置之後畫的。
在我們建立了一個空的頂點著色器物件 vertexShader 之後,就可以用 glShaderSource 把前面的字串程式碼放入頂點著色器物件中,然後用 glCompileShader 把這段程式碼編譯成可執行檔案。這個過程跟 c 語言的編譯過程是相似的。
gl.attachShader(program, /*某個著色器(下文的vertexShader)*/);
gl.attachShader(program, vertexShader);
完成這一步之後,就要回到上面寫註釋那裡,把著色器物件關聯到程式物件裡。當然,你還得去寫一個片元著色器,用同樣的步驟把一個片元著色器也關聯到程式物件裡。

將資料存入緩衝區
const points = new Float32Array([-1, -1, 0, 1, 1, -1]);
const bufferId = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, bufferId);
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
經過上文的操作之後,我們已經有了一個裝載著著色器程式碼的程式物件,這個物件放到 gl 繪圖上下文中被啟用了。接下來,我們要定義的就是給這個程式用的資料。
在頂點著色器那一塊,程式碼裡面接受一個傳入的二維向量,就是我們現在要定義的。首先定義一個型別化陣列,初始化的時候放入 6 個數,這個 6 個數後面會被繪圖程式分成三組放到三次頂點著色器呼叫中。另外,使用型別化陣列是為了優化效能,讓大量資料的情況下,資料佔用的空間更小。
有了資料之後,呼叫 glCreateBuffer 建立一個緩衝區物件,用 glBindBuffer 把這個物件跟 gl 繪圖上下文關聯起來,最後呼叫 glBufferData 把 points 的資料放入緩衝區中。
gpu載入快取中的資料
const vPosition = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(vPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vPosition);
在這一步中,我們先呼叫 glGetAttribLocation 拿到程式物件中 position 這個變數的位置,呼叫 glVertexAttribPointer 把這個變數的長度設定為 2,型別設定成 glFLOAT,並用 glEnableVertexAttribArray 啟用這個變數
繪製圖形
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
到了最後一步,只要用 glClear 把顏色緩衝區清空,然後用 glDrawArrays 進行繪圖就行了。其中 gl.TRIANGLES 確定了片元著色器的繪圖範圍,當這個值是 gl.POINTS,著色器會把點兩兩連線,而 gl.TRIANGLES 讓第三個點成一組繪製三角形

這樣,WebGL 的一個 Hello World 就完成了,上面的三角形就是這40 行程式碼輸出的影象。
總結—
這段程式在 three.js 和其他的 3d 框架和工具庫裡都有一定的封裝,通過那些庫進行 WebGL 的繪圖相對來說會方便很多,但如果不知道這些庫最根本的操作,就很容易在遇到問題的時候繞進去。所以希望本文能增加大家對 web 3d 底層方面的理解,給大家在學習這些3d工具庫的時候提供一些幫助。
參考資料—
GPU與渲染管線:如何用WebGL繪製最簡單的幾何圖形?: http://time.geekbang.org/column/article/63c01cb24d76d96cfd6a9ce64db6a623/share?code=CsdMNTf%2FboqZugxI1qspWwJxCaw2PUoiTwhMmOZ4Klw%3D&source=app_share
本文分享自微信公眾號 - 凹凸實驗室(AOTULabs)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。
- 2023 年的 Web Worker 專案實踐
- 型別體操的9種類型運算、4種類型套路總結
- 由淺入深瞭解羚瓏平臺統一接入服務 —— Monet
- 元宇宙 3D 開荒場 - 探味奇遇記
- 元宇宙 3D 開荒場 - 探味奇遇記
- 論 T 級互動開發如何在我們手上發光發熱
- 論 T 級互動開發如何在我們手上發光發熱
- Taro 3.5 beta 編譯提速,支援 Webpack5、React 18...
- 3D 沙盒遊戲之人物的點選行走移動
- 3D 沙盒遊戲之人物的點選行走移動
- 3D 沙盒遊戲之地面網格設計
- 3D 沙盒遊戲之地面網格設計
- 元宇宙探索之路
- 3D 沙盒遊戲之避障踩坑和實現之旅
- WebGL 的 Hello World
- 不懂物理的前端不是好的遊戲開發者(二)—— 物理引擎的學習之路
- Web3D 從入門到跑路 · 3D 初體驗
- Taro 在多端浪潮下的選擇與挑戰
- Taro 在多端浪潮下的選擇與挑戰
- Taro 3 與原生小程式混合開發實踐