Сейчас мы напишем первую программу с использованием технологии 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
,
через который и буду вестись все основные действия. Далее программа содержит три основные части: установка шейдеров, установка буфера вершин и сама отрисовка.
А пока можно запустить веб-страничку в браузере. И браузер явит нам белый треугольник на красном фоне.
Всего лишь? Это только начало. Позже мы подробнее поговорим о шейдерах, вершинах, геометрических примитивах и т.д. А теперь перейдем ко второму примеру.
Второй пример представляет собой хрестоматийный пример вращающегося куба. Вначале загрузим специальную библиотеку 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.WebGLRenderer(); // установка размеров 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. А теперь можно переходить уже непосредственно к особенностям технологии.