glTF в Dagon

В Dagon 0.12 появится начальная поддержка glTF 2.0, популярного формата 3D-моделей от Khronos. На сегодняшний день готова загрузка мешей, текстур и, частично, материалов, на очереди – сцены и узлы, а в перспективе не исключена и поддержка анимации. Ниже – скриншоты дворца Спонца, загруженного из glTF.

Первое знакомство с WGSL

У тех, кто работает с низкоуровневой графикой, сегодня на слуху WebGPU – новый кроссплатформенный API для доступа к возможностям современных видеокарт. WebGPU призван объединить Vulkan, Metal и D3D12 под унифицированным набором функций и станет не просто веб-стандартом, но и, в перспективе, неплохой заменой OpenGL: реализации этого API уже существуют в виде рабочих прототипов wgpu-native от Mozilla и Dawn от Google – любой может использовать их в своих собственных приложениях.

WebGPU имеет сравнительно простую архитектуру, доступную для понимания “простыми смертными” практически с первого прочтения заголовочного файла. Единственной проблемой до недавнего времени было отсутствие консенсуса по шейдерному языку – существующие реализации WebGPU использовали двоичное промежуточное представление SPIR-V от Khronos, а Apple настаивала на текстовом языке на основе WSL. Компромиссом стал WGSL (WebGPU Shading Language), высокоуровневый язык со строго определенной семантикой и буквальной трансляцией в/из SPIR-V. Многие разработчики оказались недовольны этим решением, так как SPIR-V уже успел стать привычным решением и оброс инструментами – сегодня можно компилировать в SPIR-V код на всех языках предыдущих поколений. Однако я вижу больше преимуществ, чем недостатков – перечислю некоторые из них.

  • Использование SPIR-V усложняет жизнь при создании игрового движка, требует внедрения дополнительной стадии компиляции шейдеров на стороне разработчика. Референсным компилятором шейдеров считатеся GLSLang от Khronos, но его довольно трудно встроить в приложение как библиотеку, особенно если вы не пишете на C++ – приходится использовать GLSLang как приложение, и это усложняет тулчейн разработки, если нужна кроссплатформенность. Встроенный в API высокоуровневый язык решает эту проблему.
  • WGSL разрабатывается как текстовый аналог SPIR-V – они имеют общий набор возможностей. Это значит, что не будет повторения ситуации с GLSL, когда язык по-разному обрабатывается в компиляторах от различных поставщиков. Сохраняется главное преимущество SPIR-V при высоком удобстве использования.
  • Vulkan-диалект GLSL 4.60, являющийся де-факто стандартным языком под SPIR-V, имеет множество костылей и архаизмов – у WGSL более продуманный синтаксис, лишенный неявности и многозначности.

Синтаксис WGSL имеет много общего с Rust, особенно заголовки функций:

fn someFunc(x: i32) -> i32 {
    //…
}

Типы объявляются через двоеточие после идентификатора, переменные – при помощи ключевого слова let, константность подразумевается по умолчанию. Для изменяемых переменных есть ключевое слово var. Система типов также пришла из вселенной Rust. Векторные типы имеют форму vec4<f32> (вместо простого vec4), что позволяет явным образом указать битовость используемых чисел.

let a = vec4<f32>(0.0, 0.0, 0.0, 1.0);

При этом можно объявить type vec4f = vec4<f32>; и писать коротко, если вам так привычнее.

Вместо ключевого слова layout – нотация с использованием двойных квадратных скобок, внутри которых записываются атрибуты location и др.:

[[location(0)]] position: vec4<f32>;

Встроенные переменные конвейера обозначаются атрибутом builtin, что весьма удобно при объявлении структур для хранения промежуточных результатов:

struct VertexOutput
{
    [[builtin(position)]] position: vec4<f32>;
    [[location(0)]] color: vec4<f32>;
};

Сравните это с GLSL, где для встроенных переменных используется рарезервированный префикс gl_.

Структуры, являющиеся uniform-блоками, помечаются атрибутом block:

[[block]] struct Uniforms {
    //…
};

Прямым аналогом вулкановских set и binding являются атрибуты group и binding.

Vulkan/GLSL:

layout(set=0, binding=0) uniform Uniforms uniforms;

WGSL:

