(Практический пример по этой статье: Фиксированные функции графического конвейера в GLFW)
Часть этапов графического конвейера в Vulkan управляются фиксированными функциями.
Структура VkPipelineVertexInputStateCreateInfo описывает формат данных вершин, которые будут переданы в вершинный шейдер:
typedef struct VkPipelineVertexInputStateCreateInfo {
VkStructureType sType;
const void* pNext;
uint32_t vertexBindingDescriptionCount;
const VkVertexInputBindingDescription* pVertexBindingDescriptions;
uint32_t vertexAttributeDescriptionCount;
const VkVertexInputAttributeDescription* pVertexAttributeDescriptions;
} VkPipelineVertexInputStateCreateInfo;
Структура определяет следующие поля:
sType: тип структуры, который должен представлять значение VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO.
pNext: NULL или указатель на структуру, расширяющую эту структуру.
vertexBindingDescriptionCount: количество описаний привязок вершин в поле pVertexBindingDescriptions
pVertexBindingDescriptions: указатель на массив структур VkVertexInputBindingDescription.
vertexAttributeDescriptionCount: количество описаний атрибутов вершин, предоставленных в поле pVertexAttributeDescriptions.
pVertexAttributeDescriptions: указатель на массив структур VkVertexInputAttributeDescription.
Таким образом, для описания вершин необходимо два компонента:
Привязки (структура VkVertexInputBindingDescription): интервал между данными и устанавливаются ли данные для вершины или для геометрии
Описания атрибутов (структура VkVertexInputBindingDescription): тип атрибутов, переданных в вершинный шейдер, с помощью какой привязки их загружать и по какому смещению
Пример описания формата вершин:
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0; // количество привязок
vertexInputInfo.pVertexBindingDescriptions = nullptr; // необязательно
vertexInputInfo.vertexAttributeDescriptionCount = 0; // количество описаний атрибутов
vertexInputInfo.pVertexAttributeDescriptions = nullptr; // необязательно
Для установки параметров сборки входных данных конвейера применяется структура VkPipelineInputAssemblyStateCreateInfo:
typedef struct VkPipelineInputAssemblyStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPrimitiveTopology topology;
VkBool32 primitiveRestartEnable;
} VkPipelineInputAssemblyStateCreateInfo;
Поля структуры:
sType: тип структуры - значение VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO.
pNext: NULL или указатель на структуру, расширяющую эту структуру.
topology: значение перечисления VkPrimitiveTopology, которое определяет топологию примитива - какой тип геометрии будет нарисован из вершин
primitiveRestartEnable: указывает, следует ли подключить перезапуск сборки примитивов
Итак, поле topology управляет построением геометрического примитива и представляет перечисление VkPrimitiveTopology. ОТмечу его основные значения:
VK_PRIMITIVE_TOPOLOGY_POINT_LIST: отдельные точки из вершин
VK_PRIMITIVE_TOPOLOGY_LINE_LIST: линия из каждых 2 вершин без повторного использования
VK_PRIMITIVE_TOPOLOGY_LINE_STRIP: конечная вершина каждой линии используется как начальная вершина для следующей линии
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: треугольник из каждых 3 вершин без повторного использования
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP: вторая и третья вершина каждого треугольника используются как первые две вершины следующего треугольника
Другое поле - primitiveRestartEnable при установке в VK_TRUE позволяет разбить линии и треугольники в топологических режимах VK_PRIMITIVE_TOPOLOGY_[ТИП]_STRIP,
используя специальный индекс 0xFFFF или 0xFFFFFFFF.
Например, определение входных данных для рисования треугольника выглядело бы следующим образом:
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
Область просмотра представляет область буфера кадра, в которую будет визуализироваться вывод. Она представлена структурой VkViewport:
typedef struct VkViewport {
float x;
float y;
float width;
float height;
float minDepth;
float maxDepth;
} VkViewport;
Поля структуры описывают положение и размеры область просмотра:
x и y — верхний левый угол области просмотра (x,y).
width и height — ширина и высота области просмотра соответственно.
minDepth и maxDepth — диапазон глубины области просмотра.
Например:
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float) swapChainExtent.width;
viewport.height = (float) swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
Обычно область просмотра начинается в точке (0, 0) и растягивается по ширине и высоте. Здесь предполагается, что ширина и высота передаются через переменную swapChainExtent, которая хранит ширину и высоту изображений цепочки буферов.
Значения minDepth и maxDepth указывают диапазон значений глубины для использования в буфере кадра. Эти значения должны быть в диапазоне [0.0f, 1.0f],
но minDepth может быть больше maxDepth. В большинстве случаев можно использовать стандартные значения - 0.0f и 1.0f.
Ножницы (scissors) определяют прямоугольную область, в которой фактически будут храниться пиксели. Ножницы представлены структурой VkRect2D
typedef struct VkRect2D {
VkOffset2D offset;
VkExtent2D extent;
} VkRect2D;
Тут всего два поля:
offset: смещение на изображении, которая попадает в прямоугольник. Представляет значение VkOffset2D
extent: значение VkExtent2D, которое определяет размеры прямоугольника.
Любые пиксели за пределами прямоугольников ножниц будут отброшены растеризатором. То есть ножницы фактически обрезают изображение.
Таким образом, если бы мы хотели рисовать во всем буфере кадра, мы бы указали прямоугольник-ножницы, который покрывает его полностью:
VkRect2D scissor{};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
Для конфигурации области просмотра и ножниц применяется структура VkPipelineViewportStateCreateInfo:
typedef struct VkPipelineViewportStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineViewportStateCreateFlags flags;
uint32_t viewportCount;
const VkViewport* pViewports;
uint32_t scissorCount;
const VkRect2D* pScissors;
} VkPipelineViewportStateCreateInfo;
Структура определяет следующие поля:
sType: тип структуры, должен представлять значение VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO.
pNext: NULL или указатель на структуру, расширяющую эту структуру.
flags: не используется, зарезервирован для будущего использования.
viewportCount: количество областей просмотра, используемых конвейером.
pViewports: указатель на массив структур VkViewport, определяющих преобразования области просмотра. Если состояние области просмотра динамическое, этот элемент игнорируется.
scissorCount: количество ножниц, которое должно соответствовать количеству областей просмотра.
pScissors: указатель на массив структур VkRect2D, определяющих прямоугольные границы ножниц для соответствующей области просмотра. Если состояние ножниц динамическое, этот элемент игнорируется.
Область просмотра и прямоугольники ножниц могут быть указаны либо как статическая часть конвейера, либо как динамическое состояние, установленное в буфере команд. При динамической установки области просмотра и ножниц необходимо включить соответствующие динамические состояния для конвейера с помощью структуры VkPipelineDynamicStateCreateInfo. Ее основные поля:
dynamicStateCount: количество элементов в массиве pDynamicStates.
pDynamicStates: указатель на массив значений VkDynamicState, указывающий, какие части состояния конвейера будут использовать значения из динамического состояния
Пример определения динамического состояния конвейера:
std::vector<VkDynamicState> dynamicStates = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState{};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
dynamicState.pDynamicStates = dynamicStates.data();
А затем надо указать их количество во время создания конвейера:
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
Фактические же область просмотра и ножницы будут настроены позднее во время рисования.
При определении области просмотра и ножниц как статической части конвейера их надо установить в конвейере с помощью структуры VkPipelineViewportStateCreateInfo:
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
Это делает область просмотра и прямоугольник ножниц для этого конвейера неизменяемыми. Любые изменения, требуемые для этих значений, потребуют создания нового конвейера с новыми значениями.
Растеризатор (rasterizer) берет геометрию, сформированную вершинами из вершинного шейдера, и превращает ее во фрагменты, которые будут раскрашены фрагментным шейдером. Он также выполняет проверку глубины, отбраковку граней и т.д. Его можно настроить на вывод фрагментов, заполняющих целые полигоны или только края (каркасный рендеринг). Все это настраивается с помощью структуры VkPipelineRasterizationStateCreateInfo:
typedef struct VkPipelineRasterizationStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineRasterizationStateCreateFlags flags;
VkBool32 depthClampEnable;
VkBool32 rasterizerDiscardEnable;
VkPolygonMode polygonMode;
VkCullModeFlags cullMode;
VkFrontFace frontFace;
VkBool32 depthBiasEnable;
float depthBiasConstantFactor;
float depthBiasClamp;
float depthBiasSlopeFactor;
float lineWidth;
} VkPipelineRasterizationStateCreateInfo;
Поля структуры:
sType: тип структуры, должен быть равен VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO
pNext: NULL или указатель на структуру, расширяющую эту структуру.
flags: не используется и зарезервировано для будущего использования.
depthClampEnable управляет тем, следует ли фрагментам ограничивать значения глубины
Если depthClampEnable установлено в VK_TRUE, то фрагменты, которые находятся за пределами ближней и дальней плоскостей, прикрепляются к ним, а не отбрасываются.
Это полезно, например, для карт теней. Для использования этого требуется включение функции GPU.
rasterizerDiscardEnable: управляет тем, будут ли примитивы отбрасываться непосредственно перед этапом растеризации.
Если rasterizerDiscardEnable установлен на VK_TRUE, то геометрия никогда не проходит через стадию растеризации. Это по сути отключает любой вывод в буфер кадра.
polygonMode: режим рендеринга треугольников. Представляет перечисление VkPolygonMode со следующими значениями:
VK_POLYGON_MODE_FILL: заполняет область полигона фрагментами
VK_POLYGON_MODE_LINE: ребра полигона рисуются как линии
VK_POLYGON_MODE_POINT: вершины полигона рисуются как точки
cullMode: определяет тип отбраковки граней, который следует использовать. Может принимать следующие значения:
VK_CULL_MODE_NONE: ни один треугольник не отбрасывается
VK_CULL_MODE_FRONT_BIT: передние грани отбрасываются
VK_CULL_MODE_BACK_BIT: задние грани отбрасываются
VK_CULL_MODE_FRONT_AND_BACK: передние и задние грани отбрасываются
frontFace: определяет, является ли треугольник лицевой (обращен вперед) или тыльной стороной (обращен назад). Определяет порядок вершин для граней, которые следует считать обращенными вперед. Принимает следующие значения:
VK_FRONT_FACE_COUNTER_CLOCKWISE: треугольник с положительной площадью считается обращенным вперед.
VK_FRONT_FACE_CLOCKWISE: треугольник с отрицательной площадью считается обращенным вперед.
depthBiasEnable: управляет смещением значений глубины фрагмента
depthBiasConstantFactor: скалярный коэффициент, управляющий постоянным значением глубины, добавляемым к каждому фрагменту. Необязательное поле
depthBiasClamp: максимальный (или минимальный) сдвиг глубины фрагмента. Необязательное поле
depthBiasSlopeFactor: скалярный коэффициент, применяемый к наклону фрагмента при расчетах смещения глубины.
lineWidth: описывает толщину линий в терминах количества фрагментов. Максимальная поддерживаемая ширина линии зависит от оборудования, и любая линия толще 1.0f требует включения функции wideLines GPU.
Пример определения растеризатора:
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
rasterizer.depthBiasConstantFactor = 0.0f; // Необязательное поле
rasterizer.depthBiasClamp = 0.0f; // Необязательное поле
rasterizer.depthBiasSlopeFactor = 0.0f; // Необязательное поле
Мультисэмплинг (Multisampling) представляет один из способов сглаживания. Для его настройки применяется структура VkPipelineMultisampleStateCreateInfo:
typedef struct VkPipelineMultisampleStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineMultisampleStateCreateFlags flags;
VkSampleCountFlagBits rasterizationSamples;
VkBool32 sampleShadingEnable;
float minSampleShading;
const VkSampleMask* pSampleMask;
VkBool32 alphaToCoverageEnable;
VkBool32 alphaToOneEnable;
} VkPipelineMultisampleStateCreateInfo;
Поля структуры:
sType: тип структуры, должен быть равен VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO
pNext: NULL или указатель на структуру, расширяющую эту структуру.
flags: не используется и зарезервировано для будущего использования.
rasterizationSamples: значение VkSampleCountFlagBits, указывающее количество выборок, используемых при растеризации. Принимает следующие значения:
VK_SAMPLE_COUNT_1_BIT: изображение с одним образцом на пиксель.
VK_SAMPLE_COUNT_2_BIT: изображение с 2 образцами на пиксель.
VK_SAMPLE_COUNT_4_BIT: изображение с 4 образцами на пиксель.
VK_SAMPLE_COUNT_8_BIT: изображение с 8 образцами на пиксель.
VK_SAMPLE_COUNT_16_BIT: изображение с 16 образцами на пиксель.
VK_SAMPLE_COUNT_32_BIT: изображение с 32 образцами на пиксель.
VK_SAMPLE_COUNT_64_BIT: изображение с 64 образцами на пиксель.
sampleShadingEnable: указывает, надо ли подключать технику Sample Shading.
minSampleShading: указывает на минимальную долю выборочной заливки, если sampleShadingEnable равно VK_TRUE.
pSampleMask: указатель на массив значений VkSampleMask, используемых в тесте маски выборки.
alphaToCoverageEnable: управляет тем, генерируется ли временное значение покрытия на основе альфа-компонента первого цветового вывода фрагмента
alphaToOneEnable управляет тем, заменяется ли альфа-компонент первого цветового вывода фрагмента.
Мультисэмплинг работает путем объединения результатов фрагментного шейдера, которые растеризуются в один и тот же пиксель. Это в основном происходит вдоль краев, где также возникают наиболее заметные артефакты сглаживания. Для его включения требуется включение функции GPU. Пример настройки мультисэмплинга:
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
multisampling.minSampleShading = 1.0f; // Необязательное поле
multisampling.pSampleMask = nullptr; // Необязательное поле
multisampling.alphaToCoverageEnable = VK_FALSE; // Необязательное поле
multisampling.alphaToOneEnable = VK_FALSE; // Необязательное поле
После того, как фрагментный шейдер возвратил цвет, его необходимо объединить с цветом, который уже находится в буфере кадра. Это преобразование известно как color blending или цветовой переход (еще называют смешиванием цветов). Для настройки цветового перехода есть два типа структур: VkPipelineColorBlendAttachmentState и VkPipelineColorBlendStateCreateInfo.
Структура VkPipelineColorBlendAttachmentState содержит конфигурацию для каждого прикрепленного буфера кадров
typedef struct VkPipelineColorBlendAttachmentState {
VkBool32 blendEnable;
VkBlendFactor srcColorBlendFactor;
VkBlendFactor dstColorBlendFactor;
VkBlendOp colorBlendOp;
VkBlendFactor srcAlphaBlendFactor;
VkBlendFactor dstAlphaBlendFactor;
VkBlendOp alphaBlendOp;
VkColorComponentFlags colorWriteMask;
} VkPipelineColorBlendAttachmentState;
Поля структуры:
blendEnable управляет включением смешивания для соответствующего цветового прикрепления.
Цветовое прикрепление (color attachment) — это текстура, которая прикрепляется к буферу кадра в качестве цели рендеринга, используемой для внеэкранного рендеринга. Цветовые прикрепления используются в таких техниках, какотражение, преломление и отложенное затенение.
И если смешивание не включено, цвет исходного фрагмента для этого прикрепления передается без изменений.
srcColorBlendFactor: выбирает, какой коэффициент смешивания используется для определения исходных факторов (Sr,Sg,Sb).
dstColorBlendFactor: выбирает, какой коэффициент смешивания используется для определения целевых факторов (Dr,Dg,Db).
colorBlendOp: выбирает, какая операция смешивания используется для расчета значений RGB для записи в цветовое прикрепление.
srcAlphaBlendFactor: выбирает, какой коэффициент смешивания используется для определения исходного фактора Sa.
dstAlphaBlendFactor: выбирает, какой коэффициент смешивания используется для определения целевого фактора Da.
alphaBlendOp: выбирает, какая операция смешивания используется для расчета значений альфа для записи в цветовое прикрепление.
colorWriteMask: битовая маска VkColorComponentFlagBits, указывающая, какие компоненты R, G, B и/или A разрешены для записи. Может принимать следующие значения:
VK_COLOR_COMPONENT_R_BIT указывает, что значение R записывается в цветовую вставку для соответствующего образца. В противном случае значение в памяти остается неизменным.
VK_COLOR_COMPONENT_G_BIT указывает, что значение G записывается в цветовую вставку для соответствующего образца. В противном случае значение в памяти остается неизменным.
VK_COLOR_COMPONENT_B_BIT указывает, что значение B записывается в цветовую вставку для соответствующего образца. В противном случае значение в памяти остается неизменным.
VK_COLOR_COMPONENT_A_BIT указывает, что значение A записывается в цветовую вставку для соответствующего образца. В противном случае значение в памяти остается неизменным.
Если blendEnable установлен в VK_FALSE, то новый цвет из фрагментного шейдера передается без изменений. В противном случае выполняются две операции смешивания для
вычисления нового цвета. Результирующий цвет подвергается операции AND с colorWriteMask для определения того, какие каналы фактически передаются. Псевдокод смешения цветов:
if (blendEnable) {
finalColor.rgb = (srcColorBlendFactor * newColor.rgb) <colorBlendOp> (dstColorBlendFactor * oldColor.rgb);
finalColor.a = (srcAlphaBlendFactor * newColor.a) <alphaBlendOp> (dstAlphaBlendFactor * oldColor.a);
} else {
finalColor = newColor;
}
finalColor = finalColor & colorWriteMask;
Где oldColor - старый цвет, newColor - новый цвет, finalColor - это финальный цвет, получаемый в результате смешения oldColor и newColor
ПРимер создания цветового перехода для одного буфера кадров:
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; // Необязательное поле
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; // Необязательное поле
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Необязательное поле
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; // Необязательное поле
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; // Необязательное поле
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Необязательное поле
Наиболее распространенный способ использования смешивания цветов — это реализация альфа-смешивания, когда мы хотим, чтобы новый цвет был смешан со старым цветом на основе его непрозрачности. Затем finalColor должен быть вычислен следующим образом:
finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor; finalColor.a = newAlpha.a;
Для этого мы можем настроить структуру следующим образом:
colorBlendAttachment.blendEnable = VK_TRUE; colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
VkPipelineColorBlendStateCreateInfo определяет глобальные настройки смешивания цветов. Она ссылается на массив структур для всех буферов кадра и позволяет устанавливать константы смешивания, которые можно использовать в качестве факторов смешивания в вышеупомянутых вычислениях
typedef struct VkPipelineColorBlendStateCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineColorBlendStateCreateFlags flags;
VkBool32 logicOpEnable;
VkLogicOp logicOp;
uint32_t attachmentCount;
const VkPipelineColorBlendAttachmentState* pAttachments;
float blendConstants[4];
} VkPipelineColorBlendStateCreateInfo;
Поля структуры:
sType: тип структуры, должен иметь значение VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO.
pNext: NULL или указатель на структуру, расширяющую эту структуру.
flags: битовая маска VkPipelineColorBlendStateCreateFlagBits, которая содержит дополнительную информацию для смешивания цветов.
logicOpEnable: управляет применением логических операций.
logicOp: выбирает, какую логическую операцию применять.
attachmentCount: количество элементов VkPipelineColorBlendAttachmentState в pAttachments.
pAttachments: указатель на массив структур VkPipelineColorBlendAttachmentState, которые определяют состояние смешивания для каждого цветового прикрепления.
blendConstants: указатель на массив из четырех цветовых компонентов R, G, B и A
ПРимер создания структуры:
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Необязательное поле
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment; // ранее созданная структура VkPipelineColorBlendAttachmentState
colorBlending.blendConstants[0] = 0.0f; // Необязательное поле
colorBlending.blendConstants[1] = 0.0f; // Необязательное поле
colorBlending.blendConstants[2] = 0.0f; // Необязательное поле
colorBlending.blendConstants[3] = 0.0f; // Необязательное поле
Макет конвейреа позволяет определить некоторые константы для передачи динамических значений в шейдеры. Макет конвейера представляет непрозрачный указатель (opaque handle), для создания которого применяется функция vkCreatePipelineLayout:
VkResult vkCreatePipelineLayout(
VkDevice device,
const VkPipelineLayoutCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkPipelineLayout* pPipelineLayout);
В качестве параметров функция принимает:
device: логическое устройство, которое создает схему конвейера.
pCreateInfo: указатель на структуру VkPipelineLayoutCreateInfo, которая определяет конфигурацию макета конвейера.
pAllocator: аллокатор памяти хоста
pPipelineLayout: указатель на дескриптор VkPipelineLayout, в котором возвращается созданный макет конвейера.
Для создания макета необходима конфигурация, которая определяется структурой
typedef struct VkPipelineLayoutCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineLayoutCreateFlags flags;
uint32_t setLayoutCount;
const VkDescriptorSetLayout* pSetLayouts;
uint32_t pushConstantRangeCount;
const VkPushConstantRange* pPushConstantRanges;
} VkPipelineLayoutCreateInfo;
Поля структуры:
sType: тип структуры, должен иметь значение VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO.
pNext: NULL или указатель на структуру, расширяющую эту структуру.
flags: битовая маска VkPipelineLayoutCreateFlagBits, которая содержит параметры для создания макета конвейерав.
setLayoutCount: количество наборов дескрипторов, включенных в макет конвейера.
pSetLayouts: указатель на массив объектов VkDescriptorSetLayout.
pushConstantRangeCount: количество диапазонов констант, включенных в макет конвейера.
pPushConstantRanges: это указатель на массив структур VkPushConstantRange - набор диапазонов констант для использования в макете конвейера.
Пример создания макета конвейера:
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // Необязательное поле
pipelineLayoutInfo.pSetLayouts = nullptr; // Необязательное поле
pipelineLayoutInfo.pushConstantRangeCount = 0; // Необязательное поле
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Необязательное поле
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("Не удалось создать макет конвейера!");
}
После заврешения работы с макетом конвейера его надо удалить с помощью функции vkDestroyPipelineLayout():
void vkDestroyPipelineLayout(
VkDevice device,
VkPipelineLayout pipelineLayout,
const VkAllocationCallbacks* pAllocator);
(Практический пример по этой статье: Фиксированные функции графического конвейера в GLFW)