11 августа 2012 г.

Простое приложение на WebGL

В полку "хеллоуворлдов" прибыло. В этот раз на очереди WebGL. Захотелось приглядеться к этому "зверю" попристальней, потому как потенциал в нем видится не малый. Думаю, рано или поздно он потеснит на рынке все другие технологии в своей нише. Или появится что-то более удобное во всех отношениях. Пока же, по моим ощущениям, для Stage3D (Flash) писать гораздо приятней, что-ли. Но хватит разжигания межплатформенных розней.

Основа кода мной была взята отсюда. Я лишь немного её изменил для возможного дальнейшего препарирования, добавил чуток анимации и некоторых других утилитарных "плюшек".

Вкратце, можно сказать, что для простейшего приложения на WebGL необходим файл HTML, файл с кодом приложения на Javascript и утилитарный файл с Javascript для работы с матрицами. Впрочем, без последнего вполне себе можно обойтись, но тогда или придется много написать самому или приложение будет ну совсем уж простым.

Файл HTML нам нужен для представления сцены (HTML-элемент canvas с поддержкой OpenGL-контекста) и кода шейдеров на ESSL (подмножестве высокоуровнего языка GLSL для написания программ для графических процессоров). Я взял заготовку HTML из набора Boilerplate (кстати, очень рекомендую) и добавил следующий код из урока:

<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec4 aVertexColor;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;

varying vec4 vColor;

void main(void) {
    gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
    vColor = aVertexColor;
}
</script>

Это код вершинного шейдера — программы отвечающей за обработку каждой вершины в графическом процессоре. Еще нам необходима программа обработки каждого пиксела в графическом процессоре:

<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;

varying vec4 vColor;

void main(void) {
    gl_FragColor = vColor;
}
</script>

Стоит отметить, что данные шейдеры можно заменить на другие (коих уже очень много) и можно получить совершенно другой эффект на экране.

Далее сам HTML-код, содержащий необходимые элементы:

<!--основной контейнер-->
<div role="main" id="content-holder">
    <!--контейнер для отображения FPS (кадры в секунду)-->
    <div>FPS: <span id="fps-holder">60</span></div>
    <!--элемент canvas - где мы рисуем-->
    <canvas id="scene-holder" width="640" height="480">HTML5 not supported!</canvas>
    <!--контейнер для отображения ошибок или лога-->
    <div id="error-holder"></div>
</div>

Дальше подключаем необходимые нам скрипты:

<script src="js/libs/underscore.js"></script>
<script src="js/libs/glMatrix-0.9.5.min.js"></script>
<script src="js/main.js"></script>

Первым в списке идет шикарная библиотека underscore, которая превращает Javascript в очень удобный язык. После того, как начинаешь ей пользоваться отвыкнуть уже сложно. Затем подключаем библиотеку работы с матрицами и, наконец, код со скриптом приложения.

Код приложения я решил построить на основе функции конструктора, чтобы максимально избежать наличия глобальных переменных. Основа:

/**
 * Renderer application
 *
 * @module main
 * @requires underscore, json, glMatrix
 */
var WEBGLEX = WEBGLEX || {};

WEBGLEX.Renderer = function (errorCallback) {
    'use strict';
    if (!(this instanceof WEBGLEX.Renderer)) {
        return new WEBGLEX.Renderer();
    }
};
// entry point
window.onload = function () { WEBGLEX.Renderer(); };

Здесь как бы создается пространство имен WEBGLEX и класс Renderer в нем. Класс инициируется после загрузки DOM. В классе мы вызываем последовательность методов, необходимых для работы приложения:

(function (self, callbacks) {
    var success = callbacks.shift().call(self);
    if (!success) return;
    _.each(callbacks, function (callback) {
        callback.call(self);
    });
})(_self, [_initGL, _initHelpers, _initGLShaders,
           _initGLBuffers, _initGLParams, _drawLoop]);

_initGL выбирает контекст рисования, в _initHelpers настраиваются различные утилитарные вещи, _initGLShaders компилиреут шейдеры, _initGLBuffers загружает и биндит VBO (буффер вершин), в _initGLParams настраиваются начальные параметры GL, _drawLoop — основной цикл прорисовки.

У получившегося приложения еще не мало мест для оптимизации и рефакторинга. Но целью было знакомство с технологией, а не создание своего движка.

Полный код доступен здесь.

Комментариев нет:

Отправить комментарий