Vulkan API — урок 50. Samplers

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

Эти фильтры полезны для решения проблем oversampling (чем делать кривой перевод этого слова лучше сразу перейдем к объяснению). Рассмотрим текстуру, которая накладывается на геометрию с большим количеством фрагментов, нежели текселей. Если вы просто будете брать ближайший тексель для координаты, тогда получите результат, как на первом изображении:

Но если скомбинировать 4 ближайших текселя через линейную интерполяцию, тогда вы получите более приемлемый результат (правое изображение), ну а дальше можете сами выбрать более подходящий вариант для конкретного приложения, но в большинстве случаев это все же будет второй. Объект sampler автоматически применяет этот фильтр во время чтения цвета из текстуры.

Есть в корне противоположная проблема – undersampling, когда текселей больше, чем фрагментов. Это приведет к появлению артефактов при просмотре под острым углом паттернов с высокой плотностью, как шахматная доска:

Как показано на левом изображении, текстуру размывает, особенно на расстоянии. Решением проблемы является анизотропная фильтрация, которая так же может автоматически выполняться при помощи упомянутого ранее sampler.

Помимо этих фильтров, sampler может так же позаботиться о преобразованиях. Он определяет что должно произойти, когда вы пытаетесь считать тексели за пределами изображения в режиме адресации. На рисунке показаны некоторые из возможных вариантов:

Теперь создадим функцию createTextureSampler. Будем использовать sampler для чтения цветов из текстуры в шейдер позже.

void initVulkan() {
    ...
    createTextureImage();
    createTextureImageView();
    createTextureSampler();
    ...
}

...

void createTextureSampler() {

}

Для настройки используем структуру, которая определяет все применяемые фильтры фильтры и и преобразования.

Поля magFilter и minFilter определяют как интерполировать увеличенные или уменьшенные тексели. Увеличение (magnification — mag) касается проблемы oversampling, и уменьшение (minification — min) соответственно undersampling. Значения могут быть VK_FILTER_NEAREST и VK_FILTER_LINEAR, соответствующие режимам, продемонстрированным в изображении выше.

VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;

Режим адресации может быть указан для каждой оси в полях addressMode. Большинство из возможных значений уже были показаны на изображении ранее.

  • VK_SAMPLER_ADDRESS_MODE_REPEAT: Повторять текстуру при выходе за границы размеров изображения.
  • VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT: Аналогично первому, но инвертирует координаты, создавая эффект зеркального отражения.
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE: Берет цвет ближайшей координаты изображения.
  • VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE: Аналогично предыдущему, но берет цвет с края противоположного ближайшему.
  • VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER: Возвращает цвет границы.

На самом деле не важно, что сейчас здесь указать, ведь выходить за пределы изображения не будем. Но знать о том, что возможны подобные варианты будет полезно, а repeat наверно наиболее распространенный вариант, т.к. он может быть использован для плиточных текстур, на подобии пола, стен и т.п.

samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;

Обратите внимание, оси называются U, V, W вместо X, Y, Z. Это соглашение для координат пространства текстуры.

Следующие два поля определяют, должна ли использоваться анизотропная фильтрация. Единственная причина отказаться от неё – проблемы с производительностью. Поле maxAnisotropy ограничивает количество текселей, которые будут использоваться для вычисления конечного цвета. Чем ниже значение, тем выше производительность, но и ниже качество. Сегодня практически нет графического железа, которое может использовать значение более 16 (16 samples, по сути – 16 образцов), потому что разница за этой границей несущественна.

samplerInfo.anisotropyEnable = VK_TRUE;
samplerInfo.maxAnisotropy = 16;

Поле borderColor определяет какой цвет будет возвращен, в режиме VK_SAMPLER_ADDRESS_MODE_REPEAT. Можно вернуть черный, белый, прозрачный или иной в формате float или int.

samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;

Поле unnormalizedCoordinates определяет, какую систему координат нужно использовать обращаясь к текселям изображения. Если это поле имеет значение VK_TRUE, тогда можно использовать координаты в диапазоне [0, texWidth] и [0, texHeight]. Если значение VK_FALSE, то тексели используют координаты в диапазоне по всем осям [0, 1]. Обычно используется второй вариант с нормализированными координатами, это позволяет не задумываться о разрешении во время разработки.

samplerInfo.unnormalizedCoordinates = VK_FALSE;

Если функция сравнения включена, тогда тексели сначала будут сравниваться со значением, и результат этого сравнения будет использован в операциях фильтрации. В основном это используется для процентного соотношения близости (percentage-closer filtering) на картах теней.

samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;

Следующие поля относятся к мипмаппингу:

samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;

Итак, теперь sampler полностью определен, пришло время его создать используя vkCreateSampler:

VDeleter textureImageView{device, vkDestroyImageView};
VDeleter textureSampler{device, vkDestroySampler};

...

void createTextureSampler() {
    ...

    if (vkCreateSampler(device, &samplerInfo, nullptr, textureSampler.replace()) != VK_SUCCESS) {
        throw std::runtime_error("failed to create texture sampler!");
    }
}

Обратите внимание, нигде не упомянут VkImage. Все дело в том, что sampler является отдельным объектом, который предоставляет интерфейс для извлечения цвета из текстуры. Он может быть применен для любого изображения, будь то 1D, 2D или 3D. Это идет вразрез с большинством старых API, которые совмещали изображение текстуры и фильтацию в нечто единое.

Main Admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *