понедельник, 7 ноября 2011 г.

Материалы. Часть третья, коллекции.

В предыдущей части мы затронули одну из самый серьезных проблем системы материалов GLScene - привязку текстур к материалам сцены, потому решение было очевидным - отделить свойства материалов от текстур, таким образом в VBOMesh появилось две коллекции - коллекция свойств материала (TMaterialLibrary) и коллекция текстур (TTextureLibrary). Изначально эти библиотеки были раздельно, создавались вместе с TVBOMesh и наследовались всеми создаваемыми объектами. При этом у объекта появлялось несколько дополнительных свойств: Material: TMaterial, Texture: TTexture, Blending: TCustomBlending, NoDepthTest и NoZWrite. Из названий не сложно догадаться для чего они были нужны.



Так как своих лоадеров текстур у меня не было, так как не было VCL редактора материалов, то первым решением было импорт всех свойств из материалов сцены, что и было реализовано. С материалами сложностей не было, но вот текстуры доставили хлопот. Вот в чем причина - в сцене нет ни единого способа узнать существует текстура или нет... При попытке узнать хэндл текстуры - создается новый (при попытке получить свойства текстуры по такому хэндлу не получишь ничего хорошего), попытка узнать сколько было выделено памяти под текстуру через "TextureImageRequiredMemory" - тоже давала сбои, так как в некоторых ситуациях оно инициализировалось каким-то мусором... Единственный вариант - использовать свойство "Disabled", как это делается в сцене, но беда в том, что текстура может быть временно отключена, при этом находясь в видеопамяти, тогда пришлось бы непрерывно следить за состоянием этого свойства... Как позже выяснилось - у "Material.Texture" еще и не обновлялись свойства TexWidth, TexHeight, пришлось вытаскивать их из "Material.Texture.Image".

Впоследствии вылезла еще одна проблема, проблема с отложенной инициализацией текстуры драйверами некоторых видеокарт. Фокус в том, что после многих танцев с бубном вокруг свойств текстуры я перестал им доверять и получал всю информацию "из первых уст", тобишь прямо из OpenGL по хэндлу текстуры. Как оказалось - некоторые драйвера, ради экономии видеопамяти, не загружали текстуру в видеопамять до тех пор, пока она действительно не использовалась для отрисовки. Таким образом - не смотря на реально существующий хэндл текстуры не было возможности получить информацию о текстуре, ее свойствах и параметрах. Танцы с бубном и гугление не принесло результатов, потому пришлось надеяться на данные, хранимые у сценовской TGLTexture.

Следующая проблема возникла с обновлением свойств материалов, так, если в сцене поменять какой-то свойство у материал, свойства которого уже были импортированы, то естественно мои материалы/текстуры об этом ничего не знали, как следствие - результат отличатся от ожидаемого. Пришлось вводить еще одну процедуру, которая бы искала все используемые сценовские материалы, искала бы их соответствие в моей библиотеке материалов и по новой импортировала бы все свойства. Такая процедура была написана, как для отдельного материала так и для всего VBOMesh, но решение мне не очень нравилось...
Загрузка и использование коллекции свойств материалов


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

При всем этом я все так же не мог отказаться от существующей системы материалов из-за загрузчика векторных файлов (GLVectorFileObjects), который был жестко завязан на систему материалов сцены, потому проблему пришлось решать комплексно, написав свои загрузчики для нужных векторных файлов (obj/3ds/smd) и уже приучив их использовать свою систему материалов. В этом случае проблема с именами текстур перестала существовать, так как каждая текстура имела уникальное имя файла, по которому ее можно было идентифицировать.

Так же пришлось написать свои загрузчики для некоторых графических форматов, в частности, в настоящий момент модуль может грузить текстуры в таких форматах: bmp, jpg, tga, dds (пока только DXT1-5). Загрузчики написаны на скорую руку, потому есть некоторые проблемы с производительностью, в частности с загрузкой jpg, но это все решаемо. Так же решил включить поддержку dds, но ввиду огромного разнообразия возможных текстурных форматов, хранимых в контейнере dds, это займет некоторое время, потому пока реализовал наиболее востребованные форматы сжатых текстур (DXT1-5). К сожалению, из-за сильной привязки к сценовским классам, использование сценовских загрузчиков, без полной переделки, не представляется возможным.