[[group(0), binding(0)]] var uniforms: Uniforms;

Программы на WGSL можно не разделять на два текста – вершинный и фрагментный шейдеры можно хранить в одном файле и, таким образом, использовать общие объявления. Для этого используется атрибут stage. Названия самих входных точек могут быть произвольными, но чаще всего в примерах используют vs_main и fs_main.

[[stage(vertex)]]
fn vs_main() -> VertexOutput
{
    //...
}

[[stage(fragment)]]
fn fs_main(input: FragmentInput) -> [[location(0)]] vec4<f32>
{
    //...
}

Очень непривычно в WGSL записываются циклы:

var i = i32(0);
loop {
    break if (i == 5);
    //…
    continuing {
        i = i + 1;
    }
}

Впрочем, на момент написания статьи обсуждается возможность поддержки классического for.

Подведу итог: с первого взгляда WGSL кажется хорошим решением давней проблемы с языками шейдеров. Высокоуровневое представление SPIR-V – это отличная идея. Непривычный синтаксис и конструкции со спорным юзабилити могут усложнить портирование на WGSL готовых шейдеров, но в целом впечатление от языка весьма позитивное.

Новые статьи на Medium

Написал две новые статьи на английском:

  • dlib: Past, Present and Future – экскурс в историю dlib, о текущем статусе проекта и планах на будущее;
  • GitHub Actions and D – о том, как настроить тестирование на GitHub Actions для проектов на D.

D в статистике Codecov

Интересная новость: согласно годовому отчету Codecov по анализу покрытия OpenSource-кода, D стал резко популярнее. За 2020 год использование языка возросло в 2.18 раз, он находится на третьем месте по динамике роста, уступая Objective-C и Groovy.

О продаже 3D-моделей

Одно из моих хобби – 3D-моделирование. С 2018 года я создаю модели для продажи на стоковых площадках. Я делаю это не столько ради дохода, сколько ради изучения рынка: веду статистику продаж, осваиваю маркетинг, работу с клиентами и инструментами аналитики. Стоки вообще являются отличным способ попробовать себя в микробизнесе, не уходя с основной работы и практически ничем не рискуя (кроме, разве что, своим свободным временем). А рынок 3D-моделей, как мне кажется, более привлекателен, чем те же фотографии или векторные иллюстрации – здесь не такие высокие комиссии, меньше формальных требований к качеству и заметно более низкая конкуренция, поэтому начать что-то продавать можно с первых же шагов.

Может показаться, что продажа файлов – это пассивный доход, но это не совсем так. Да, популярный продукт может продаваться много раз, однако у любой модели есть некая нефиксированная “продолжительность жизни”, если можно так выразиться: технологии не стоят на месте, из года в год стандарты компьютерной графики повышаются, и старые модели постепенно теряют востребованность. Например, появление физически обоснованного рендеринга (PBR) в real-time графике полностью изменило стоковый рынок: PBR-модели с реалистичными материалами имеют несопоставимое преимущество перед моделями предыдущего поколения. К тому же, чем старше модель, тем глубже она появляется в поисковой выдаче. Необходимо постоянно следить за рынком и создавать новые конкурентоспособные продукты – на это будет уходить много времени и сил, поэтому, если моделирование не является вашей основной деятельностью, и вы относитесь к роялти как к пассивному доходу, то на регулярные продажи рассчитывать не стоит.

Также я понял, что стоки – это не место для чистого творчества. Конечно, вы можете моделировать все, что приходит вам в голову, и любая качественная творческая работа обязательно получит свою порцию лайков и положительных комментов. Но хорошо продаются, в первую очередь, самые обыкновенные, даже скучные вещи: мебель, предметы быта, техника, архитектура – словом, все, что вы видите вокруг себя каждый день. Причина тому простая: покупателями стоковых моделей являются дизайнерские фирмы, которым нужно быстрее выполнять их проекты. Модели, которые они у вас покупают, становятся частью разнообразных производных работ: это дизайн интерьеров, рекламные ролики, иллюстрации для сайтов и изданий, окружение в компьютерных играх и т.д. Задайтесь вопросом, нужен ли ваш уникальный персонаж в рекламном буклете какого-нибудь автосалона? Уникальный контент дизайнерские конторы делают всегда сами, а покупают они все остальное, чтобы сэкономить на них трудозатраты и сосредоточиться на более важных составляющих проекта.

