初識 WebGL
什么是 WebGL
webgl 在支持 canvas 的瀏覽器中進行 2d 或 3d 渲染,
webgl 程式除了有 Html、javascript,還需要加入著色器語言(GLSL ES),
WebGL 使得網頁在支持 HTML <canvas> 標簽的瀏覽器中,不需要使用任何插件,便可以使用基于 OpenGL ES 2.0 的 API 在 canvas 中進行 3D 渲染 —— MDN WebGL 教程
通過 caniuse 得知 webgl(98.15%) 和 webgl 2.0(94.12%) 的支持情況,請看下圖:

Tip:個人計算機上,繪制三維最廣泛使用的技術有 Direct3D 和 OpenGL,前者是微軟的,后者是開源免費的,OpenGL 有個特殊版本 OpenGL ES 專門用于嵌入式計算機、手機,而 WebGL 就是從 OpenGL ES 派生出來的,下圖是 OpenGL、OpenGL ES、WebGL 三者之間的關系,其中 webgl 2.0 基于 OpenGL ES 3.0 未畫出來:

canvas
Canvas_API 提供了一個通過JavaScript 和 HTML的 <canvas>元素來繪制圖形的方式,它可以用于影片、游戲畫面、資料可視化、圖片編輯以及實時視頻處理等方面,
Canvas API 主要聚焦于 2D 圖形,而同樣使用<canvas>元素的 WebGL API 則用于繪制硬體加速的 2D 和 3D 圖形,
示例:
// canvas.html
<body>
<canvas id="canvas" height="300">
抱歉,您的瀏覽器不支持 canvas 元素
(這些內容將會在不支持<canvas>元素的瀏覽器或是禁用了 JavaScript 的瀏覽器內渲染并展現)
</canvas>
<script>
var canvas = document.getElementById('canvas');
// getContext - 方法回傳canvas 的背景關系,如果背景關系沒有定義則回傳 null
var ctx = canvas.getContext('2d');
// 設定填充顏色
ctx.fillStyle = 'green';
// 繪制矩形
ctx.fillRect(10, 10, 100, 100);
</script>
</body>
效果如下:

Tip:不管繪制二維還是三維都是這三步:
- 獲取 canvas
- 請求繪圖背景關系
- 呼叫繪圖背景關系中的繪圖函式
第一個webgl示例
需求:清慷訓圖區,也就是使用背景色清空 canvas 的繪圖區
實作如下:
// webgl01.html
<body>
<canvas id="canvas" height="300">
抱歉,您的瀏覽器不支持 canvas 元素
(這些內容將會在不支持<canvas>元素的瀏覽器或是禁用了 JavaScript 的瀏覽器內渲染并展現)
</canvas>
<script>
var canvas = document.getElementById('canvas');
const gl = canvas.getContext("webgl");
// 使用完全不透明的藍色清除所有影像
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 用上面指定的顏色清除緩沖區
gl.clear(gl.COLOR_BUFFER_BIT);
</script>
</body>
效果如下:

仍舊是3步:
- 獲取 canvas
- 請求繪圖背景關系
- 呼叫繪圖背景關系中的繪圖函式
在 canvas 繪制矩形之前需要指定顏色(ctx.fillStyle = 'green';),在 webgl 中類似,清慷訓圖區之前也得指定背景色,一旦指定背景色,背景色就會在 webgl 系統中存留,將來還需要使用同樣的顏色清慷訓圖區,,就不需要再次指定背景色,
clearColor 和 clear語法如下:
// WebGLRenderingContext.clearColor() 方法用于設定清空顏色緩沖時的顏色值,指定呼叫 clear() 方法時使用的顏色值
void gl.clearColor(red, green, blue, alpha)
// WebGLRenderingContext.clear() 方法使用預設值來清慷訓沖,
void gl.clear(mask);
mask
gl.COLOR_BUFFER_BIT // 顏色緩沖區
gl.DEPTH_BUFFER_BIT // 深度緩沖區 - 三維世界中使用
gl.STENCIL_BUFFER_BIT // 模板緩沖區 - 很少使用
如果沒有指定背景色,默認值如下:
- 顏色緩沖區 -
(0.0, 0.0, 0.0, 0.0) - 深度緩沖區 - 1.0
繪制一個點
需求
需求:在 canvas 中心畫一個 10px 紅色的點,
效果如下:

