Первое знакомство с 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), что позволяет явным образом указать битовость используемых чисел. При этом можно объявить type vec4f = vec4<f32>; и писать коротко, если вам так привычнее.

Кстати, очень порадовало, что есть вывод типов – можно не указывать тип переменной, если она тут же инициализируется:

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

Вместо ключевого слова 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-сообществе. Серьезно зарабатывают, как правило, на регулярных индивидуальных заказах. Но ваш профиль в маркетплейсе даст вам невероятное конкурентное преимущество. Заказчик охотнее пойдет к тому, чью работу можно оценить и пощупать – продукт, имеющий реальный спрос, значительно круче, чем просто картинки в портфолио. Кроме того, вы получаете опыт работы с рынком и представление о том, что нужно потребителю – переоценить это тоже сложно.

Итоги 2020 года

Близится конец года, и это значит, что наступило время для традиционного подведения итогов по проектам.

  • У меня появился домен для личного бренда https://timurgafarov.ru, и, соответственно, блог переехал на новый адрес: https://gamedev.timurgafarov.ru.
  • Вышел Dagon 0.11.0. Движок был значительно улучшен, переработана структура модулей, практически полностью переписан рендер, исправлено множество проблем и узких мест производительности. Посмотреть движок в действии можно при помощи демки dagon-sandbox, а также на моем YouTube-канале. Также были дополнены уроки и примеры.
  • Вышли dlib 0.18, 0.19 и 0.20. У проекта появилась онлайн-документация, генерируемая из исходников при помощи Dub/ddox. В 2021 году dlib исполняется 10 лет!
  • Я опубликовал две новые статьи по D на Medium: Getting started with D и Const-correctness in D, а также небольшую вводную статью по WebGPU на CGWorld.
  • За этот год мне удалось собрать донатов на сумму $172,10. Огромное спасибо всем, кто перечислил деньги! Часть средств пошла на покупку аппаратного обеспечения – в частности, SSD (3590 руб.), наушников (790 руб.), USB-разветвителя (790 руб.), разветвителя для аудио (45,95 руб.). Также был приобретен графический софт: ArtRage (2423,38 руб.) и Armor Paint (1205,78 руб.). На оплату хостинга, на котором размещен этот блог, ушло 1447 руб. Кроме того, был куплен домен timurgafarov.ru за 199 руб. Итого израсходовано 10491,11 руб.

Ну и, конечно, не могу не назвать самые значимые для меня события в мире CG, СПО и геймдева:

  • Выход Blender 2.90 – очень впечатлила новая опция режима редактирования, позволяющая автоматически смещать UV-координаты синхронно с изменением геометрии. В целом Blender 2.80+ в моем восприятии превратился в полноценно рабочий инструмент, я начал использовать его в коммерческих проектах.
  • Выход бесплатной версии Unigine. Скачал, заценил – есть множество интересных фич, в частности понравился live reloading моделей и текстур при их обновлении внешними приложениями. Не понравилось, однако, то, что редактор нельзя запустить без входа в аккаунт (возможно, есть какой-то оффлайн-режим – не искал).
  • Открытие исходников NeoAxis. Пока детально не изучал этот движок, но в целом выглядит привлекательно.
  • Форк Dev-C++ от Embarcadero. До сих пор иногда пользуюсь этой IDE, поэтому новость для меня позитивная.