В настоящее время можно найти несколько "пригодных" библиотек, в частности это:
  • http://imaginglib.sourceforge.net - неплохая библиотека, но уж больно громоздкая, да и работа с ней очень замороченная.
  • http://openil.sourceforge.net - поддерживает огромное количество графических форматов, но написана паскудно и присутствует множество багов. К тому же приходится таскать с собой порядка 800кб библиотек.
  • GraphicEx library - неплохая библиотека, написанная создателем GLScene (Mike Lischke), но уже давно не обновлялась и работает со стандартными делфийскими TPicture/TBitmap, как следствие - потом приходится через ScanLine "побайтово" копировать эти текстуры в видеопамять.
Была еще парочка загрузчиков, но не сохранилось на них ссылок, так или иначе все они обладали перечисленными выше недостатками. Потратив кучу времени на поиск подходящего остановился на общеизвестном загрузчике:
http://www.sulaco.co.za/opengl_project_BMP_JPG_TGA_texture_loader.htm
Существенно переработав и доработав его, но оттуда и растут ноги у проблемы со скоростью загрузки jpg.

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

Через некоторое время, разрабатывая несколько проектов, заметил что существующая в сцене система "объединяющего" материала (свойства материала + текстура + шейдер + смешивание) все же была достаточно удобной, так как позволяла объединить все настройки поверхности, что упрощало копирование, идентификацию и редактирование свойств для отдельных объектов. В результате появился новый модуль uMaterialObjects, где была объявлена своя коллекция (TMaterialObjectsLib) и класс (TMaterialObject), на который и была переписана вся система материалов модуля. В результате все данные о текстурах и материалах теперь хранятся в нескольких коллекциях - коллекции свойств материалов, коллекции текстур, коллекции шейдеров (об этом позже) и коллекции объектов материала. Примерно такую же структуру можно видеть в системе материалов COLLADA, что в будущем упростит импорт данных в этом формате.

Краткий обзор возможностей системы материалов VBOMesh:
  • Установка свойств материалов;
  • Установка типа взаимодействия с вершинными атрибутами;
  • Установка свойств освещения;
  • Установка режима смешивания и альфатеста (через предустановленные режимы и произвольные);
  • Установка параметров взаимодействия материала с текстурой;
  • Установка параметров взаимодействия со сценой и окружением;
  • Установка режимов буфера глубины (для объекта);
  • Установка параметров смешивания Объект/материал;
  • Установка дополнительных текстурных слоев;
  • Отключение использования текстуры/материала/шейдера; 
  • Загрузка готовых коллекций материалов;
  • Активация шейдера при применении объекта материала;
  • Сортировка сцены для корректного отображения полупрозрачных объектов. 
  • Загрузка материалов из библиотеки материалов mtl/obj
Работа с текстурами будет рассмотрена позже.
Из недоработок - нет возможности указывать тест глубины для отдельных материалов и нет возможности разделить прозрачные и непрозрачные объекты для раздельной сортировки. Нужно доработать загрузчики текстур, нужно предусмотреть автоматическую передачу свойств материала/индексов текстур в шейдер, добавить шейдерные материалы.
О всем, что касается шейдеров, мы поговорим в следующий раз.

