WebGL - 绘制模型:初学者指南

你好,未来的 WebGL 巫师们!我很高兴能成为你们在这个激动人心的 3D 图形编程世界中的向导。作为一名多年来一直教授计算机科学的人,我可以告诉你,在 WebGL 中绘制模型就像用数字乐高积木建造东西一样——既具挑战性,又极其有成就感!让我们跳进去,在屏幕上创造一些魔法吧。

WebGL - Drawing a Model

理解基础

在我们开始绘制模型之前,让我们快速回顾一下 WebGL 是什么。WebGL(Web 图形库)是一个 JavaScript API,它允许我们在不使用任何插件的情况下,在网页浏览器中渲染 3D 图形。这就像是给你的网页赋予了超能力!

现在,当我们谈论在 WebGL 中绘制模型时,我们有两种主要方法可以使用:

绘制方法

方法 描述 用例
drawArrays() 使用数组数据绘制基本图形 简单形状,非索引几何体
drawElements() 绘制索引基本图形 复杂模型,优化渲染

让我们详细探索这些方法。

drawArrays() 方法:简化绘制

drawArrays() 是什么?

drawArrays() 方法就像是用铅笔素描——直接且非常适合简单形状。这个方法使用顶点数据数组来绘制几何基本体。

语法和参数

gl.drawArrays(mode, first, count);
  • mode:要绘制的图形类型(例如,gl.TRIANGLES, gl.LINES)
  • first:数组中的起始索引
  • count:要绘制的顶点数

示例:绘制三角形

让我们使用 drawArrays() 方法绘制一个简单的三角形:

// 三角形的顶点数据
const vertices = [
0.0,  0.5,  0.0,  // 顶点
-0.5, -0.5,  0.0,  // 底部左顶点
0.5, -0.5,  0.0   // 底部右顶点
];

// 创建并绑定缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

// 设置属性指针
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

// 绘制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

在这个示例中,我们定义了三角形的三个顶点,创建一个缓冲区来存储这些数据,设置一个属性指针告诉 WebGL 如何读取我们的顶点数据,最后调用 drawArrays() 来渲染我们的三角形。

drawElements() 方法:构建复杂模型

drawElements() 是什么?

如果 drawArrays() 像是用铅笔素描,那么 drawElements() 就像是用画笔绘画——它在你绘制复杂形状时给你更多的控制和效率。这个方法使用索引渲染,允许我们重用顶点数据。

语法和参数

gl.drawElements(mode, count, type, offset);
  • mode:要绘制的图形类型
  • count:要绘制的元素数
  • type:元素数组缓冲区中的值类型
  • offset:元素数组缓冲区的偏移量

示例:绘制正方形

让我们使用 drawElements() 方法绘制一个正方形:

// 正方形的顶点数据
const vertices = [
-0.5,  0.5,  0.0,  // 左上角
0.5,  0.5,  0.0,  // 右上角
0.5, -0.5,  0.0,  // 右下角
-0.5, -0.5,  0.0   // 左下角
];

// 索引数据
const indices = [
0, 1, 2,  // 第一个三角形
0, 2, 3   // 第二个三角形
];

// 创建并绑定顶点缓冲区
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

// 创建并绑定索引缓冲区
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

// 设置属性指针
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

// 绘制正方形
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);

在这个示例中,我们定义了正方形的四个顶点,并使用索引来指定这些顶点应该如何连接以形成两个三角形。我们创建了一个顶点缓冲区和索引缓冲区,设置了属性指针,然后调用 drawElements() 来渲染我们的正方形。

必要操作:成功的关键

要在 WebGL 中成功绘制一个模型,我们需要遵循几个关键步骤。把它想象成制作美味 3D 图形蛋糕的食谱!

WebGL 绘制清单

  1. 创建缓冲区:设置顶点和索引缓冲区以存储模型数据。
  2. 编译着色器:编写并编译顶点和片段着色器。
  3. 创建程序:将编译好的着色器链接到程序中。
  4. 设置属性指针:告诉 WebGL 如何解释顶点数据。
  5. 设置统一变量:将任何必要的统一值传递给着色器。
  6. 绑定缓冲区:激活你想要用于绘制的缓冲区。
  7. 绘制:调用 drawArrays()drawElements() 来渲染你的模型。

让我们在更完整的示例中将它们全部放在一起:

// 顶点着色器
const vsSource = `
attribute vec4 a_position;
void main() {
gl_Position = a_position;
}
`;

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

// 编译着色器函数
function compileShader(gl, source, type) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
return shader;
}

// 创建程序函数
function createProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
return program;
}

// 主绘制函数
function drawModel(gl) {
// 编译着色器
const vertexShader = compileShader(gl, vsSource, gl.VERTEX_SHADER);
const fragmentShader = compileShader(gl, fsSource, gl.FRAGMENT_SHADER);

// 创建程序
const program = createProgram(gl, vertexShader, fragmentShader);
gl.useProgram(program);

// 创建缓冲区并设置数据(如前所述)
// ...

// 设置属性指针
const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttributeLocation);
gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);

// 绘制
gl.drawArrays(gl.TRIANGLES, 0, 3);  // 或者对于索引几何体使用 gl.drawElements()
}

这个示例将所有绘制模型所需的操作集中在一起。我们编译着色器,创建程序,设置缓冲区和属性指针,最后绘制我们的模型。

结论

恭喜你!你已经迈出了进入 WebGL 惊奇世界的第一步。记住,像学习任何新技能一样,掌握 WebGL 需要练习和耐心。如果一开始事情没有完美进行——即使是最有经验的图形程序员有时也会意外绘制出粉红色的立方体而不是雄伟的龙!

继续实验,继续学习,最重要的是,享受这个过程。在你意识到之前,你将能够在网页浏览器中创建令人惊叹的 3D 世界。祝编码愉快,未来的 3D 艺术家们!

Credits: Image by storyset