Основы формата 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 и стандартизация формата — вот главные плюсы, которые, как я уверен, со временем сделают свое дело в дальнейшей его популяризации!

© Habrahabr.ru