WebGL - 颜色:WebGL初学者指南,为您的3D图形添加生命力

你好,有抱负的WebGL爱好者们!我很高兴能在这段五彩斑斓的旅程中担任您的向导。作为一个教授计算机图形学超过十年的人,我可以告诉你,为你的3D场景添加颜色就像给黑白照片赋予生命。这是神奇的,今天,我们将一起解锁那份魔力!

WebGL - Colors

理解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中应用颜色。

应用颜色的步骤

  1. 在顶点着色器中定义颜色属性
  2. 从JavaScript代码中传递颜色数据
  3. 在片段着色器中使用颜色

让我们通过一些代码示例来分解这些步骤。

第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);

这段代码可能一开始看起来令人畏惧,但让我们分解一下:

  1. 我们在一个数组中定义了我们的顶点和颜色。每个顶点有5个值:x, y, r, g, b。
  2. 我们创建了一个缓冲区并将我们的数据发送到其中。
  3. 我们告诉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>
// 顶点着色器程序
const vsSource = `
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}
`;

// 片段着色器程序
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冒险之旅永远色彩斑斓!

Credits: Image by storyset