January 15

Система материалов, часть 5

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


Описания материалов хранятся в файлах с произвольным расширением и в произвольной папке (это настраивается пользователем). Для чего это нужно станет понятно далее. Каждый файл описания материалов может содержать в себе только произвольное количество материалов, а имена самих скриптовых файлов ни на что не влияют. Но при этом вы конечно можете придерживаться практики "один материал - один файл".


Технически каждый материал представляет собой именованный блок фигурных скобок, такого плана:

"my_material_name"
{
}

Имя материала может быть опционально заключено в кавычки, хотя явным образом этого и не требуется. Скорее вопрос безопасности, если в имени материала попадётся какая-нибудь фигурная скобка или пробел (известная проблема ещё со времён GoldSrc).

Имя материала выступает связующим звеном между геометрией и настройками рендеринга. Для каждого типа моделей существует так же секция материала "по умолчанию". Для сцены эта секция называется "mod_static" для моделей со скелетной анимацией - "mod_studio", для спрайтов "mod_sprite" и.т.д.

Все имена вы сможете узнать из документации по материал-системе, однако полный объем документации (только по материал-системе) составляет более 100 килобайт чистого текста, поэтому я, разумеется, не буду приводить его здесь. Это просто для примера, чтобы вы могли лучше понять концепцию. Дефолтные секции существуют в единственном экземпляре на всю игру, а пользовательские - без ограничений. Таким образом, при загрузке материал формируется путём объединения двух секций - пользовательской и дефолтной.

В принципе система материалов допускает отсутствие пользовательской части - тогда материал формируется из дефолтной. Но допускается и отсутствие дефолтной части - тогда вам придётся каждый раз в каждый материал заново копировать то, что могло бы находится в дефолтной части.

Так же есть техники (techniques), которые, если проводить аналогию с языком программирования больше всего напоминают функции. Их можно переиспользовать для разных материалов.
Внутри секций можно вызывать различные встроенные команды, которые могут быть использованы в качестве подсказок как на этапе компиляции, так и на этапе рендеринга. Так например команда

addCompileFlags( C_NOLIGHTMAP );

сообщает компилятору, что мы не хотим *рассчитывать* лайтмапу для этой поверхности и следовательно - тратить на это время. Чтобы просто игнорировать уже рассчитанную лайтмапу у нас будет масса других способов.

Кроме встроенных функций вызовов, мы так же можем объявлять переменные различных типов, обычно это float, int, vec2, vec3, vec4, mat3, mat4 и image. Последний используется для объявления текстурных юнитов. Имена объявленных переменных будут автоматически доступны в шейдере, который будет связан с текущим материалом.
Примеры объявления переменных:

  vec2 u_LightmapOffset = "entity->lightmapOffset";
  mat4 u_ModelMatrix = "entity->transform";
  mat4 u_ModelViewMatrix = "render->modelViewMatrix";
  mat4 u_ModelViewProjectionMatrix = "render->modelViewProjectionMatrix";
  ivec4 u_DynamicLights = "entity->dynamicLights";
  vec4 u_RenderColor = "entity->renderColor";
  vec3 u_ViewForward = "render->viewForward";
  vec3 u_ViewOrigin = "render->viewOrigin";
  vec2 u_DiffuseSize = u_ColorMap->size;
  vec2 u_DetailScale = vec2( 1.0f, 1.0f );
  float u_RealTime = "globals->time";

Примеры объявления текстурных юнитов:

  image u_ColorMap = "globals->$WhiteTexture";  // fallback texture
  image u_StyleMap = "entity->$StylemapTexture";
  image u_FogTable = "globals->$FogTexture";
  image u_LightMapA = "entity->$AddLightA";
  image u_LightMapB = "entity->$AddLightB";
  image u_LightMapC = "entity->$AddLightC";
  image u_LightMapD = "entity->$AddLightD";
  image u_DebugLightMap = "entity->$DebugLightmap";
  image u_WorldLights = "globals->$WorldLights";
  image u_GlobalMap = "globals->$BlackTexture";
  image u_LayerMap = "globals->$BlackTexture";
  image u_FlowMap = "entity->$FlowMap";

Продолжение следует