本文整理自 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繪制最簡單的幾何圖形?
歡迎關注凹凸實驗室博客:aotu.io
或者關注凹凸實驗室公眾號(AOTULabs),不定時推送文章:

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/449108.html
標籤:其他
上一篇:less的使用方法
下一篇:精靈圖和字體圖示的概念
