Данная статья является продолжением рассмотра основ GLTF и GLB форматов. Вы можете найти первую часть статьи здесь. В первой части мы рассмотрели с вами зачем изначально планировался формат, а также такие артефакты и их атрибуты GLTF формата как Scene, Node, Buffer, BufferView, Accessor и Mesh. В данной же статье мы рассмотрим Material, Texture, Animations, Skin, Camera, а также закончим формировать минимальный валидный GLTF файл.
Material и Texture
С мешем неразрывно связаны материалы и текстуры. При необходимости меш может быть анимирован. Материал хранит информацию о том, как модель будет отрендерена движком. GLTF определяет материалы, используя общий набор параметров, которые основаны на Physical-Based Rendering (PBR). PBR модель позволяет создавать “физически корректное” отображение объекта в разных световых условиях благодаря тому, что шейдинговая модель должна работать с “физическими” свойствами поверхности. Есть несколько способов описания PBR. Самая распространенная модель — это metallic-roughness model, которая и используется по умолчанию в GLTF. Также можно использовать и specular-glosiness модель, но только при помощи отдельного расширения (extenstion). Основные атрибуты материала следующие:
- name — имя меша.
- baseColorFactor/baseColorTexture — хранит инфомрацию о цвете. В случае атрибута Factor информация хранится в числовом значении для RGBA, в случае Texture — хранится ссылка на текстуру в объекте textures.
- metallicFactor — хранит информцию о Metallic
- roughnessFactor — хранит информцию об Roughness
- 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 следующие:
- name — название анимации (если таковое имеется)
- channel — массив, который соединяет выходные значения ключевых кадров анимации с определённой нодой в иерархии.
- sampler — атрибут, который ссылающийся на Accessor, который обрабатывает ключевые кадры (key frames) из буфера.
- target — это объект, который определяет какую ноду (объект Node) нужно анимировать используя атрибут node, а также какое свойство ноды нужно анимировать используя атрибут path — translation, rotation, scale, weights и т.п. Неанимированные атрибуты сохраняют свои значения во время анимаций. Если node не определена, то атрибут channel желательно опустить.
- 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
}
]
Основные атрибуты:
- name — название скиннинга
- inverseBindMatrices — указывает на номер акссессора, хранящего информацию об Inverse Bind Matrix
- joints — указывает на номер акссессора, храняшего информацию о суставах/joints
- 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:
- name — название скиннинга
- type — тип камеры, perspective или orthographic.
- perspective/orthographic — атрибут, содержащий детали соответствущего type значения
- aspectRatio — Соотношение сторон экрана (fov).
- yfov — угол вертикального поля зрения (fov) в радианах
- zfar — расстояние до дальней плоскости отсечения (clipping plane)
- znear — расстояние до ближней плоскости отсечения
- 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 и стандартизация формата – вот главные плюсы, которые, как я уверен, со временем сделают свое дело в дальнейшей его популяризации!
Комментарии (8)
customizer
16.04.2019 16:50+1Написание статьи с использованием жаргонных слов конечно некрасив. Например, в первой части (кстати, а зачем вы разбили статью на две части? — ведь они не так и велики):
Данный «срез» описывается при помощи 2х пропертей: — проперти — это наверное свойства?
нода — узел, меш — сетка, translation — это скорее перенос, чем смещение, смещение — это offset и т.д.
Я понимаю, что так вам привычней, но статья же пишется для того, чтобы её прочитали и, по возможности, поняли, как можно большее число людей, с разными уровнями вовлеченности в описываемую тему.
Я сам перевожу на русский язык документацию по three.js и понимаю трудности перевода и как нелегко подобрать наиболее точное слово… но у вас все как-то уж «пофигистично».
И да, переводы нужны и кто их сделает лучше, как не люди с опытом в описывемой сфере. Но, к сожалению, специалисты не стремятся делать переводы, им и так все понятно, и поэтому переводят люди, начинающие осваивать данную тему, так что «ляпы» неизбежны.SobakaRU
16.04.2019 17:15+1Переводы технической документации — вообще очень скользкая тема. В том смысле, что очень часто это скорее вредит чем помогает (в том числе из-за трудностей перевода и разночтений в толковании терминов).
Тут хороший пример — документация и интерфейсы самолетов, которые принципиально никогда не переводят с английского (если мы говорим о гигантах вроде Boeing, Airbus, Bombardier, Embraer).
В IT-сфере проблемы переводов документации упираются во-первых в трудности с поддержкой (ситуация «спека поменялась, а перевод актуализировать некому»), а во-вторых в тот факт, что большая часть описанного практического опыта чаще всего лежит в англоязычном пласте информации. Как следствие, специалистам проще оперировать английской терминологией и переводы выглядят чужеродно и искуственно.
В целом я двумя руками «за» переводы статей с практическим опытом и резко против кустарных неподдерживаемых переводов спецификаций и документации.
Но это все мое imho, конечно. А в чем, с вашей точки зрения, необходимость перевода документации того же three.js?customizer
16.04.2019 18:53Мне нужно понять как устроен three.js, вернее, как устроена трехмерная графика. Вариант с библиотекой three.js мне показался наиболее гибким. Я задумал показать графически как протекает электрический ток, т.е. почему он течет, почему у него такая скорость и отчего она такая, почему ток течет по пути наименьшего сопротивления и т.д.
А для этого нужно показать этот ток (движение заряда) хотя бы в двумерном варианте, т.е. написать детектор столкновений, движок и все остальное. Все имеющиеся русскоязычные руководства в основном «заточены» под создание игр, а у меня более узкая задача и все, встреченные мной описания, как-то обходят самые простые вопросы. Поэтому я взялся за перевод.
BloodyPoSTaL Автор
16.04.2019 18:16Благодарю за «проперти», это проскочило из моей документации, которую я готовил для внутреннего использования. Сам не фанат такого, плюс не на руку сыграло ещё и то, что я впервые занимаюсь такого рода публикациями. Касательно слов «меш» или «сетка»: как я писал выше, я считаю, что слово «сетка», в отличии от «меша», под которым обычно подразумевает «полигональный меш/сетку», может трактоваться не однозначно, поэтому такие слова я оставлял. Также тому, кто может начать осваивать данную тематику, например 3Д моделирование и работу с данным форматом, будет проще ориентироваться в объектах по их практическому названию, т.е. если он читал инфу по «узлу», а потом в работе встречается «нода», то, по моему мнению, это будет не удобно. И благодарю за ваше мнение!
BloodyPoSTaL Автор
16.04.2019 18:19-1customizer Забыл упомянуть о разбиении статьи: изначально я писал всё в рамках одной, это намного удобнее, но ограничение по коливеству символов не дало мне такой возможности. Статья обрезалась по середине главы **Materials and Textures**
customizer
16.04.2019 18:59Как то я не знал про ограничение по количеству символов. Это что-то новенькое. Совсем я от жизни отстал.
SobakaRU
Автор публикации зачем-то фрагментарно перевел часть спецификации формата на русский язык. Причем перевёл довольно условно — с использованием странных лингвистических уродцев типа «морф таргет».
Попытки неподдерживаемых кустарных переводов живых спецификаций в большинстве случаев — это абсолютное зло. Не обижайтесь, но, на мой взгляд, было бы куда интереснее почитать о практических кейсах и подводных камнях (раз уж автор позиционирует себя, как специалист, с личным опытом использования glTF в Facebook, Oculus, UE и Microsoft).
BloodyPoSTaL Автор
Видимо вы поняли моя слова о личном опыте как о достаточно глубоком опыте, там же вопрос был о том, где этот формат используется и я ответил, что имел возможность использовать его в тех программах, которые перечислил. Большинство времени я работал с данным форматом в рамках CAD программ, Facebook, Oculus и UE были использованы для проверки корректности импорта специфических случаев.
Здесь я старался объединить мои знания о практической реализации и несколько документаций, поэтому, с большего, данные статьи выглядят как перевод, хотя это не совсем так. Некоторых «лингвистических уродцев» я решил не переводить как раз из-за того, что в обиходе понятнее использовать именно их, а к примеру слово «сетка», в отличии от «меша», под которым обычно подразумевает «полигональный меш/сетку», может трактоваться не однозначно.
К практическим случая можно и нужно переходить, но мне кажется рано это делать без рассказа об основах данного формата. К сожалению я не нашёл на территори ру нета такой информации и решил исправить данное упущение, ведь вещь действительно стоящая.