Spec-Zone.ru › Web APIs

Создание 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API