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

首先,我们需要创建一个包含canvas元素的HTML文件。这个canvas就是我们WebGL魔法发生的地方。

<!DOCTYPE html>
<html lang="zh-CN">
<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步:创建着色器

着色器是运行在GPU上的特殊程序。它们是用一种叫做GLSL(OpenGL着色语言)的语言编写的。我们需要两种类型的着色器:顶点着色器和片段着色器。

// 顶点着色器程序
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