Урок по использованию геометрических объектов, текстур и шейдеров в Processing - Часть вторая
Содержание материала
Работа с трёхмерными объектами
Что-то мы заработались в 2D, пора переходить к объёму! А поможет нам в этом пример Custom3DGeometry. Он представляет собой бесконечный поток летающих разноцветных пирамид. По своей сути этот пример является трёхмерным эквивалентом первого примера по работе с 2D графикой. В нём применяются те же самые методы - beginShape, endShape и vertex. Вся разница заключается лишь в дополнительном измерении. Однако в структуре программы есть одно большое различие. Все 2D-примеры были процедурными, а в Custom3DGeometry используется объектно-ориентированное программирование (ООП).
В программе описан класс Pyramid, который содержит весь код для создания, обновления и отображения фигур. ООП в большинстве случаев будет полезно и при создании 2D-скетчей. Когда Вы начнёте создавать целые 3D-миры, использование ООП станет обязательным условием, учитывая сложность 3D-скетчей. В противном случае Вам будет сложно уследить за программой. Преимущество ООП в том, что этот подход позволяет как можно больше упростить код основного скетча, разместив весь остальной код по классам.
В интернете существует множество ресурсов посвящённых созданию 3D-объектов в Processing. Во-первых, многие математические объекты описаны на таких сайтах, как Wikipedia и подобных ей. Можно использовать эти описания для воплощения объектов в коде. Во-вторых, существует множество открытых примеров исходного кода для создания 3D-геометрии. Даже если пример написан на другом языке программирования, код по созданию геометрии, как правило, легко портируется. И наконец, существуют несколько библиотек для Processing – например, Hemesh, Shapes3D и Toxiclibs - которые облегчают создание сложных трёхмерных объектов.
Текстурирование в 3D
Все приведённые выше примеры были простыми, поэтому использование прямых вызовов не сказывалось на частоте кадров. Но когда Вы начнёте работать со сложными объектами, состоящими из большого числа вертексов, частота кадров может упасть. К счастью существуют классы, которые могут эффективно хранить все необходимые данные способом, не сказывающемся на производительности и работающем с GPU (Vertex Buffer Object). В библиотеке GLGraphics для этих целей есть класс GLModel. В бета-версии Processing для этих целей существует PShape. Обе реализации работают схожим образом, не смотря на небольшие различия в синтаксисе при написании кода. В дальнейшем при упоминании GLModel, можете подразумевать PShape, если Вы работаете с версией Processing 2.0. Использование этих методов позволит создавать геометрические объекты с большим числом вертексов и приличной частотой кадров.
Основные шаги по созданию трёхмерных объектов в примерах таковы:
- Создание необходимых для построения объекта данных (вертексы, нормали, координаты текстур) и хранение их во временных массивах.
- Создание объекта GLModel и перевод данных из массивов в GLModel.
- Отображение объекта.
Первые два шага (создание объекта и создание GLModel) делаются один раз. После этого объект можно отображать на экране.
В примере TexturedSphere реализованы описанные выше шаги для создания виртуальной модели Земли. Относительно легко рассчитать положение точек, которые составляют сферу. И достаточно просто разместить на ней несколько полигонов. А вот, что действительно сложно, так это рассчитать координаты текстуры. Работа с координатами текстур всегда сложна, потому что Вам приходится накладывать плоское изображение на трёхмерный объект. Поэтому на объекте всегда будут области, которые будут выглядеть не совсем правильно.
При написании этого кода автор столкнулся с такой проблемой (неправильное размещение текстуры на полюсах и стыках изображений ), и нашёл несколько статей на эту тему:
http://tat-tvam-asi.in/hacking/webgl/sphere-shader/
http://sol.gfxile.net/sphere/index.html
http://gamedev.stackexchange.com/questions/33631/textureing-subdivided-icosahedron-in-xna-seam-problem
http://howevangotburned.wordpress.com/2011/02/28/the-oddyssey-of-texturing-a-geodesic-dome/
Быть может, стоило поискать существующий код, решающий эту проблему? И в самом деле, после недолгих поисков был найден код на C++, написанный Габором Паппом, который и был портирован в Processing. Метод Габора основан на сегментации, которая очень полезна при задании плотности меша с равномерным распределением вертексов по всей форме. Этот код, скорее всего, может быть оптимизирован, потому что он рисует вертексы с одним и тем же положением, но не использует индексы для их повторного использования. Проблема в том, что в некоторых случаях вертексы находятся в одной и той же позиции, но имеют разные текстурные координаты. Как бы там ни было, данный пример даёт реальный результат – корректно текстурированную сферу (разбитую на икосаэдры), хранящуюся в GLModel.
Применение шейдеров к объектам
Теперь, когда Вы создали 3D–объект и разместили его в GLModel, настало время перейти к следующей задаче – применению к объекту шейдеров. Шейдеры завоёвывают всё большую аудиторию, отчасти, благодаря таким инструментам, как Shadertoy [https://www.shadertoy.com/]. К сожалению, таким инструментам не хватает дружелюбного пользовательского интерфейса. Тем не менее, их небывалая мощь и грядущая поддержка шейдеров в 2.0 делает эти инструменты привлекательными для виджеев. В этом уроке будут затронуты два типа GLSL шейдеров: вертексные и фрагментные. В TexturedSphereGLSL за основу взят предыдущий пример кода и добавлено применение GLSL шейдеров. Вы можете взять оба примера и, сравнив их, увидеть добавленный код, отвечающий за использование шейдеров, хотя результат работы обоих примеров может быть очень схож или даже абсолютно идентичен. В этом примере можно двигать мышью, чтобы поменять освещаемую позицию.
В примере MultiTexturedSphereGLSL сделан ещё один шаг – добавлено несколько текстур. В зависимости от освещения рассчитываются шейдеры для получения итогового изображения. Например, можно сделать день, ночь или нечто среднее. Также здесь добавлен слой облаков нависших над поверхностью Земли. Эти примеры должны помочь Вам понять основы применения шейдеров к 3D-объектам.
Дополнительные эффекты при работе с шейдерами
Вертексное смещение (Vertex displacement). Это удивительный эффект. Увидев видео с его использованием, Вам без сомнения захочется воссоздать его в своём коде. Здесь будут представлены два типа смещения – смещение двумерной карты высот и смещение трёхмерной сферы. Кроме того, каждый из примеров имеет по две вариации: с другим входным изображением и с процедурным шумом, генерируемым внутри самого шейдера. Названия примеров говорят сами за себя, так что Вам должно быть понятно, что именно каждый из них делает. Основная идея вертексного смещения заключается в том, что позиция каждого вертекса смещается вдоль нормали на определённую величину.
Эта величина определяется двумя значениями: displaceStrength (глобальное смещение, которое применяется ко всем вертексам) и индивидуальным смещением, которое различно для каждого вертекса. Индивидуальное смещение обычно определяется картой (изображением). Также часто бывает, что индивидуальное смещение определяется шумом Перлина (perlin noise). Пример использования каждого из смещений можно увидеть в видео к этой статье (в самом верху).
GLSL_TextureMix – это пример ещё одного шейдерного эффекта. Здесь показано, как можно слить несколько текстур в одну. В основном скетче можно задать различные режимы смешения, которые будут оказывать влияние на код в шейдере. Конечно, лучше создать несколько различных шейдеров и переключаться между ними. Но этот урок не нацелен на оптимизацию, а приведённый код наилучшим образом демонстрирует основы использования GLSL. Этот эффект смешения текстур делается внутри фрагментного шейдера.
Преимуществами смешения текстур внутри фрагментного шейдера являются автоматическая интерполяция и нормализация координат текстуры. Это не просто слияние изображений. Вам не нужно заботиться о количестве пикселей, а нужно только хранить координаты текстур в диапазоне от 0 до 1 – в результате получите идеально гладкое смешение текстур.
Проблемы
Почти всегда при написании своего кода, что-то начинает идти не так. Вот несколько советов, если Вы столкнулись с «необъяснимыми» проблемами.
- При использовании некоторых видеокарт ATI Radeon компилятор начинает выдавать сообщения об ошибках (впрочем, совсем безобидные) такого вида: “Validation warning! – Sampler value [имя_первой_текстуры] has not been set.” Это сообщение можно просто проигнорировать. Оно никак не отразится на работе скетча.
- В бета-версии Processing в консоль могут выводиться предупреждения такого вида: “Display 0 does not exist, using the default display instead”. Это сообщение также можно просто проигнорировать. Оно никак не отразится на работе скетча
- Существует ещё несколько типов сообщений об ошибках, наподобие таких: “Error: OpenGL error [номер ошибки] at top endDraw(): [текст сообщения об ошибке]”.
Общая рекомендация – ошибки по 1280 номер можно игнорировать без каких-либо последствий для Вашего проекта. В то время как ошибки 1281, 1282 и т.д. оказывают существенное влияние на компиляцию скетча, он может просто не запуститься. Если скетч запустился без проблем, ошибку можно игнорировать. Если же экран стал чёрным, с ошибкой нужно разобраться. Такие ошибки появляются при использовании старых видеокарт, устаревших драйверов или при ошибках в коде. Многие из них, наверняка, уже обсуждались на форуме, посвященном работе в Processing, так что лучшим решением будет поискать, не было ли у кого-то такой же проблемы, и посмотреть как она была устранена.
Замечания
В одном уроке невозможно охватить всё, но этот урок – неплохое введение в основы работы с геометрическими объектами, текстурами и GLSL шейдерами. Возможно, что самым главным для Вас была возможность скачать примеры кода и посмотреть, как он работает. Попытайтесь понять, как основной скетч работает с данными внутри шейдеров. Скорее всего, на глубокое понимание этого уйдёт какое-то время. Тем более, что многие аспекты работы с GLSL не являются интуитивно понятными. Что можно сказать? Кодируйте, учитесь, старайтесь делать нестандартные вещи! За поддержкой обращайтесь на форум - https://forum.processing.org/. Если у Вас возникают непонятные ошибки, и Вы уверены, что проблема не в Вашем коде, высылайте отчёт на https://github.com/processing/processing/issues. Убедитесь, что сопроводили свой отчёт чётким описанием и примером кода, вызывающего ошибку, в противном случае она не будет исправлена.