思路
用 canvas 繪制一個矩形很簡單,先指定顏色,在繪制矩形,就像這樣:
// canvas繪制矩形
ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 100, 100);
但 webgl 需要使用著色器,著色器提供了靈活且強大的繪制二維或三維的方法,也更加復雜,
我們先看代碼,有一個具體的感受后,在分析其中細節,
代碼
共3個檔案,重點關注 point01.js 即可,
- 新建入口檔案 point01.html:
<!-- point01.html -->
<script src="https://www.cnblogs.com/pengjiali/archive/2023/02/27/cuon-utils.js"></script>
<script src="https://www.cnblogs.com/pengjiali/archive/2023/02/27/point01.js"></script>
<body onl oad="main()">
<canvas id="webgl" height="300"> 抱歉,您的瀏覽器不支持 canvas 元素</canvas>
</body>
Tip:以上這段代碼在 chrome 中運行通過,瀏覽器會自動補全格式,例如把 script 標簽放入 head 中,
- 新建 point01.js:
// point01.js
// 頂點著色器
const VSHADER_SOURCE = `
void main() {
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 10.0;
}
`
// 片元著色器
const FSHADER_SOURCE = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
// 初始化著色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to intialize shaders.');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 1);
}
檔案加載后運行 main() 方法,相對第一個 webgl 示例,這里增加了初始化著色器,
Tip:現在只需要把初始化著色器的方法(initShaders() - 請看本篇 cuon-utils.js 章節)作為一個庫中的輔助方法看待,后續文章將介紹其中原理,
- 新建 cuon-utils.js(內容見本篇
擴展),主要提供初始化著色器的方法
代碼決議
總體流程
檔案加載后執行 main() 方法,有如下5個階段:
- 獲取canvas
- 取得 webgl 背景關系
初始化著色器- 清除繪圖區
- 呼叫
drawArrays繪圖
下面我們主要講一下第三步和最后一步,
齊次坐標
齊次坐標就是將一個原本是 n 維的向量用一個 n+1 維向量來表示,齊次坐標能提高處理三維資料的有效率,所以在三維系統中大量使用,齊次坐標(x, y, z, w) 等價于三維坐標 (x/w, y/w, z/w)
頂點著色器
頂點著色器(Vertex Shader) - 用來描述頂點特征的程式,例如這里的位置和大小,頂點指二維(x, y)或三維(x, y, z)空間中的一個點,例如端點或交點,
內置變數:
gl_Position- 用于描述頂點位置,必傳,型別是vec4(即4個float)gl_PointSize- 用戶描述頂點的尺寸(像素),如果不傳,默認 1.0,型別是float
關于位置,我們只有 (x, y, z) 三個變數,但 vec4 是 4 個,所以需要使用內置函式 vec4() 幫忙創建 vec4 型別的變數,
代碼中 vec4(0.0, 0.0, 0.0, 1.0),這里第四個分量是 1.0,使用的是齊次坐標,
Tip:先記著 (0.0, 0.0, 0.0) 就是繪圖區的中心,本篇 坐標系統 中會詳細講解,
片元著色器
片元著色器(Fragment Shader) - 進行逐片元處理程序如光照的程式,片元是 webgl 的一個術語,暫時可以將其理解成像素,
內置變數:
gl_FragColor- 指定片元顏色(RGBA格式),型別是 vec4
初始化著色器
webgl 需要兩種著色器:頂點著色器(Vertex Shader)、片元著色器(Fragment Shader),
在三維場景中,僅僅用線條和顏色把圖畫出來不夠,還需要考慮光照上去或者觀察者的視角發生變化,對場景有什么影響,著色器可以靈活的完成這些作業,
初始化著色器之前,頂點著色器和片元著色器都是空白,把著色器程式作為字串形式傳給 initShaders(initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE))之后,webgl 系統中的著色器就建立好,
下圖是執行 initShaders() 前后的情形:

