Создание 3D-объектов с помощью WebGL
Давайте превратим наш квадрат в трёхмерный куб, добавив ещё пять граней. Для этого мы переключимся с рисования вершин напрямую, вызывая метод gl.drawArrays(), на использование массива вершин как таблицы и обращение к отдельным вершинам в этой таблице для определения позиций вершин каждой грани, вызывая gl.drawElements().
Подумайте: каждая грань требует четырёх вершин для определения, но каждая вершина используется тремя гранями. Мы можем передать гораздо меньше данных, создав массив из всех 24 вершин, а затем ссылаясь на каждую вершину по её индексу в этом массиве вместо перемещения полных наборов координат. Если вы задаётесь вопросом, почему нам нужны 24 вершины, а не 8, то это потому, что каждый угол принадлежит трём граням разных цветов, и отдельная вершина должна иметь единственный конкретный цвет; поэтому мы создадим три копии каждой вершины в трёх разных цветах, по одной для каждой грани.
Определение позиций вершин куба
Сначала давайте создадим буфер позиций вершин куба, обновив код в initBuffers(). Это практически то же самое, что и для квадратной плоскости, но немного длиннее, так как вершин 24 (по 4 на сторону).
Примечание: В функции initPositionBuffer() вашего модуля "init-buffers.js" замените объявление positions этим кодом:
const positions = [ // Front face -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, // Back face -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, // Top face -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, // Bottom face -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // Right face 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, // Left face -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, ];
Поскольку мы добавили z-компоненту в наши вершины, нам нужно обновить numComponents нашего атрибута vertexPosition до 3.
Примечание: В функции setPositionAttribute() вашего модуля "draw-scene.js" измените константу numComponents с 2 на 3:
const numComponents = 3;
Определение цветов вершин
Нам также нужно создать массив цветов для каждой из 24 вершин. Этот код начинается с определения цвета для каждой грани, а затем использует цикл для сборки массива всех цветов для каждой из вершин.
Примечание: В функции initColorBuffer() вашего модуля "init-buffers.js" замените объявление colors этим кодом:
const faceColors = [
[1.0, 1.0, 1.0, 1.0], // Front face: white
[1.0, 0.0, 0.0, 1.0], // Back face: red
[0.0, 1.0, 0.0, 1.0], // Top face: green
[0.0, 0.0, 1.0, 1.0], // Bottom face: blue
[1.0, 1.0, 0.0, 1.0], // Right face: yellow
[1.0, 0.0, 1.0, 1.0], // Left face: purple
];
// Convert the array of colors into a table for all the vertices.
var colors = [];
for (var j = 0; j < faceColors.length; ++j) {
const c = faceColors[j];
// Repeat each color four times for the four vertices of the face
colors = colors.concat(c, c, c, c);
}
Определение массива элементов
После генерации массивов вершин нам нужно создать массив элементов.
Примечание: В вашем модуле "init-buffer.js" добавьте следующую функцию:
function initIndexBuffer(gl) {
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
// This array defines each face as two triangles, using the
// indices into the vertex array to specify each triangle's
// position.
const indices = [
0,
1,
2,
0,
2,
3, // front
4,
5,
6,
4,
6,
7, // back
8,
9,
10,
8,
10,
11, // top
12,
13,
14,
12,
14,
15, // bottom
16,
17,
18,
16,
18,
19, // right
20,
21,
22,
20,
22,
23, // left
];
// Now send the element array to GL
gl.bufferData(
gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array(indices),
gl.STATIC_DRAW,
);
return indexBuffer;
}
Массив indices определяет каждую грань как пару треугольников, указывая вершины каждого треугольника как индексы в массивах вершин куба. Таким образом, куб описывается как набор из 12 треугольников.
Затем вам нужно вызвать эту новую функцию из initBuffers() и вернуть созданный буфер.
Примечание: В конце функции initBuffers() вашего модуля "init-buffers.js" добавьте следующий код, заменив существующее объявление return:
const indexBuffer = initIndexBuffer(gl);
return {
position: positionBuffer,
color: colorBuffer,
indices: indexBuffer,
};
Рисование куба
Далее нам нужно добавить код в функцию drawScene() для рисования с использованием буфера индексов куба, добавив новые вызовы gl.bindBuffer() и gl.drawElements().
Примечание: В вашей функции drawScene() добавьте следующий код непосредственно перед строкой gl.useProgram:
// Tell WebGL which indices to use to index the vertices gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
Примечание: В функции drawScene() вашего модуля "draw-scene.js" замените блок непосредственно после двух вызовов gl.uniformMatrix4fv, содержащий строку gl.drawArrays(), следующим блоком:
{
const vertexCount = 36;
const type = gl.UNSIGNED_SHORT;
const offset = 0;
gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
}
Поскольку каждая грань нашего куба состоит из двух треугольников, на каждой грани по 6 вершин, или всего 36 вершин в кубе, даже несмотря на то, что многие из них являются дубликатами.
Наконец, давайте заменим нашу переменную squareRotation на cubeRotation и добавим второе вращение вокруг оси x.
Примечание: В начале вашего файла "webgl-demo.js" замените объявление squareRotation этой строкой:
let cubeRotation = 0.0;
Примечание: В объявлении вашей функции drawScene() замените squareRotation на cubeRotation:
function drawScene(gl, programInfo, buffers, cubeRotation) {
Примечание: В вашей функции drawScene() замените вызов mat4.rotate следующим кодом:
mat4.rotate( modelViewMatrix, // destination matrix modelViewMatrix, // matrix to rotate cubeRotation, // amount to rotate in radians [0, 0, 1], ); // axis to rotate around (Z) mat4.rotate( modelViewMatrix, // destination matrix modelViewMatrix, // matrix to rotate cubeRotation * 0.7, // amount to rotate in radians [0, 1, 0], ); // axis to rotate around (Y) mat4.rotate( modelViewMatrix, // destination matrix modelViewMatrix, // matrix to rotate cubeRotation * 0.3, // amount to rotate in radians [1, 0, 0], ); // axis to rotate around (X)
Примечание: В вашей функции main() замените код, вызывающий drawScene() и обновляющий squareRotation, на передачу и обновление cubeRotation:
drawScene(gl, programInfo, buffers, cubeRotation); cubeRotation += deltaTime;
На этом этапе у нас теперь есть анимированный куб, вращающийся, его шесть граней ярко окрашены.
Просмотреть весь код | Открыть эту демонстрацию в новой вкладке
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Creating_3D_objects_using_WebGL