6 комментариев:

  1. То есть у тебя сейчас есть есть коллекции TTextureList, TMaterialOptionsList, TShaderList (названия взяты для примера), и есть объект TMaterialObject, который хранит ссылки на элементы коллекций, я верно понял?

    Сам планировал систему материалов, но без отдельных коллекций и тоже столкнулся с проблемой копирования ресурсов (шейдеров и текстур, с MaterialOptions как-то еще ладно). Элегантного решения сходу не придумал, но теперь, благодаря тебе, возникли мысли :)

    P.S. Что тебя держит у сцены? Viewer? VectorGeometry?

    P.P.S. blogger с ума сходит с твоим блогом, не хочет добавлять его в ленту, google reader чаще всего вызывает ошибки. Ума не приложу, в чем тут проблема

    ОтветитьУдалить
  2. Да, TMaterialObject хранит ссылки на элементы коллекций материалов, текстур, шейдеров + настройки смешивания + режим теста и записи в буфер глубины. Хотя можно и отдельно создать материал или текстуру и прикрепить ее к TMaterialObject, так как он хранит не "номер в коллекции" а указатель на объект.

    Решение перейти на TMaterialObject было продиктовано необходимостью как-то идентифицировать свойства поверхности (для стэнсильного рендера), так как нужно было объединить все одинаковые материалы на сцене и присвоить им номера + реализовать поиск среди существующих материалов. С раздельными ресурсами это было бы сложно и ресурсоемко.

    На счет "что держит" - собственно уже ничего, просто дань уважения :)
    Пример использования модуля без сцены я выкладывал еще год назад, но тогда еще не было своей системы материалов, не было своих загрузчиков векторных форматов, и не было VCL вьювера. Вьювер я уже давным-давно написал (причем уже несколько вариантов), проблему материалов и GLVectorFileObjects я уже решил, так что могу в любой момент оборвать последние связи со сценой.

    Просто сейчас есть возможность использовать и ресурсы моего модуля и ресурсы сцены (кинуть камеру, прикрутить SimpleNavigator, добавить DCE и т.д. Если откажусь от вьювера/графа сцены - придется и с этим попрощаться, а на замену еще ничего нет.

    Вообще как-то планировал оставить такой вариант сосуществования сцены и VBOMesh, так как развитие VBOMesh практически завершено (осталась еще парочка новинок и большой багфикс). Следующий шаг - новая версия полноценного движка на новой архитектуре. Это сейчас в процессе разработки и если я смогу разобраться со своим временем, то может к Новому Году выложу альфаверсию.

    На счет блога - понятия не имею что не так, это мой первый опыт и все оставлено по дефаулту, разве что закрыл доступ поисковикам.

    ОтветитьУдалить
  3. Последнее слово в 3 абзаце: TGLTExture

    А внедрение png в приорететах нет? Я только его и использую.

    ОтветитьУдалить
  4. В планах много чего есть, но на все рук не хватает :)
    Png в принципе добавить не сложно, по аналогии с jpeg/bmp, через стандартные средства делфи.
    Как руки дойдут - сделаю.
    Пока дорабатываю загрузчик dds, уже грузит сжатые текстуры (DXT1/3/5), 32-битные XRGB/ARGB/XBGR/ABGR и грузит 8-битные Alpha/Luminance/Grayscale(Palette). Пока правда только 2D текстуры, текстурные массивы, 3D и кубические карты еще не доделал.

    Вообще думаю сделать формат коллекций текстур, загнать его в какой-то архив и оттуда грузить сырые данные. Уж больно много хлопот с загрузкой текстур из форматов, не предназначенных для этого... Тот же dds - может хранить порядка 200 форматов текстур, из которых в OpenGL всего десятка два аналогов, да и там проблемы... Тут вот выяснилось что в dds можно не все мип-уровни загонять, а ОГЛ требует чтоб были заполнены все, приходится достраивать недостающие, но возникает вопрос - а нафига мне тогда вообще нужны dds с мип-уровнями, если их всеравно приходится строить...

    ОтветитьУдалить
  5. >кинуть камеру, прикрутить SimpleNavigator, добавить DCE и т.д.
    Разве этим кто-то еще пользуется? DCE ущербен сам по себе, а изучить все возможности Box2D, ODE или Newton можно за неделю, если прочесть мануал и посмотреть демки.

    >...разве что закрыл доступ поисковикам.
    А зачем? По-моему тебе тут нечего стыдиться. Очень хорошие статьи. Ничем не хуже Steps3D.

    ОтветитьУдалить
  6. Отказаться от сцены легко, вернуть будет сложнее :)
    Все же предполагалось что этот движок будет использоваться вместе со старыми проектами а не только для новых, в этом плане поддержка сцены так же нужна. Вообще, в текущей версии модуля, у TVBOMesh есть два конструктора, один - позволяет создавать TVBOMesh как объект сцены, второй - как новый объект. Тоесть уже сейчас можно прикрутить свой вьювер (GLUT,SDL и прочее) и в цикле вызывать метод "DoRender". Тоесть жесткой привязки к сцене уже нет.

    На счет поисковиков - ну во-первых это не Steps3D, на Steps3D выложены переводы спецификации с демками, у меня же - только статьи о каком-то VBOMesh, о котором кроме участников GLScene.ru никто ничего не знает.

    Прежде чем открывать полный доступ нужно как минимум закончить оформление блога, привести ссылку на репозиторий проекта (предварительно выложив релизную версию модуля), пересобрать демки под новую версию модуля, сделать какую-то галерею скриншотов и т.д. и т.п. Иначе народ просто не поймет о чем вообще речь.

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

    Некоторое из написанного меняется прямо сейчас, к примеру выше я писал о разработке загрузчика dds, или о граблях, на которые я на днях наступил, используя сценовский вариант TMaterialObject, в частности возникла проблема с передачей разных параметров в один шейдер, расшаренный между несколькими TMaterialObject. Для этого нужно было идентифицировать объект, вызвавший шейдер, в сцене Яр это решил через добавление глобальной переменной, хранящей идентификатор выводимого объекта (решение через задницу если честно). Мне же пришлось дорабатывать методы Apply/unApply, в которые передается ссылка на соответствующее событие выводимого объекта. Тоже наглый хак, но на скорую руку ничего лучше не придумал...

    ОтветитьУдалить