終于要進入到webgl的世界了。還真是有點小激動。在之前的文章里,跟大家詳細分享了下 canvas 的相關(guān)內(nèi)容。下面會有這幾篇文章的鏈接,有興趣的小伙伴可以自行查閱。
準備好了嗎?接下來讓我們開啟奇幻旅程,進入 3D 的世界。
1. 認識3D
首先我們要介紹的是幾個概念,這是我們要進入到 3D 不可或缺的內(nèi)容。認識一下它們吧。
1.1 視點,視線,目標點,上方向
這幾個概念在webgl中屬于最常見的內(nèi)容。
- 視點:可以簡易的理解為眼睛,也叫觀察點
- 目標點:可以理解為我們要看的物體(任何物體)
- 上方向:頭頂?shù)姆较颉?/strong>
實際生活中,我們的目光總是以我們的眼睛為起始點,到達我們想要看到的物體,同時,我們觀察的角度不同,物體也會呈現(xiàn)不一樣的形態(tài)。以一張圖說明吧。

如此幾個內(nèi)容,創(chuàng)建出了3D世界的基本顯示模型,由此可見其重要程度。后面我們也會說到如何在 webgl 中設(shè)定這幾個內(nèi)容。也會有的小伙伴把視點稱為相機,目標點稱為畫布。其實是一樣的道理。按照自己的理解記憶就好。
1.2 可視范圍
可視范圍指的是我們所能看到的最大范圍。如:一般情況下我們看不到自己身后的事物。
眾所周知,三維物體具有深度的概念。在我們的理解中,深度就是 z 軸。
雖然我們可以將物體放置在三維空間中的任何位置,但是在webgl中,可視范圍之外的物體是不被繪制的,這也是為了節(jié)省開銷。
1.3 可視空間
水平視角、垂直視角、可視深度 定義了可視空間的概念。
可視空間分為兩種。
- 正射投影:與物體的遠近無關(guān),通常用在建筑設(shè)計和建模上。

- 透視投影:我們平時觀察的真實世界都是透視投影。更有深度的感覺。

1.3 著色器
如果想渲染 3d 圖形,就需要經(jīng)過一系列的步驟,這些步驟稱為渲染管線。在開發(fā) webgl 程序時,我們就需要通過著色器語言跟GPU進行溝通,用來設(shè)定我們需要渲染和顯示的圖形。
由此可見:著色器是編寫webgl時最重要的一點(沒有之一)。我們之所以能生成并操作3d圖像,都是因為著色器在起作用。webgl中著色器分為兩種。頂點著色器和片元著色器。
1.3.1 頂點著色器
這里的頂點代表的是組成物體的每一個點。
頂點著色器的功能主要是將位置數(shù)據(jù)經(jīng)過矩陣變換、計算光照之后生成頂點顏色、變換紋理坐標。并將生成的數(shù)據(jù)輸出到片元著色器。
1.3.2 片元著色器
片元著色器的作用是將光柵化階段生成的每個片元,計算出每個片元的最終元素。
注:
由于著色器內(nèi)容比較重要,這里我們先引入這兩個概念,先簡單理解就可以,后面專門對著色器進行分享。
2. 繪制圖形
2.1 獲取繪圖上下文
了解了第一小節(jié)的內(nèi)容之后,我們開始進入到webgl開發(fā)實戰(zhàn)中。
還記得canvas中第一步需要干什么嗎?
沒錯,需要獲取 canvas 元素和繪圖上下文。webgl 開發(fā)也不例外,也需要首先獲取元素和繪圖上下文。形如下方代碼所示:
// <canvas id="canvas"></canvas> canvas的dom結(jié)構(gòu)
// 獲取canvas元素
const ctx = document.getElementById('canvas')
// 獲取繪圖上下文
const gl = ctx.getContext('webgl')
2.2 初始化著色器
1. 編寫著色器代碼
獲取到繪圖上下文之后,我們需要初始化webgl 的著色器了,著色器代碼是以字符串的形式嵌入到渲染程序中,所以我們需要編寫兩個著色器的字符串。
// 頂點著色器
const VERTEX_SHADER = '' +
'void main(){' +
' gl_Position = vec4(0.0,0.0,0.0,1.0);' +
' gl_PointSize = 15.0;' +
'}' +
''
// 片元著色器
const FRAGMENT_SHADER = '' +
'void main() {' +
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}' +
''
兩個著色器代碼都是以字符串的形式存在,并在執(zhí)行渲染時嵌入到渲染流程內(nèi)。
說明:
-
void main() {}: 創(chuàng)建一個主函數(shù)。 -
gl_Position: 指定繪制的坐標,接收一個擁有4個浮點分量的vec4數(shù)據(jù)。分別代表x,y,z,w數(shù)據(jù) -
gl_PointSize: 表示要繪制圖形的尺寸大小。 -
gl_FragColor: 定義圖形顏色,1.0 0.0 0.0 1.0分別代表r g b a
2. 創(chuàng)建著色器
當然,只是編寫完著色器代碼依然不能完成渲染工作,接下來我們就需要將著色器添加到渲染流程內(nèi)
// 首先創(chuàng)建頂點和片元著色器
// 創(chuàng)建頂點著色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
// 創(chuàng)建片元著色器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
3. 著色器編譯
完成上述兩步之后,我們就需要將著色器代碼添加到著色器中。看下例子。
// 將頂點著色器代碼添加到頂點著色器中
gl.shaderSource(vertexShader, VERTEX_SHADER);
// 將片元著色器代碼添加到片元著色器中
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
// 添加完成后,需要編譯著色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
4. 創(chuàng)建 program
完成編譯之后,我們需要將著色器添加到渲染程序中。
const program = gl.createProgram();
// 將著色器添加到程序中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 關(guān)聯(lián)program
gl.linkProgram(program);
// 使用program
gl.useProgram(program);
5. 繪制圖形
完成上述步驟之后,就可以繪制我們的圖形了。這里我們以一個點為例。在畫布上繪制一個點出來。
gl.drawArrays(gl.POINTS, 0, 1);
此時就可以打開頁面,看到我們繪制的這個點了。
總結(jié)代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webgl初章:進入3D世界</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const ctx = document.getElementById('canvas');
const gl = ctx.getContext('webgl');
// 頂點著色器
const VERTEX_SHADER = '' +
'void main(){' +
' gl_Position = vec4(0.0,0.0,0.0,1.0);' +
' gl_PointSize = 15.0;' +
'}' +
''
// 頂點著色器
const FRAGMENT_SHADER = '' +
'void main() {' +
' gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}' +
''
// 創(chuàng)建頂點和片元著色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// 將頂點著色器代碼添加到頂點著色器中
gl.shaderSource(vertexShader, VERTEX_SHADER);
// 將片元著色器代碼添加到片元著色器中
gl.shaderSource(fragmentShader, FRAGMENT_SHADER);
// 添加完成后,需要編譯著色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
const program = gl.createProgram();
// 將著色器添加到程序中
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 關(guān)聯(lián)program
gl.linkProgram(program);
// 使用program
gl.useProgram(program);
// 繪制一個點
gl.drawArrays(gl.POINTS, 0, 1);
</script>
</body>
</html>
由于是初章,內(nèi)容會比較少。今天的內(nèi)容就先分享到這里,Bye~