Tip: 先執行頂點著色器,然后把 gl_Position 和 gl_PointSize 傳給片元著色器,實際上片元著色器接收到的是經過柵格化處理后的片元(柵格化在畫三角形時在講解),
繪圖
建立著色器之后,首先清慷訓圖區域,然后使用 gl.drawArrays() 進行繪制,
gl.drawArrays(mode, first, count) 執行頂點著色器,按照 mode 指定的引數繪制圖形,first 指定從哪個點開始繪制,count 指繪制需要幾個點,
Tip:mode 型別有:
- gl.POINTS: 繪制一系列點,
- gl.LINE_STRIP: 繪制一個線條,即,繪制一系列線段,上一點連接下一點,
- gl.LINE_LOOP: 繪制一個線圈,即,繪制一系列線段,上一點連接下一點,并且最后一點與第一個點相連,
- gl.LINES: 繪制一系列單獨線段,每兩個點作為端點,線段之間不連接,
- gl.TRIANGLE_STRIP:繪制一個三角帶,
- gl.TRIANGLE_FAN:繪制一個三角扇,
- gl.TRIANGLES: 繪制一系列三角形,每三個點作為頂點,
例如我們這里是:gl.drawArrays(gl.POINTS, 0, 1),繪制圖形(點),需要一個點,從第一個點開始繪制,后續畫多個點時會對 first 和 count 有更清晰的理解,
代碼注釋
// point01.js
// 頂點著色器
const VSHADER_SOURCE = `
// 和 C 語言一樣,必須包含一個 main() 函式,void 表示沒有回傳值
// 注:不能給 main() 指定引數
void main() {
// 頂點著色器內置變數: gl_Position 頂點位置、gl_PointSize 頂點尺寸
gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
gl_PointSize = 10.0;
}
`
// 片元著色器
const FSHADER_SOURCE = `
void main() {
// 片元著色器內置變數: gl_FragColor 指定片元顏色
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main() {
const canvas = document.getElementById('webgl');
const gl = canvas.getContext("webgl");
// 初始化著色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('初始化著色器失敗');
return;
}
// 清慷訓圖區
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪制圖形(點),需要一個點,從第一個點開始繪制
gl.drawArrays(gl.POINTS, 0, 1);
}
擴展
坐標系統
webgl 的坐標系(x, y, z)和 canvas 的坐標系(x, y)不同,
canvas 的原點(0, 0)在左上角,webgl 處理的是三維,所以使用三維坐標系統(笛卡爾坐標系),可用(x, y, z) 表示,也可認為是右手坐標系,請看下圖:

webgl 坐標和 canvas 坐標對應關系如下(可對照上面中間那張圖):

cuon-utils.js
// cuon-utils.js (c) 2012 kanda and matsuda
/**
* Create a program object and make current
* @param gl GL context
* @param vshader a vertex shader program (string)
* @param fshader a fragment shader program (string)
* @return true, if the program object was created and successfully made current
*/
function initShaders(gl, vshader, fshader) {
var program = createProgram(gl, vshader, fshader);
if (!program) {
console.log('Failed to create program');
return false;
}
gl.useProgram(program);
gl.program = program;
return true;
}
/**
* Create the linked program object
* @param gl GL context
* @param vshader a vertex shader program (string)
* @param fshader a fragment shader program (string)
* @return created program object, or null if the creation has failed
*/
function createProgram(gl, vshader, fshader) {
// Create shader object
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
if (!vertexShader || !fragmentShader) {
return null;
}
// Create a program object
var program = gl.createProgram();
if (!program) {
return null;
}
// Attach the shader objects
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// Link the program object
gl.linkProgram(program);
// Check the result of linking
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
var error = gl.getProgramInfoLog(program);
console.log('Failed to link program: ' + error);
gl.deleteProgram(program);
gl.deleteShader(fragmentShader);
gl.deleteShader(vertexShader);
return null;
}
return program;
}
/**
* Create a shader object
* @param gl GL context
* @param type the type of the shader object to be created
* @param source shader program (string)
* @return created shader object, or null if the creation has failed.
*/
function loadShader(gl, type, source) {
// Create shader object
var shader = gl.createShader(type);
if (shader == null) {
console.log('unable to create shader');
return null;
}
// Set the shader program
gl.shaderSource(shader, source);
// Compile the shader
gl.compileShader(shader);
// Check the result of compilation
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var error = gl.getShaderInfoLog(shader);
console.log('Failed to compile shader: ' + error);
gl.deleteShader(shader);
return null;
}
return shader;
}
出處:https://www.cnblogs.com/pengjiali/p/17156241.html
本文著作權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連接,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/545245.html
標籤:其他
下一篇:nestJs中使用typeORM報'QueryFailedError: Table 'equtype' already exists'錯誤
