WebGL - 颜色:3D 圖形添彩新手指南
你好,有志於 WebGL 的同好們!我很高興能夠帶領你們踏上這次充滿色彩之旅。作為一個教導電腦圖形超過十年的老師,我可以告訴你們,為你的 3D 場景添加顏色就像為黑白照片注入生命一樣。這很神奇,今天,我們將一起解鎖這份魔力!
理解 WebGL 中的顏色
在我們深入應用顏色之前,讓我們花一會兒時間來理解在 WebGL 的背景下顏色意味著什麼。在這個數字領域中,顏色是使用 RGB(紅、綠、藍)顏色模型來表示的。每種顏色都是這三種主要顏色的組合,其值範圍從 0.0 到 1.0。
例如:
- 紅色:
(1.0, 0.0, 0.0)
- 綠色:
(0.0, 1.0, 0.0)
- 藍色:
(0.0, 0.0, 1.0)
- 白色:
(1.0, 1.0, 1.0)
- 黑色:
(0.0, 0.0, 0.0)
把它想成像調配油畫一樣,但用的是光而不是顏料。這就像是一位數位藝術家,手握著無限的調色盤!
在 WebGL 中應用顏色
現在我們已經理解了基本知識,讓我們動手(或者說,讓我們調色?)在 WebGL 中應用顏色。
應用顏色的步驟
- 在你的頂點著色器中定義顏色屬性
- 從你的 JavaScript 代碼中傳遞顏色數據
- 在你的片段著色器中使用顏色
讓我們通過一些代碼示例來分解這些步驟。
步驟 1:在頂點著色器中定義顏色屬性
首先,我們需要告訴我們的頂點著色器我們將會使用顏色。以下是如何操作的:
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}
在這段代碼中,我們定義了一個屬性 a_Color
來接收顏色數據,以及一個變量 v_Color
來將顏色傳遞給片段著色器。這就像是我們從 JavaScript 代碼到像素之間建立了一個顏色管道!
步驟 2:從 JavaScript 傳遞顏色數據
現在,我們需要把我們的顏色數據從 JavaScript 代碼傳遞到著色器。以下是一個如何做到這一點的例子:
// 定義頂點和顏色
var vertices = new Float32Array([
0.0, 0.5, 1.0, 0.0, 0.0, // 頂點 1: x, y, r, g, b
-0.5,-0.5, 0.0, 1.0, 0.0, // 頂點 2: x, y, r, g, b
0.5,-0.5, 0.0, 0.0, 1.0 // 頂點 3: x, y, r, g, b
]);
// 創建緩存並將數據發送到它
var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// 告訴 WebGL 如何讀取緩存
var FSIZE = vertices.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0);
gl.enableVertexAttribArray(a_Position);
var a_Color = gl.getAttribLocation(program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2);
gl.enableVertexAttribArray(a_Color);
這段代碼可能一開始看起來令人却步,但讓我們來分解它:
- 我們在單一數組中定義我們的頂點和顏色。每個頂點有 5 個值:x, y, r, g, b。
- 我們創建一個緩存並將我們的數據發送到它。
- 我們告訴 WebGL 如何讀取這個緩存,對於位置(前兩個值)和顏色(最後三個值)。
這就像打包衣服和洗漱用品,然後告訴你的朋友如何拆包一樣!
步驟 3:在片段著色器中使用顏色
最後,我們在片段著色器中使用顏色:
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
這個簡單的著色器將我們從頂點著色器傳遞過來的顏色應用到我們的片段(像素)上。這是我們顏色旅程的最後一步,顏色終於在屏幕上閃耀!
示例 - 應用顏色
讓我們把所有東西放在一起,用一個完整的例子。我們將創建一個彩色的三角形,使用我們討論過的代碼。
<!DOCTYPE html>
<html>
<head>
<title>彩色 WebGL 三角形</title>
</head>
<body>
<canvas id="glCanvas" width="640" height="480"></canvas>
<script>
// Vertex shader program
const vsSource = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}
`;
// Fragment shader program
const fsSource = `
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
`;
function main() {
const canvas = document.querySelector("#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, 'a_Position'),
vertexColor: gl.getAttribLocation(shaderProgram, 'a_Color'),
},
};
// 創建緩存
const buffers = initBuffers(gl);
// 繪製場景
drawScene(gl, programInfo, buffers);
}
function initBuffers(gl) {
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
0.0, 0.5, 1.0, 0.0, 0.0,
-0.5, -0.5, 0.0, 1.0, 0.0,
0.5, -0.5, 0.0, 0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
return {
position: positionBuffer,
};
}
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);
{
const numComponents = 2; // 每次迭代拉出 2 個值
const type = gl.FLOAT; // 缓存中的數據是 32 位浮點數
const normalize = false; // 不要標準化
const stride = 20; // 從一組值到下一組值的字節數
const offset = 0; // 缓存內從何處開始
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexPosition);
}
{
const numComponents = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = 20;
const offset = 8;
gl.vertexAttribPointer(
programInfo.attribLocations.vertexColor,
numComponents,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(
programInfo.attribLocations.vertexColor);
}
gl.drawArrays(gl.TRIANGLES, 0, 3);
}
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;
}
window.onload = main;
</script>
</body>
</html>
當你運行這段代碼時,你會看到一個有紅色、綠色和藍色頂點的漂亮三角形。就像看著彩虹在屏幕上栩栩如生!
結論
現在,我們已經一起走過了 WebGL 的彩色世界,從理解顏色是如何表示的到將它們應用到我們的 3D 圖形。記住,這只是個開始。當你掌握了這些基本知識,你就踏上了創造驚艷、生動的 3D 圖形的道路。
當我們結束這個話題時,我會想起一個學生曾經告訴我,學習 WebGL 顏色就像學習用光來繪畫。她完全說對了。所以,我的親愛的學生們,去 forth 吧,用風的顏色來繪製你的數位畫布(是的,那是迪士尼的引用,我不會為此感到羞愧)!
快樂編程,願你的 WebGL 冒險永遠色彩繽紛!
注意:Markdown 不支持 HTML 的 `<script>` 标签,因此 JavaScript 代码块在 Markdown 中只是文本,并不会执行。上面的代码块是为了展示目的。
Credits: Image by storyset