Основы формата GLTF и GLB, часть 2

    Данная статья является продолжением рассмотра основ GLTF и GLB форматов. Вы можете найти первую часть статьи здесь. В первой части мы рассмотрели с вами зачем изначально планировался формат, а также такие артефакты и их атрибуты GLTF формата как Scene, Node, Buffer, BufferView, Accessor и Mesh. В данной же статье мы рассмотрим Material, Texture, Animations, Skin, Camera, а также закончим формировать минимальный валидный GLTF файл.


    image

    Material и Texture


    С мешем неразрывно связаны материалы и текстуры. При необходимости меш может быть анимирован. Материал хранит информацию о том, как модель будет отрендерена движком. GLTF определяет материалы, используя общий набор параметров, которые основаны на Physical-Based Rendering (PBR). PBR модель позволяет создавать “физически корректное” отображение объекта в разных световых условиях благодаря тому, что шейдинговая модель должна работать с “физическими” свойствами поверхности. Есть несколько способов описания PBR. Самая распространенная модель — это metallic-roughness model, которая и используется по умолчанию в GLTF. Также можно использовать и specular-glosiness модель, но только при помощи отдельного расширения (extenstion). Основные атрибуты материала следующие:


    1. name — имя меша.
    2. baseColorFactor/baseColorTexture — хранит инфомрацию о цвете. В случае атрибута Factor информация хранится в числовом значении для RGBA, в случае Texture — хранится ссылка на текстуру в объекте textures.
    3. metallicFactor — хранит информцию о Metallic
    4. roughnessFactor — хранит информцию об Roughness
    5. doubleSided — имеет значение true либо false (значение по умолчанию) и указывает на то, будет ли меш рендериться с обоих сторон или только с "лицевой" стороны.
      "materials": [
          {
              "pbrMetallicRoughness": {
                  "baseColorTexture": {
                      "index": 0
                  },
                  "metallicFactor": 0.0,
                  "roughnessFactor": 0.800000011920929
              },
              "name": "Nightshade_MAT",
              "doubleSided": true
          }
      ],

    Metallic или значение “металичности”. Этот параметр описывает как сильно отражающая способность схожа с настоящим металлом, т.е. насколько сильно свет отражается от поверхности. Значение измеряется от 0 до 1, где 0 — это диэлектрик, а 1 — чистый металл.


    Roughness или «шероховатость». Данный атрибут отображает насколько “шероховата” поверхность, тем самым воздействуя на рассеяние света от поверхности. Измеряется от 0 до 1, где 0 — идеально плоская, а 1 — полностью шероховатая поверхность, которая отражает лишь небольшое количество света.


    Texture — объект, который хранит в себе текстурные карты (Texture maps). Такие карты придают реалистичности модели. Благодаря ним можно обозначить внешний вид модели, придать различных свойств таких как металличность, шероховатость, естественное затемнение от окружения и даже свойств свечения. Текстуры описываются тремя высокоуровневыми массивами: textures, samplers, images. Объект Textures использует индексы для ссылок на sampler и image экземпляры. Самым важным объектом является image, т.к. именно он хранит информацию об местоположении карты. В textures он описывается словом source. Картинка может находится как где-то на жестком диске (например "uri": “duckCM.png”) так и закодирована в GLTF ("bufferView": 14, "mimeType": “image/jpeg”). Samplers — это объект, который определяет параметры фильтров и упаковки (wrapping) соответствующие GL типам.


    В нашем примере с треугольником нету текстур, но я приведу JSON из других моделей, с которыми работал. В данном примере текстуры были записаны в буфер, поэтому они тоже считываются с buffer при помощи BufferView:


    "textures": [
            {
                "sampler": 0,
                "source": 0
            }
        ],
        "images": [
            {
                "bufferView": 1,
                "mimeType": "image/jpeg"
            }
        ],

    Animations


    GLTF поддерживает сочлененную (articulated), скиновую (skinned) и морф таргет (morph target) анимации с помощью ключевых кадров (key frames). Информация этих кадров хранится в буферах и ссылается на анимации при помощи аксессоров. GLTF 2.0 определяет только хранилище анимации, поэтому в нём не определено какое-либо конкретное поведение во время выполнения, например такое как порядок воспроизведения, автозапуск, циклы, отображение временных шкал и т. д. Все анимации хранятся в массиве Animations и они определяются как набор каналов (атрибут channel), а также набор сэмплеров, которые определяет акссессоры (Accessor) обрабатывающие информацию о ключевых кадрах (key frames) и методом интерполяции (атрибут samples)


    Основные атрибуты объекта Animations следующие:


    1. name — название анимации (если таковое имеется)
    2. channel — массив, который соединяет выходные значения ключевых кадров анимации с определённой нодой в иерархии.
    3. sampler — атрибут, который ссылающийся на Accessor, который обрабатывает ключевые кадры (key frames) из буфера.
    4. target — это объект, который определяет какую ноду (объект Node) нужно анимировать используя атрибут node, а также какое свойство ноды нужно анимировать используя атрибут path — translation, rotation, scale, weights и т.п. Неанимированные атрибуты сохраняют свои значения во время анимаций. Если node не определена, то атрибут channel желательно опустить.
    5. samplers — определяет входные и выходные пары: набор скалярных значений с плавающей запятой, представляющих линейное время в секундах. Все значения (input/output) хранятся в буфере и доступны через акссессоры. Атрибут interpolation хранит значение интерполяции между ключами.

    В простейшем GLTF нету анимаций. Пример взят из другого файла:


    "animations": [
            {
                "name": "Animate all properties of one node with different samplers",
                "channels": [
                    {
                        "sampler": 0,
                        "target": {
                            "node": 1,
                            "path": "rotation"
                        }
                    },
                    {
                        "sampler": 1,
                        "target": {
                            "node": 1,
                            "path": "scale"
                        }
                    },
                    {
                        "sampler": 2,
                        "target": {
                            "node": 1,
                            "path": "translation"
                        }
                    }
                ],
                "samplers": [
                    {
                        "input": 4,
                        "interpolation": "LINEAR",
                        "output": 5
                    },
                    {
                        "input": 4,
                        "interpolation": "LINEAR",
                        "output": 6
                    },
                    {
                        "input": 4,
                        "interpolation": "LINEAR",
                        "output": 7
                    }
                ]
            },

    Skin


    Инфомрация о скиннинге, также известном как "шкуринг", a.k.a. костная анимация, хранится в массиве skins. Каждый скин определяется при помощи атрибута inverseBindMatrices, который ссылается на акссессор с IBM (inverse bind matrix) данными. Эти данные используются для переноса координат координат в то же пространство, что и каждый сустав/joint, а также атрибут массива joints, который перечисляет индексы узлов, используемые в качестве суставов/joints для анимации кожи. Порядок соединений определяется в массиве skin.joints и должен соответствовать порядку данных inverseBindMatrices. Атрибут skeleton указывает на объект Node, который является общим корнем иерархии суставов/joints или на прямую или косвенную родительскую ноду общего корня.


    Пример использования объекта skin (отсутствует в примере с треугольником):


        "skins": [
            {
                "name": "skin_0",
                "inverseBindMatrices": 0,
                "joints": [ 1, 2 ],
                "skeleton": 1
            }
        ]

    Основные атрибуты:


    1. name — название скиннинга
    2. inverseBindMatrices — указывает на номер акссессора, хранящего информацию об Inverse Bind Matrix
    3. joints — указывает на номер акссессора, храняшего информацию о суставах/joints
    4. skeleton — указывает на номер акссессора, храняшего информацию о "корневом"
      суставе/joint с которого начинается скелет модели

    Camera


    Камера определяет матрицу проекции, которая получается трансформацией “вида” (view) в координаты клипа. Если проще, то камеры определяют визуальный вид (угол обзора, направления “взгляда” и т.п.), который видит пользователь при загрузке модели.


    Проекция может быть “Перспективной” и “Ортогональной”. Камеры содержатся в нодах (nodes) и могут иметь трансформации. Камеры закреплены в объектах Node и, таким образом, могут иметь трансформации. Камера определена так, что локальна ось +X направлена вправо, объектив смотрит в направлении локальной оси -Z, а верх камеры совмещена с локальной осью +Y. Если же трансформация не указана, то камера находится в начале координат. Камеры хранятся в массива cameras. Каждая из них определяет атрибут type, который назначает тип проекции (перспектива или ортогональный), а также такие атрибуты как perspective или orthographic, в которых уже хранится более детальная информация. В зависимости от наличия атрибута zfar, камеры с типом "перспектива" могут использовать конечную или бесконечную проекцию.


    Пример камеры в JSON с типом perspective. Не актуально для примера минимального корректного GLTF файла (треугольника):


    "cameras": [
            {
                "name": "Infinite perspective camera",
                "type": "perspective",
                "perspective": {
                    "aspectRatio": 1.5,
                    "yfov": 0.660593,
                    "znear": 0.01
                }
            }
        ]

    Основные атрибуты объекта Camera:


    1. name — название скиннинга
    2. type — тип камеры, perspective или orthographic.
    3. perspective/orthographic — атрибут, содержащий детали соответствущего type значения
    4. aspectRatio — Соотношение сторон экрана (fov).
    5. yfov — угол вертикального поля зрения (fov) в радианах
    6. zfar — расстояние до дальней плоскости отсечения (clipping plane)
    7. znear — расстояние до ближней плоскости отсечения
    8. extras — данные, специфичные для приложения

    Минимальный валидный GLTF файл


    В начале статьи я писал о том, что мы соберём минимальный GLTF файл, который будет содержать в себе 1 треугольник. JSON со встроенным буфером можно найти ниже. Просто скопируйте его в текстовый файл, измените формат файла на .gtlf. Для просмотра 3D ассета в файле можете использовать любой просмотрщик, поддерживающий GLTF, но лично я использую этот


    {
      "scenes" : [
        {
          "nodes" : [ 0 ]
        }
      ],
    
      "nodes" : [
        {
          "mesh" : 0
        }
      ],
    
      "meshes" : [
        {
          "primitives" : [ {
            "attributes" : {
              "POSITION" : 1
            },
            "indices" : 0
          } ]
        }
      ],
    
      "buffers" : [
        {
          "uri" : "data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=",
          "byteLength" : 44
        }
      ],
      "bufferViews" : [
        {
          "buffer" : 0,
          "byteOffset" : 0,
          "byteLength" : 6,
          "target" : 34963
        },
        {
          "buffer" : 0,
          "byteOffset" : 8,
          "byteLength" : 36,
          "target" : 34962
        }
      ],
      "accessors" : [
        {
          "bufferView" : 0,
          "byteOffset" : 0,
          "componentType" : 5123,
          "count" : 3,
          "type" : "SCALAR",
          "max" : [ 2 ],
          "min" : [ 0 ]
        },
        {
          "bufferView" : 1,
          "byteOffset" : 0,
          "componentType" : 5126,
          "count" : 3,
          "type" : "VEC3",
          "max" : [ 1.0, 1.0, 0.0 ],
          "min" : [ 0.0, 0.0, 0.0 ]
        }
      ],
    
      "asset" : {
        "version" : "2.0"
      }
    }

    Что в итоге?


    В заключении хочу отметить растущую популярность GLTF и GLB форматов, многие компании уже активно используют его, а некоторые уже активно стремятся к этому. Сильно способствует популяризации формата легкость его использования в социальной сети Facebook (3D посты и, с недавних пор, 3D Photos), активное использование GLB в Oculus Home, а также ряд нововведений, которые были озвучены в рамках GDC 2019. Легковесность, быстрая скорость рендеринга, простота использования, продвижение Khronos Group и стандартизация формата – вот главные плюсы, которые, как я уверен, со временем сделают свое дело в дальнейшей его популяризации!

    • +13
    • 2,6k
    • 8
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 8

      0
      Автор публикации зачем-то фрагментарно перевел часть спецификации формата на русский язык. Причем перевёл довольно условно — с использованием странных лингвистических уродцев типа «морф таргет».

      Попытки неподдерживаемых кустарных переводов живых спецификаций в большинстве случаев — это абсолютное зло. Не обижайтесь, но, на мой взгляд, было бы куда интереснее почитать о практических кейсах и подводных камнях (раз уж автор позиционирует себя, как специалист, с личным опытом использования glTF в Facebook, Oculus, UE и Microsoft).
        0
        Видимо вы поняли моя слова о личном опыте как о достаточно глубоком опыте, там же вопрос был о том, где этот формат используется и я ответил, что имел возможность использовать его в тех программах, которые перечислил. Большинство времени я работал с данным форматом в рамках CAD программ, Facebook, Oculus и UE были использованы для проверки корректности импорта специфических случаев.
        Здесь я старался объединить мои знания о практической реализации и несколько документаций, поэтому, с большего, данные статьи выглядят как перевод, хотя это не совсем так. Некоторых «лингвистических уродцев» я решил не переводить как раз из-за того, что в обиходе понятнее использовать именно их, а к примеру слово «сетка», в отличии от «меша», под которым обычно подразумевает «полигональный меш/сетку», может трактоваться не однозначно.
        К практическим случая можно и нужно переходить, но мне кажется рано это делать без рассказа об основах данного формата. К сожалению я не нашёл на территори ру нета такой информации и решил исправить данное упущение, ведь вещь действительно стоящая.
        0
        Написание статьи с использованием жаргонных слов конечно некрасив. Например, в первой части (кстати, а зачем вы разбили статью на две части? — ведь они не так и велики):

        Данный «срез» описывается при помощи 2х пропертей: — проперти — это наверное свойства?
        нода — узел, меш — сетка, translation — это скорее перенос, чем смещение, смещение — это offset и т.д.

        Я понимаю, что так вам привычней, но статья же пишется для того, чтобы её прочитали и, по возможности, поняли, как можно большее число людей, с разными уровнями вовлеченности в описываемую тему.
        Я сам перевожу на русский язык документацию по three.js и понимаю трудности перевода и как нелегко подобрать наиболее точное слово… но у вас все как-то уж «пофигистично».

        И да, переводы нужны и кто их сделает лучше, как не люди с опытом в описывемой сфере. Но, к сожалению, специалисты не стремятся делать переводы, им и так все понятно, и поэтому переводят люди, начинающие осваивать данную тему, так что «ляпы» неизбежны.
          +1
          Переводы технической документации — вообще очень скользкая тема. В том смысле, что очень часто это скорее вредит чем помогает (в том числе из-за трудностей перевода и разночтений в толковании терминов).
          Тут хороший пример — документация и интерфейсы самолетов, которые принципиально никогда не переводят с английского (если мы говорим о гигантах вроде Boeing, Airbus, Bombardier, Embraer).

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

          В целом я двумя руками «за» переводы статей с практическим опытом и резко против кустарных неподдерживаемых переводов спецификаций и документации.
          Но это все мое imho, конечно. А в чем, с вашей точки зрения, необходимость перевода документации того же three.js?
            0
            Мне нужно понять как устроен three.js, вернее, как устроена трехмерная графика. Вариант с библиотекой three.js мне показался наиболее гибким. Я задумал показать графически как протекает электрический ток, т.е. почему он течет, почему у него такая скорость и отчего она такая, почему ток течет по пути наименьшего сопротивления и т.д.
            А для этого нужно показать этот ток (движение заряда) хотя бы в двумерном варианте, т.е. написать детектор столкновений, движок и все остальное. Все имеющиеся русскоязычные руководства в основном «заточены» под создание игр, а у меня более узкая задача и все, встреченные мной описания, как-то обходят самые простые вопросы. Поэтому я взялся за перевод.
            0
            Благодарю за «проперти», это проскочило из моей документации, которую я готовил для внутреннего использования. Сам не фанат такого, плюс не на руку сыграло ещё и то, что я впервые занимаюсь такого рода публикациями. Касательно слов «меш» или «сетка»: как я писал выше, я считаю, что слово «сетка», в отличии от «меша», под которым обычно подразумевает «полигональный меш/сетку», может трактоваться не однозначно, поэтому такие слова я оставлял. Также тому, кто может начать осваивать данную тематику, например 3Д моделирование и работу с данным форматом, будет проще ориентироваться в объектах по их практическому названию, т.е. если он читал инфу по «узлу», а потом в работе встречается «нода», то, по моему мнению, это будет не удобно. И благодарю за ваше мнение!
              –1
              customizer Забыл упомянуть о разбиении статьи: изначально я писал всё в рамках одной, это намного удобнее, но ограничение по коливеству символов не дало мне такой возможности. Статья обрезалась по середине главы **Materials and Textures**
                0
                Как то я не знал про ограничение по количеству символов. Это что-то новенькое. Совсем я от жизни отстал.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое