Курс по программированию на JavaScript
Урок 46: Введение в графику с WebGL
WebGL (Web Graphics Library) — это JavaScript API для рендеринга 2D и 3D графики в веб-браузере без использования плагинов. WebGL позволяет использовать аппаратное ускорение графики, предоставляя мощные возможности для создания интерактивных графических приложений. В этом уроке мы рассмотрим основы WebGL и создание простых 3D-сцен.
Основы WebGL
WebGL основан на OpenGL ES 2.0 и предоставляет программный интерфейс для работы с графическим процессором (GPU). С помощью WebGL можно создавать сложные графические сцены, манипулировать ими и отображать их в реальном времени.
Чтобы начать работу с WebGL, нам нужно создать контекст WebGL и инициализировать его. Рассмотрим пример создания контекста WebGL:
<canvas id="glcanvas" width="640" height="480"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL не поддерживается вашим браузером.');
}
</script>
// Контекст WebGL инициализирован
Рендеринг простейшего треугольника
Давайте рассмотрим пример рендеринга простейшего треугольника с помощью WebGL:
<canvas id="glcanvas" width="640" height="480"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL не поддерживается вашим браузером.');
}
// Устанавливаем цвет очистки и очищаем буфер цвета
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Определяем вершины треугольника
const vertices = new Float32Array([
0.0, 1.0,
-1.0, -1.0,
1.0, -1.0,
]);
// Создаем буфер и загружаем в него вершины
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
// Определяем и компилируем шейдеры
const vertexShaderSource = `
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// Создаем и связываем шейдерную программу
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// Привязываем буфер вершин к атрибуту шейдера
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.enableVertexAttribArray(positionAttribLocation);
gl.vertexAttribPointer(positionAttribLocation, 2, gl.FLOAT, false, 0, 0);
// Рендерим треугольник
gl.drawArrays(gl.TRIANGLES, 0, 3);
</script>
// Треугольник отрендерен
Создание простых 3D-сцен
Для создания 3D-сцен с помощью WebGL нужно определить вершины объектов, настроить камеры и освещение, а также создать шейдеры для рендеринга. Рассмотрим пример создания простого куба.
<canvas id="glcanvas" width="640" height="480"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL не поддерживается вашим браузером.');
}
// Устанавливаем цвет очистки и очищаем буфер цвета
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Определяем вершины куба
const vertices = new Float32Array([
-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,
]);
// Определяем индексы вершин для рендеринга куба
const indices = new Uint16Array([
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
0, 1, 5, 0, 5, 4,
2, 3, 7, 2, 7, 6,
0, 3, 7, 0, 7, 4,
1, 2, 6, 1, 6, 5
]);
// Создаем буферы и загружаем в них вершины и индексы
const vertexBuffer = gl.createBuffer();
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
// Определяем и компилируем шейдеры
const vertexShaderSource = `
attribute vec3 aPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
}
`;
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// Создаем и связываем шейдерную программу
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// Привязываем буфер вершин к атрибуту шейдера
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.enableVertexAttribArray(positionAttribLocation);
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, false, 0, 0);
// Определяем матрицы модели-вида и проекции
const modelViewMatrix = mat4.create();
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45, canvas.width / canvas.height, 0.1, 100.0);
mat4.translate(modelViewMatrix, modelViewMatrix, [0.0, 0.0, -6.0]);
// Передаем матрицы в шейдеры
const modelViewMatrixLocation = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
const projectionMatrixLocation = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
gl.uniformMatrix4fv(modelViewMatrixLocation, false, modelViewMatrix);
gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix);
// Рендеринг куба
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
</script>
Упражнения
Упражнение 1: Рендеринг квадрата
Создайте простую сцену, которая рендерит квадрат. Определите вершины квадрата и используйте шейдеры для отображения.
Решение:
<canvas id="glcanvas" width="640" height="480"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL не поддерживается вашим браузером.');
}
// Устанавливаем цвет очистки и очищаем буфер цвета
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Определяем вершины квадрата
const vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
]);
// Определяем индексы вершин
const indices = new Uint16Array([
0, 1, 2,
0, 2, 3
]);
// Создаем буфер и загружаем в него вершины и индексы
const vertexBuffer = gl.createBuffer();
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
// Определяем и компилируем шейдеры
const vertexShaderSource = `
attribute vec2 aPosition;
void main() {
gl_Position = vec4(aPosition, 0.0, 1.0);
}
`;
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// Создаем и связываем шейдерную программу
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// Привязываем буфер вершин к атрибуту шейдера
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.enableVertexAttribArray(positionAttribLocation);
gl.vertexAttribPointer(positionAttribLocation, 2, gl.FLOAT, false, 0, 0);
// Рендеринг квадрата
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
</script>
Объяснение: Мы определили вершины и индексы для квадрата, создали шейдеры и отрендерили квадрат.
Упражнение 2: Вращение куба
Создайте анимацию вращения куба. Используйте requestAnimationFrame для создания плавной анимации.
Решение:
<canvas id="glcanvas" width="640" height="480"></canvas>
<script>
const canvas = document.getElementById('glcanvas');
const gl = canvas.getContext('webgl');
if (!gl) {
console.error('WebGL не поддерживается вашим браузером.');
}
// Устанавливаем цвет очистки и очищаем буфер цвета
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// Определяем вершины куба
const vertices = new Float32Array([
-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,
]);
// Определяем индексы вершин
const indices = new Uint16Array([
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
0, 1, 5, 0, 5, 4,
2, 3, 7, 2, 7, 6,
0, 3, 7, 0, 7, 4,
1, 2, 6, 1, 6, 5
]);
// Создаем буферы и загружаем в них вершины и индексы
const vertexBuffer = gl.createBuffer();
const indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW);
// Определяем и компилируем шейдеры
const vertexShaderSource = `
attribute vec3 aPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
void main() {
gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
}
`;
const fragmentShaderSource = `
void main() {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
`;
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSource);
gl.compileShader(vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSource);
gl.compileShader(fragmentShader);
// Создаем и связываем шейдерную программу
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
// Привязываем буфер вершин к атрибуту шейдера
const positionAttribLocation = gl.getAttribLocation(shaderProgram, 'aPosition');
gl.enableVertexAttribArray(positionAttribLocation);
gl.vertexAttribPointer(positionAttribLocation, 3, gl.FLOAT, false, 0, 0);
// Определяем матрицы модели-вида и проекции
const modelViewMatrix = mat4.create();
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45, canvas.width / canvas.height, 0.1, 100.0);
mat4.translate(modelViewMatrix, modelViewMatrix, [0.0, 0.0, -6.0]);
// Передаем матрицы в шейдеры
const modelViewMatrixLocation = gl.getUniformLocation(shaderProgram, 'uModelViewMatrix');
const projectionMatrixLocation = gl.getUniformLocation(shaderProgram, 'uProjectionMatrix');
gl.uniformMatrix4fv(projectionMatrixLocation, false, projectionMatrix);
let angle = 0;
function animate() {
angle += 0.01;
mat4.rotate(modelViewMatrix, mat4.create(), angle, [1, 1, 1]);
gl.uniformMatrix4fv(modelViewMatrixLocation, false, modelViewMatrix);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0);
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
</script>
Объяснение: Мы добавили анимацию вращения куба с использованием requestAnimationFrame для создания плавной анимации.
|