GrabDuck

Платформа для экспериментов на Three.JS и WebGL

:

Если не играть и не рассматривать фотографии знакомых девушек в контакте, то программирование является моим любимым занятием за компьютером. Особенно мне нравится «быстрое» программирование. Это такое программирование, когда проект небольшой и когда мы не занимаемся фундаментальными изысканиями, а планирование не нуждается в документации. К слову, на работе все проекты не такие, да ещё и ориентированы на закрытую аудиторию. В основном поэтому вы сейчас читаете мою статью, а ещё я желаю попасть на Хабр.

Хочу поделиться с вами результатами проекта моих выходных. Это чисто клиентское javascript/html приложение, которое состоит из редактора исходных кодов на языке Javascript и GLSL-шейдеров, мини-линкера, окна с Three.JS/WebGL и небольшого рантайм-интерфейса. Всё это склеено на jquery, а поверх прикручена галерея с видео. В галерее представлено несколько демонстраций. Можно выбрать понравившуюся демонстрацию и поиграть с её кодом, а также экспортировать и импортировать целые проекты из нескольких файлов через JSON.

Код каждой демонстрации может быть расположен в нескольких файлах. Эти файлы подгружаются ajax'ом и сразу же сохраняются в local storage где их можно редактировать, добавлять новые и удалять. Повторюсь, приложение чисто клиентское. Для добавления файлов в local storage используется file api. Можно добавлять только текстовые файлы, потому что с объёмом local storage не разгуляешься при хранении base64-представлений бинарных файлов.

Давать ссылку на работающее приложение я не буду, потому что у меня нет своего нормального хостинга. Остаётся только качать исходники и размещать их у себя на веб-сервере. Веб-сервер всё-таки нужен, потому что с недавних пор для WebGL запрещена загрузка текстур с локальной файловой системы.

Все исходники доступны на Bitbucket в системе Mercurial: https://bitbucket.org/Bangybug/treejsplayground, там же имеется возможность скачать Zip-архив.

Есть ещё небольшое видео чтобы заинтересовать вас:

Если вы заинтересовались и решили качать исходники, то считаю долгом предупредить о том что нормально работает только на Chrome (у меня 21.0.1180.60). В Firefox (у меня 14.0.1) сейчас WebGL отключен, и чтобы его включить, потребуется набрать в адресной строке about:config, затем установить фильтр на webgl, поставить force-enabled=true и disabled=false. Но на этом печали Firefox не заканчиваются — читайте ниже про Галерею.

Как всё начиналось и во что превращалось


Вначале была необходимость написать шейдер. У меня не очень много опыта в этом деле, так что в планах было попросить совет у моих виртуальных знакомых. Также хотелось немного облегчить их труд, показав сразу свой прототип, без необходимости что-то устанавливать, но с возможностью налету вносить изменения и наблюдать результат.

Задумано — сделано, выбор сразу пал на WebGL. Не хотелось строить свой велосипед из вызовов функций самого WebGL, благо удачно подвернулась библиотека Three.JS. Потом мелькнула мысль, что неплохо бы вместо textarea подобрать редактор с подсветкой. Выбор пал на ACE. Мне он показался довольно качественным, работает с большими файлами, поддерживает code folding, анализирует код и может подсказать, где ошибка, или как сделать лучше.

Потом захотелось сделать подобие менеджера проекта, который включал бы список файлов и мог бы удалять и добавлять файлы по усмотрению клиента. С серверным кодом связываться принципиально не хотелось, оставался выход — нововведения Html5: File API и Local Storage, заодно посмотреть что да как.

После того как менеджер проекта со списком файлов был готов, автоматически назрела потребность в компоновщике для файлов исходных кодов. Был написан элементарный компоновщик, который с помощью функции eval проходит по всем файлам. Если в каком-то из исходников требуется функциональность другого, до которого ещё не дошёл компоновщик, то достаточно вызвать специальную функцию компоновщика APP.require. При этом компоновщик не будет проходить по ранее подключенным файлам.

Так и не встретив проблем, решил продолжить занятие. Интерес переместился на другие нововведения Html5 в области CSS и визуальных эффектов. Захотелось сделать видео-галерею, где каждый ролик был бы показан с каким-нибудь CSS-эффектом. Тут уже возникли сложности. О них написано далее.

Финальным шагом стало выделение кода шейдеров в отдельный тип исходников. При редактировании шейдеров, редактор переключался на грамматику языка C. Это оказалось удобно, ведь иначе пришлось бы хранить код шейдеров либо внутри Javascript, либо внутри Html.

Компиляция файлов проекта и минимальный рантайм-интерфейс


