Сейчас мы напишем первую программу с использованием технологии WebGL. Фактически это будет не одна программа, а два примера, которые покажут в действии механизм WebGL.
Первый пример
Создадим какую-нибудь веб-страничку со следующим содержимым:
<!DOCTYPE html>
<html>
<head>
<title>Привет WebGL!</title>
</head>
<body>
<canvas id="canvas3D" width="400" height="300">Ваш браузер не поддерживает элемент canvas</canvas>
<!-- фрагментный шейдер -->
<script id="shader-fs" type="x-shader/x-fragment">
void main(void) {
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
<!-- вершинный шейдер -->
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
void main(void) {
gl_Position = vec4(aVertexPosition, 1.0);
}
</script>
<script type="text/javascript">
var gl;
var shaderProgram;
var vertexBuffer;
// установка шейдеров
function initShaders() {
// получаем шейдеры
var fragmentShader = getShader(gl.FRAGMENT_SHADER, 'shader-fs');
var vertexShader = getShader(gl.VERTEX_SHADER, 'shader-vs');
//создаем объект программы шейдеров
shaderProgram = gl.createProgram();
// прикрепляем к ней шейдеры
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
// связываем программу с контекстом webgl
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Не удалсь установить шейдеры");
}
gl.useProgram(shaderProgram);
// установка атрибута программы
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
// делаем доступным атрибут для использования
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
}
// Функция создания шейдера по типу и id источника в структуре DOM
function getShader(type,id) {
var source = document.getElementById(id).innerHTML;
// создаем шейдер по типу
var shader = gl.createShader(type);
// установка источника шейдера
gl.shaderSource(shader, source);
// компилируем шейдер
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("Ошибка компиляции шейдера: " + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
// установка буфера вершин
function initBuffers() {
// установка буфера вершин
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// массив координат вершин объекта
var triangleVertices = [
0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);
// указываем кол-во точек
vertexBuffer.itemSize = 3;
vertexBuffer.numberOfItems = 3;
}
// отрисовка
function draw() {
// установка области отрисовки
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
// указываем, что каждая вершина имеет по три координаты (x, y, z)
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
// отрисовка примитивов - треугольников
gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numberOfItems);
}
window.onload=function(){
// получаем элемент canvas
var canvas = document.getElementById("canvas3D");
try {
// Сначала пытаемся получить стандартный контекст WegGL
// Если не получится, обращаемся к экспериментальному контексту
gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl");
}
catch(e) {}
// Если контекст не удалось получить, выводим сообщение
if (!gl) {
alert("Ваш браузер не поддерживает WebGL");
}
if(gl){
// установка размеров области рисования
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
// установка шейдеров
initShaders();
// установка буфера вершин
initBuffers();
// покрасим фон в бледно-розовый цвет
gl.clearColor(1.0, 0.0, 0.0, 0.5);
// отрисовка сцены
draw();
}
}
</script>
</body>
</html>
Я не буду останавливаться на этом коде - основные моменты подробнее мы рассмотрим детально позднее, но, я думаю, комментарии помогут разобраться. Но если вкратце: у нас есть элемент canvas, который представляет полотно для отрисовки сцены WebGL. Далее в виде скриптов javascript объявляются две мини-программы - фрагментный и вершинный шейдер.
Весь код программы находится в обработчике функции window.onload. Вначале нам надо получить контекст gl, через который и буду вестись все основные действия. Далее программа содержит три основные части: установка шейдеров, установка буфера вершин и сама отрисовка.
А пока можно запустить веб-страничку в браузере. И браузер явит нам белый треугольник на красном фоне.
Всего лишь? Это только начало. Позже мы подробнее поговорим о шейдерах, вершинах, геометрических примитивах и т.д. А теперь перейдем ко второму примеру.
Пример 2
Второй пример представляет собой хрестоматийный пример вращающегося куба. Вначале загрузим специальную библиотеку Three.js. Ее минимизированную версию можно найти по адресу Three.js. Перейдем по этому адресу и сохраним весь код на жесткий диск в файл под названием three.min.js. Эта библиотека не является необходимой для работы с WebGL, однако она упрощает работу.
В одном каталоге с загруженной библиотекой создадим файл index.html со следующим содержанием:
<DOCTYPE html>
<html>
<head>
<title>Привет WebGL!</title>
<script src="three.min.js"></script>
<script type='text/javascript'>
window.onload=function(){
var camera, scene, renderer;
var geometry, material, mesh;
init();
animate();
// инициализация начальных значений
function init() {
// создаем камеру - перспективная проекция
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000);
// установка z-координаты камеры
camera.position.z = 600;
// настройка сцены
scene = new THREE.Scene();
// настройка геометрии - в качестве геометрии будет куб
// настроим его ширину, высоту и длину по оси z
geometry = new THREE.CubeGeometry(200, 200, 200);
// настройка материала - установка цвета
material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: true});
// настраиваем меш, который будет отображать куб
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
// создаем объект для рендеринга сцены
renderer = new THREE.CanvasRenderer();
// установка размеров
renderer.setSize(window.innerWidth, window.innerHeight);
// встраиваем в DOM-структуру страницы
document.body.appendChild(renderer.domElement);
}
// функция анимации
function animate() {
requestAnimationFrame(animate);
// вращение меша вокруг осей
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
// рендеринг сцены - метод, производящий по сути отрисовку
renderer.render(scene, camera);
}
}
</script>
</head>
<body>
</body>
</html>
Все уже выглядит гораздо круче, а кода гораздо меньше. Да, Three.js, а также другие библиотеки и фреймворки по работе с WebGL значительно упрощают работу с графикой. Но в то же время даже минимизированнные их версии весят очень не мало. Какой способ стоит выбрать - это дело разработчика. В дальнейшем мы будем рассматривать преимущественно работу с чистым WebGL, но также и затронем работу с библиотеками, например, с той же Three.js. А теперь можно переходить уже непосредственно к особенностям технологии.