Vulkan API — урок 33. Формат вершин

Следующий шаг – сказать Vulkan’у как передать этот формат данных в vertex shader, когда он будет загружен в память GPU. Для этого необходимо заполнить два типа структур.

Binding descriptions

Первая – VkVertexInputBindingDescription, и для того, что бы заполнить её правильными данными, добавим новую функцию в структуру Vertex.

struct Vertex {
    glm::vec2 pos;
    glm::vec3 color;

    static VkVertexInputBindingDescription getBindingDescription() {
        VkVertexInputBindingDescription bindingDescription = {};

        return bindingDescription;
    }
};

Vertex binding (связка вершин) определяет способ загрузки данных из памяти, для всех вершин. Он описывает количество байт между началом каждой записи и переходит к следующей записи после каждой вершины или каждого экземпляра (instance).

VkVertexInputBindingDescription bindingDescription = {};
bindingDescription.binding = 0;
bindingDescription.stride = sizeof(Vertex);
bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;

Все наши данные по вершинам пакуются в один массив, так что мы имеем дело только лишь с одной «связкой» (binding).

Параметр binding указывает индекс binding в общем массиве «связок» (bindings).

Параметр stride указывает количество бай от начала до конца одного элемента.

Параметр inputRate может иметь одно из двух значений:

  • VK_VERTEX_INPUT_RATE_VERTEX: Переход к следующему элементу после каждой вершины
  • VK_VERTEX_INPUT_RATE_INSTANCE: Переход к следующему элементу после каждого экземпляра

Мы не используем instanced rendering, так что остановим выбор на первом варианте.

Attribute descriptions

Вторая структура, описывающая как обрабатывать получаемые вершины – VkVertexInputAttributeDescription. Добавим еще одну вспомогательную функцию в структуру.

#include <array>

...

static std::array<VkVertexInputAttributeDescription, 2> getAttributeDescriptions() {
    std::array<VkVertexInputAttributeDescription, 2> attributeDescriptions = {};

    return attributeDescriptions;
}

Структура Attribute descriptions (описание атрибута) описывает как извлечь атрибут вершины из кучи данных вершин, которые в свою очередь происходят из binding description.

Параметр binding сообщает Vulkan’у, откуда брать данные binding, что логично.

Параметр location ссылается на локальную директиву ввода в vertex shader. Вход vertex shader  с location 0 – позиция, имеющая два 32-битных float компонента.

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

  • float: VK_FORMAT_R32_SFLOAT
  • vec2: VK_FORMAT_R32G32_SFLOAT
  • vec3: VK_FORMAT_R32G32B32_SFLOAT
  • vec4: VK_FORMAT_R32G32B32A32_SFLOAT

Как вы уже наверно поняли, нужно использовать формат, где количество цветовых каналов соответствует количеству компонентов в типе данных шейдера. Это позволяет использовать больше каналов, чем число компонентов в шейдере, но они будут просто отбрасываться. Если количество каналов меньше, чем количество компонентов, тогда компоненты BGA (не GBA) по умолчанию будут иметь значения (0, 0, 1). Тип цвета (SFLOAT, UINT, SINT) и разрядность должны так же соответствовать типу ввода шейдера. К примеру:

  • ivec2: VK_FORMAT_R32G32_SINT, двукомпонентный вектор, 32-бита, знаковый, целочисленный
  • uvec4: VK_FORMAT_R32G32B32A32_UINT, четырехкомпонентный вектор, 32-бита, бесзнаковый, целочисленный
  • double: VK_FORMAT_R64_SFLOAT, двойная точность (64-bit), с плавающей точкой

Параметр format определяет размер в байтах данных атрибутов и параметра смещения. Это автоматически рассчитывается при помощи offsetof макроса.

Атрибут цвета описывается аналогичным способом:

attributeDescriptions[1].binding = 0;
attributeDescriptions[1].location = 1;
attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
attributeDescriptions[1].offset = offsetof(Vertex, color);

Подача вершин на конвейер

Теперь нужно настроить графический конвейер на примем данных вершин в заданном формате, используя ссылки на структуры в createGraphicsPipeline. Найдем структуру vertexInputInfo и модифицируем её следующим образом:

auto bindingDescription = Vertex::getBindingDescription();
auto attributeDescriptions = Vertex::getAttributeDescriptions();

vertexInputInfo.vertexBindingDescriptionCount = 1;
vertexInputInfo.vertexAttributeDescriptionCount = attributeDescriptions.size();
vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();

Конвейер готов к приему вершин в заданном формате контейнера вершин и передачи его на vertex shader. Но еще нам нужно создать буфер вершин и переместить данные вершин в него так, что бы GPU получила к нему доступ.

Main Admin

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

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