WebGL -Cube Rotation

Привет, будущие маги WebGL! Сегодня мы отправляемся в увлекательное путешествие в мир 3D-графики. К концу этого урока вы сможете создать вращающийся куб с помощью WebGL. Это же здорово, не так ли? Погружаемся!

WebGL - Cube Rotation

Понимание основ

Прежде чем мы начнем крутить кубы как DJ, давайте разберемся с некоторыми основными концепциями.

Что такое WebGL?

WebGL (Web Graphics Library) - это API на JavaScript, который позволяет нам рендерить 3D-графику в браузере. Это как дать вашему браузеру парочку 3D-очков!

Почему куб?

Вы можете задаться вопросом: "Почему мы начинаем с куба?" Ну, мои дорогие студенты, куб - это как "Привет, мир" в 3D-графике. Он достаточен прост, чтобы его понять, но достаточен сложен, чтобы научить нас важным концепциям. Да и кто не любит хороший куб?

Настройка нашего окружения WebGL

HTML Canvas

Перво-наперво, нам需要一个 подиум для нашего куба. В WebGL этот подиум называется канвой. Давайте настроим его:

<canvas id="glcanvas" width="640" height="480">
Ваш браузер не поддерживает HTML5 canvas
</canvas>

Это создает канву размером 640x480 пикселей. Если вы его не видите, не волнуйтесь - это как невидимая танцплощадка прямо сейчас.

Инициализация WebGL

Теперь давайте подготовим WebGL к party:

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, это как пытаться играть DVD на ВHS-плеере - это просто не работает!

Создание нашего 3D-куба

Определение вершин

Куб имеет 8 углов (вершин). Мы должны tell 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);
}
`;

Этот простой шейдер просто закрашивает все белым. Это как покрасить наш куб!

Компиляция и liênирование шейдеров

Теперь нам нужно скомпилировать наши шейдеры и связать их в программу:

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

Это как научить наших магов (шейдеров) их трюков и затем пустить их на сцену!

Делаем наш куб вращаться

Настройка вращения

Чтобы сделать наш куб вращающимся, мы должны обновлять его angle вращения со временем:

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

Эта функция очищает канву, настраивает перспективу, применяет вращение иfinally рисует наш куб.

Заключение

И вот оно,folks! Мы создали вращающийся 3D-куб с помощью WebGL. Помните, что овладение 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() Указывает значение переменной uniform для текущего объекта программы
gl.drawArrays() Рендерит примитивы из массива данных

Продолжайте практиковаться, продолжайте программировать, и скоро вы сможете создавать удивительную 3D-графику в вашем браузере. Счастливого кодирования!

Credits: Image by storyset