WebGL:3D 圖形在瀏覽器中的入門指南

你好,未來的 3D 圖形魔法師!我很興奮能成為你進入 WebGL 世界的引路人。作為一個教學計算機圖形多年的老師,我可以告訴你,WebGL 就像是一根魔杖,為你的瀏覽器帶來魔法。它讓你能在網頁中創建令人驚艷的 3D 圖形和動畫。這不是很酷嗎?我們一起來探索吧!

WebGL - Home

什麼是 WebGL?

WebGL,全名為 Web 圖形庫,是一個 JavaScript API,讓你能在任何兼容的瀏覽器中無需插件即可渲染互動的 2D 和 3D 圖形。這就像是給你的瀏覽器賦予超能力,創造出令人驚奇的視覺體驗!

簡短歷史

WebGL 首次在 2011 年推出,從那時起,它改變了我們對網頁上圖形的看法。在 WebGL 出現之前,如果你想在瀏覽器中創建 3D 圖形,你需要依賴於如 Flash 或 Java 小程序之類的插件。現在,有了 WebGL,我們可以直接在瀏覽器中原生地做這些事情。這就像我們從自行車升級到了跑車!

預備知識

在我們開始 WebGL 冒險之前,讓我們確保我們背包裡有正確的工具:

  1. 一個現代的瀏覽器(Chrome、Firefox、Safari 或 Edge)
  2. 一個文本編輯器(我推薦 Visual Studio Code,但任何編輯器都可以)
  3. HTML 和 JavaScript 的基本知識(如果你忘記了,別擔心,我們會邊學邊進行)

開始使用 WebGL

讓我們創建我們的第一個 WebGL 程式!我們將從一個簡單的 "Hello, WebGL!" 示例開始,它在螢幕上顯示一個有顏色的三角形。

步驟 1:設置 HTML

首先,我們需要創建一個 HTML 檔案,並在其中包含一個 canvas 元素。這個 canvas 將是我們 WebGL 魔法發生的地方。

<!DOCTYPE html>
<html lang="zh-tw">
<head>
<meta charset="UTF-8">
<title>Hello, WebGL!</title>
<style>
canvas { border: 1px solid black; }
</style>
</head>
<body>
<canvas id="glCanvas" width="640" height="480"></canvas>
<script src="webgl-demo.js"></script>
</body>
</html>

在這個 HTML 中,我們創建了一個 ID 為 "glCanvas" 的 canvas 元素,並將其尺寸設為 640x480 像素。我們還鏈接了一個名為 "webgl-demo.js" 的 JavaScript 檔案,我們將在其中寫入 WebGL 代碼。

步驟 2:初始化 WebGL

現在,讓我們創建 "webgl-demo.js" 檔案並開始寫入一些 JavaScript 代碼以初始化 WebGL:

function main() {
const canvas = document.getElementById("glCanvas");
const gl = canvas.getContext("webgl");

if (!gl) {
alert("無法初始化 WebGL。您的瀏覽器或電腦可能不支持它。");
return;
}

// 設置清空顏色為黑色,完全不透明
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 使用指定的清空顏色清空顏色緩存
gl.clear(gl.COLOR_BUFFER_BIT);
}

window.onload = main;

讓我們分解這段代碼:

  1. 我們獲取 canvas 元素的引用。
  2. 我們嘗試從 canvas 獲取 WebGL 緩存。如果失敗,表示 WebGL 不被支持,我們顯示一個錯誤訊息。
  3. 如果成功,我們設置背景色為黑色並清空畫布。

步驟 3:創建 Shaders

Shaders 是運行在 GPU 上的特殊程序。它們使用一種叫做 GLSL(OpenGL 著色語言)的語言編寫。我們需要兩種類型的 shaders:頂點著色器和片段著色器。

// 頂點著色器程序
const vsSource = `
attribute vec4 aVertexPosition;

void main() {
gl_Position = aVertexPosition;
}
`;

// 片段著色器程序
const fsSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

頂點著色器負責定位我們的頂點,而片段著色器則負責給我們的像素上色(在這個例子中是紅色)。

步驟 4:初始化著色器程序

現在,我們需要編譯並鏈接這些著色器到一個著色器程序中:

function initShaderProgram(gl, vsSource, fsSource) {
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);

const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);

if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert('無法初始化著色器程序:' + gl.getProgramInfoLog(shaderProgram));
return null;
}

return shaderProgram;
}

function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('編譯著色器時發生錯誤:' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}

return shader;
}

這段代碼編譯我們的著色器,將它們鏈接到一個程序中,並檢查是否有任何錯誤。

步驟 5:創建三角形

現在,讓我們創建三角形的數據:

function initBuffers(gl) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

const positions = [
-1.0,  1.0,
1.0,  1.0,
-1.0, -1.0,
];

gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

return {
position: positionBuffer,
};
}

這創建了一個緩存並用三角形的頂點位置填充它。

步驟 6:繪製場景

最後,讓我們把所有東西放在一起並繪製我們的三角形:

function drawScene(gl, programInfo, buffers) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

gl.useProgram(programInfo.program);

gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
2,        // 每次迭代 2 個分量
gl.FLOAT, // 數據是 32 位浮點數
false,    // 不標準化
0,        // 跨度(0 = 自動)
0         // 缓存中的偏移量
);

gl.drawArrays(gl.TRIANGLES, 0, 3);
}

這個函數清空畫布,設置著色器程序,連接緩存數據,並最終繪製三角形。

把所有東西放在一起

現在,讓我們更新我們的 main 函數來使用所有這些部分:

function main() {
const canvas = document.getElementById("glCanvas");
const gl = canvas.getContext("webgl");

if (!gl) {
alert("無法初始化 WebGL。您的瀏覽器或電腦可能不支持它。");
return;
}

const shaderProgram = initShaderProgram(gl, vsSource, fsSource);

const programInfo = {
program: shaderProgram,
attribLocations: {
vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
},
};

const buffers = initBuffers(gl);

drawScene(gl, programInfo, buffers);
}

window.onload = main;

這就是你的第一個 WebGL 程式。當你在瀏覽器中打開 HTML 檔案時,你應該會在黑色背景上看到一個紅色三角形。恭喜你!

常見 WebGL 方法

這裡是我們使用過的一些常見 WebGL 方法和它們的用途:

方法 用途
gl.createBuffer() 創建一個新的緩存對象
gl.bindBuffer() 將緩存對象綁定到一個目標
gl.bufferData() 初始化並創建緩存對象的數據存儲
gl.createShader() 創建一個著色器對象
gl.shaderSource() 設置著色器對象的源代碼
gl.compileShader() 編譯著色器對象
gl.createProgram() 創建一個程序對象
gl.attachShader() 將著色器對象附加到程序對象
gl.linkProgram() 鏈接程序對象
gl.useProgram() 將指定的程序設置為當前的渲染狀態的一部分
gl.getAttribLocation() 返回屬性變量的位置
gl.enableVertexAttribArray() 啟用頂點屬性數組
gl.vertexAttribPointer() 指定頂點屬性數據的佈局
gl.drawArrays() 從數組數據渲染原始形状

結論

哇,我們今天學習了許多內容!我們學習了 WebGL 是什麼,設置了我們的開發環境,並創建了我們的第一個 WebGL 程式。記住,學習 WebGL就像學習騎自行車一樣 - 起初可能會有些晃動,但隨著練習,你會很快馳騁!

在未來的課程中,我們將探索更複雜的形狀,添加互動性,甚至深入 3D 圖形。WebGL 的世界浩瀚而令人興奮,我迫不及待地想和你一起探索更多。下次見,快樂編程!

Credits: Image by storyset