Несомненный плюс стоков – вы можете работать с несколькими маркетплейсами одновременно, выкладывать везде одни и те же продукты. По моему опыту, самая большая клиентская база на сегодняшний день у двух магазинов – CGTrader и TurboSquid. Серьезное преимущество CGTrader – высокий процент роялти, который к тому же повышается в зависимости от набранных вами баллов и достигнутого уровня репутации. Всего есть 13 уровней, а роялти повышается от 70% до 80%. CGTrader награждает авторов качественного и популярного контента: баллы набираются за продажи и лайки ваших моделей, а также за позитивные отклики покупателей и активность на форуме. У TurboSquid базовый процент роялти – 40%. Эта цифра повышается до 60% в зависимости от вашей суммарной прибыли, но только для эксклюзивных партнеров. CGTrader не требует эксклюзивности.

Бесценная фича CGTrader – доступ к аналитике: при достижении определенного уровня репутации можно увидеть самые популярные поисковые запросы, сравнить свои цены со средними по рынку и т.д. CGTrader вычисляет конверсию ваших продуктов – отношение числа покупателей к числу просмотров: зная, какие из ваших продуктов обладают высокой конверсией, вы можете в дальнейшем создавать более востребованные модели. Также всем моделям присваивается рейтинг от 0 до 10 (его видите только вы), зависящий от формальных критериев качества: количества форматов модели, наличия превью-изображений и подробного описания. Этот рейтинг не всегда коррелирует с объемом продаж, но он помогает сравнивать продукты в целях их улучшения.

Еще один полезный показатель – ликвидность категории. Далеко не все категории моделей одинаково хорошо продаются в любое время и в любом месте – это зависит от площадки, ее аудитории и даже от времени года. Существуют столь “замусоренные” конкуренцией категории, что в них лучше вообще не соваться. К сожалению, разобраться в этом с нуля практически невозможно, понимание приходит только с опытом. Рекомендую выкладывать только то, что у вас хорошо получается, и отслеживать, что из этого наиболее востребовано.

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

Отдельный вопрос – в каких форматах выкладывать модель. Какого-то единого стандартного формата, к сожалению, до сих пор нет. Самыми основными считаются OBJ, 3DS, FBX, MAX, STL, DAE. Я считаю, что в обязательном порядке нужно предоставлять модель в собственном формате программы, в которой она сделана. Обменные же форматы зависят от назначения модели. Например, если это игровой ассет, то не лишним будут варианты в виде Unity-пакета и glTF. Модели для 3D-печати – это, безусловно, STL. Такие устаревшие форматы, как 3DS и OBJ, не сохраняют определенные виды информации – например, скелет, развесовку, данные о материалах и т.д. Тем не менее, они популярны, и прилагать их тоже нужно, но они не должны быть единственными. В целом, чем больше выбор форматов, тем лучше для покупателя.

Необходимо стремиться к тому, чтобы модель выглядела одинаково в разных программных средах, поэтому не стоит полагаться на специфические возможности вашего рендер-движка и создавать сложные хитроумные материалы. Вместо этого лучше делать PBR-текстуры (albedo, roughness, metallic) и стандартные карты нормалей в пространстве касательных. Я рекомендую использовать текстуры в разрешении степеней двойки (512, 1024, 2048), так как они лучше всего подходят для игровых движков. Наиболее оптимальное разрешение текстур под PBR – 2048х2048. Текстур не должно быть слишком много, лучше объединять их в атласы. В идеале модель должна иметь всего один атлас – в противном случае могут быть сложности с импортом.

Конечно, на стоках вы много не заработаете, если только уже не являетесь профи с многолетним стажем и известностью в 3D-сообществе. Серьезно зарабатывают, как правило, на регулярных индивидуальных заказах. Но ваш профиль в маркетплейсе даст вам невероятное конкурентное преимущество. Заказчик охотнее пойдет к тому, чью работу можно оценить и пощупать – продукт, имеющий реальный спрос, значительно круче, чем просто картинки в портфолио. Кроме того, вы получаете опыт работы с рынком и представление о том, что нужно потребителю – переоценить это тоже сложно.