Как уже отмечалось, есть элементарный компоновщик. Он находится в файле app/linker.js. У него есть функция require, которая доступна из исходников как APP.require. Эту функцию стоит вызывать для того чтобы обеспечить правильный порядок подключения откомпилированных исходников. Пример использования — демонстрация «Brothers in arms» (см. галерею).

Есть ещё минимальный рантайм-интерфейс, который состоит из таких объектов:

  • APP.page.webgl.renderer — готовый к работе объект Renderer из Three.JS;
  • APP.page.webgl.viewport — объект со свойствами width и height, устанавливается автоматически при инициализации окна WebGL, значения берутся из webgl.css;
  • APP.page.webgl.viewportMouse — объект с функциями getX() и getY(), которые возвращают последние замеченные координаты указателя, когда тот был над окном WebGL;
  • APP.shader( vertexShaderFileName, fragmentShaderFileName, uniforms) — возвращает новый объект THREE.ShaderMaterial, собранный из указанных подпрограмм и констант uniforms. В дальнейшем можно менять значения свойств объекта uniforms, и шейдер автоматически их подхватит перед рендером.

Таков минимальный рантайм, который предоставляется для исходников клиента. В свою очередь, в исходниках клиента должны быть определены функции main и exit. Эти функции отвечают за старт и остановку демонстрации. Рекомендуется очищать ресурсы внутри exit.

Эксперименты с видео-галереей


Здесь меня постигли сложности. Во-первых, border-radius в Chrome не применяется к элементу video. Точнее, применяется только к изображению которое используется как poster. Как только видео начинает играть, скругления исчезают. Стиль box-reflection работает только в webkit.

В Firefox всплыли проблемы с вёрсткой, и что совсем печально, с самой идеей видео-галереи. Нельзя просто так взять и воткнуть даже 4-5 элементов video, даже если играть будет только одно из них при наведении курсора. Firefox посылает очень много запросов, это завешивает страницу и даже мой локальный Apache httpd. Приходится закрывать браузер.

В Chrome проблем с подвешиванием нет, хотя всё равно есть над чем помедитировать глядя на потребление памяти.

В итоге просто добавил параметр в файл app/gallery.js/initialize — useImages. При установке его в true, вместо видео будут картинки.

Компиляция шейдеров


Для компиляции шейдеров я добавил функцию APP.shader, она описана выше. Только лишь уточню, что здесь используется код из Three.JS. Этот код автоматически будет устанавливать в ваш вершинный шейдер атрибут с названием «position». Имейте это в виду, не пытайтесь писать шейдер с другим названием этого атрибута. Есть альтернатива — это переписать функцию app / webgl.js / shader, но при этом потеряется возможность использовать отрисовщик из Three.JS.

Сравнение с примером кода на «чистом» WebGL


Само собой разумеется, что кода будет сильно меньше на Three.JS. А если использовать мой playground, то код ещё уменьшится. Единственное препятствие — это необходимость понимать, каким образом те или иные функции WebGL обёрнуты в библиотеке Three.JS. Например, режим отправки вершинных данных TRIANGLE_STRIP в Three.JS соответствует объекту THREE.Ribbon.

Для сравнения, откройте каталоги с идентичным проектом: /defaults/sources/water и /defaults/sources/water_webgl. Так вы сможете получить представления о различиях при использовании чистого WebGL и Three.JS.

Подготовка новой демонстрации и вставка её в проект


Все демонстрации описаны в app / gallery.js / projects. Типичное описание выглядит так:
		'spheres' : 
		{
			files : [{type:'source', path:'defaults/sources/spheres/main.js', content:null}],
			category : 'Basic',
			title : 'Many ballz',
			image : 'defaults/sources/spheres/img.png',
			video : 'defaults/sources/spheres/video.ogv',
			description: 'Basic demo from Three.JS package, creates 50k spheres with different materials. Demonstrates on-the-fly texture generation.'
		}

Для записи видео я использовал бесплатную программу Camstudio. От неё нужно получить видео в размере 320х200. Для этого можно предварительно установить в webgl.css размеры окна 320х200, а в самой Camstudio настроить снятие видео с этой области. Также помогают hotkeys, они избавляют от необходимости редактировать видео. Рекомендую сразу выбрать каталог куда будут складываться видео-файлы. Желательно не корневой.

Чтобы видео можно было встроить в элемент video, придётся его преобразовать в OGG Theora. Я использовал FFMpeg2Theora.

Использование параметров в Url


Для удобства есть возможность использовать параметры в url. Например, чтобы показать галерею, достаточно добавить в адрес такое: ?gallery=category, где category — это категория демонстраций (см. описание демонстрации).

Можно сразу перенаправлять на нужный проект, добавив ?project=project_id, где project_id — ключ в app / gallery.js / projects, например spheres.