WebGL - 立方体旋转

你好,未来的 WebGL 巫师们!今天,我们将踏上一段激动人心的旅程,进入 3D 图形的世界。在本教程结束时,你将能够使用 WebGL 创建一个旋转的立方体。这难道不酷吗?让我们开始吧!

WebGL - Cube Rotation

理解基础知识

在我们开始像 DJ 一样旋转立方体之前,让我们先了解一下一些基本概念。

什么是 WebGL?

WebGL(Web 图形库)是一个 JavaScript API,它允许我们在网页浏览器中渲染 3D 图形。它就像给你的浏览器戴上了一副 3D 眼镜!

为什么选择立方体?

你可能想知道,“为什么我们从立方体开始?”亲爱的学生们,立方体就像是 3D 图形的“Hello World”。它足够简单,容易理解,但又足够复杂,可以教给我们重要的概念。而且,谁不喜欢一个好的立方体呢?

设置我们的 WebGL 环境

HTML 画布

首先,我们需要一个舞台,让我们的立方体在上面表演。在 WebGL 中,这个舞台被称为画布。让我们来设置它:

<canvas id="glcanvas" width="640" height="480">
你的浏览器不支持 HTML5 画布
</canvas>

这创建了一个 640x480 像素的画布。如果你看不到它,别担心——它现在就像一个看不见的舞池。

初始化 WebGL

现在,让我们让 WebGL 准备好派对:

var canvas = document.getElementById('glcanvas');
var gl = canvas.getContext('webgl');

if (!gl) {
console.log('WebGL 不支持,退回到 experimental-webgl');
gl = canvas.getContext('experimental-webgl');
}

if (!gl) {
alert('你的浏览器不支持 WebGL');
}

这段代码获取我们的 WebGL 上下文。如果你的浏览器不支持 WebGL,就像试图在 VHS 播放器上播放 DVD 一样——它就是不会工作!

创建我们的 3D 立方体

定义顶点

立方体有 8 个角(顶点)。我们需要告诉 WebGL 这些角在哪里:

var vertices = [
// 前面
-1.0, -1.0,  1.0,
1.0, -1.0,  1.0,
1.0,  1.0,  1.0,
-1.0,  1.0,  1.0,

// 后面
-1.0, -1.0, -1.0,
-1.0,  1.0, -1.0,
1.0,  1.0, -1.0,
1.0, -1.0, -1.0,

// 顶面
-1.0,  1.0, -1.0,
-1.0,  1.0,  1.0,
1.0,  1.0,  1.0,
1.0,  1.0, -1.0,

// 底面
-1.0, -1.0, -1.0,
1.0, -1.0, -1.0,
1.0, -1.0,  1.0,
-1.0, -1.0,  1.0,

// 右面
1.0, -1.0, -1.0,
1.0,  1.0, -1.0,
1.0,  1.0,  1.0,
1.0, -1.0,  1.0,

// 左面
-1.0, -1.0, -1.0,
-1.0, -1.0,  1.0,
-1.0,  1.0,  1.0,
-1.0,  1.0, -1.0
];

每组三个数字代表我们立方体在 3D 空间中的一个角。这就像给 WebGL 一个我们立方体的地图!

创建缓冲区

现在我们需要将这些数据发送到 GPU:

var vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

这就好比把我们的立方体打包进一个行李箱(缓冲区)然后发送给 GPU。

着色器 - WebGL 的魔术师

顶点着色器

顶点着色器定位我们的顶点:

var vertexShaderSource = `
attribute vec3 aVertexPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main(void) {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aVertexPosition, 1.0);
}
`;

这个着色器接收每个顶点并应用我们的变换矩阵。就像魔术师移动我们立方体的角落!

片段着色器

片段着色器给我们的立方体上色:

var fragmentShaderSource = `
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
`;

这个简单的着色器将所有东西都染成白色。就像给我们的立方体上了色!

编译和链接着色器

现在我们需要编译我们的着色器并将它们链接成一个程序:

function getShader(gl, source, type) {
var shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);

if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert('编译着色器时发生错误:' + gl.getShaderInfoLog(shader));
return null;
}

return shader;
}

var vertexShader = getShader(gl, vertexShaderSource, gl.VERTEX_SHADER);
var fragmentShader = getShader(gl, fragmentShaderSource, gl.FRAGMENT_SHADER);

var 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));
}

gl.useProgram(shaderProgram);

这就好比教我们的魔术师(着色器)他们的技巧,然后让他们上台表演!

让我们的立方体旋转

设置旋转

要让我们的立方体旋转,我们需要随时间更新它的旋转角度:

var cubeRotation = 0.0;

function render(now) {
now *= 0.001;  // 转换为秒
const deltaTime = now - then;
then = now;

cubeRotation += deltaTime;

drawScene();

requestAnimationFrame(render);
}

这个函数会被反复调用,每次更新我们立方体的旋转。

绘制场景

现在让我们把所有东西放在一起,绘制我们的旋转立方体:

function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);

const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45 * Math.PI / 180, gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0);

const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation, [0, 1, 1]);

gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'), false, projectionMatrix);
gl.uniformMatrix4fv(gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'), false, modelViewMatrix);

gl.drawArrays(gl.TRIANGLE_STRIP, 0, 36);
}

这个函数清除画布,设置我们的视角,应用旋转,最后绘制我们的立方体。

结论

就这样,朋友们!我们使用 WebGL 创建了一个旋转的 3D 立方体。记住,掌握 WebGL 就像学习杂技——它需要练习,但一旦你掌握了它,你就可以做出惊人的事情!

下面是一个总结我们使用的主要方法的表格:

方法 描述
gl.createBuffer() 创建一个新的缓冲区对象
gl.bindBuffer() 将缓冲区对象绑定到目标
gl.bufferData() 创建并初始化缓冲区对象的数据存储
gl.createShader() 创建一个着色器对象
gl.shaderSource() 设置着色器对象的源代码
gl.compileShader() 编译着色器对象
gl.createProgram() 创建一个程序对象
gl.attachShader() 将着色器对象附加到程序对象
gl.linkProgram() 链接程序对象
gl.useProgram() 将指定的程序设置为当前渲染状态的一部分
gl.clear() 清除缓冲区到预设值
gl.uniformMatrix4fv() 为当前程序对象指定统一变量的值
gl.drawArrays() 从数组数据渲染图元

继续练习,继续编码,很快你就能在浏览器中创建惊人的 3D 图形了。快乐编码!

Credits: Image by storyset