技术标签: vulkan
其他图形API为图形管道的大多数阶段提供了默认状态。但在Vulkan中,必须明确所有内容,从视口大小到颜色混合功能。
接下来我们试着填写配置这些固定功能操作的所有结构。
VkPipelineVertexInputStateCreateInfo结构描述将传递给顶点着色器的顶点数据的格式。
它以两种方式描述了这一点:
因为我们直接在顶点着色器中对顶点数据进行硬编码,所以我们将填充此结构以指定现在没有要加载的顶点数据。
void createGraphicsPipeline() {
......
// 这里为管道指定着色器
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
// 创建顶点着色器的数据输入
VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
vertexInputInfo.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr; // Optional
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr; // Optional
......
}
pVertexBindingDescriptions和pVertexAttributeDescriptions 成员指向一个结构数组,描述前面提到的加载顶点数据的细节。
后续在学习顶点缓冲的时候详细分析。
VkPipelineInputAssemblyStateCreateInfo结构描述了两件事:
通常,顶点缓冲区按顺序从顶点缓冲区加载,但是使用元素缓冲区可以指定要自己使用的索引,这允许您执行重用顶点等优化。
如果将primitiveRestartEnable成员设置为VK_TRUE,则可以通过使用特殊索引0xFFFF或0xFFFFFFFF来分解_STRIP拓扑模式中的行和三角形。
如果是绘制三角形,创建如下的 VkPipelineInputAssemblyStateCreateInfo:
VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
inputAssembly.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
视窗描述了输出将被渲染到的帧缓冲区域。 基本总是(0,0)到(宽度,高度),也就是窗口大小。
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;
交换链及其图像的大小可能与窗口的宽度和高度不同。 交换链图像将在以后用作帧缓冲区,因此我们不能轻易改变它们的大小。
minDepth和maxDepth值指定用于帧缓冲区的深度值范围。 这些值必须在[0.0f,1.0f]范围内,但minDepth可能高于maxDepth。
虽然视窗定义了从图像到帧缓冲的转换,但剪刀矩形定义了实际存储像素的区域。
剪刀矩形外的任何像素都将被光栅化器丢弃。 它们的功能类似于过滤器而不是转换。
差异如下所示。 请注意,左边的剪刀矩形只是导致该图像的众多可能性之一,只要它比视口大:
也就是说Viewport会缩放以显示完整的图片,而scissors会裁剪(或者说遮挡)图片内容。
简单定义一个可以绘制整个帧缓冲的Scissor:
VkRect2D scissor = {};
scissor.offset = {0, 0};
scissor.extent = swapChainExtent;
现在使用VkPipelineViewportStateCreateInfo结构将此视口和剪刀矩形组合成视口状态。
可以在某些图形卡上使用多个视口和剪刀矩形,因此其成员需要引用它们的数组。使用多个需要启用GPU功能。
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
光栅化器采用由顶点着色器的顶点整形的几何体,并将其转换为片段着色器着色的片段。
它还可以执行深度测试,面部剔除和剪刀测试,并且可以配置为输出填充整个多边形或仅填充边缘的片段(线框渲染)。
所有这些都是使用VkPipelineRasterizationStateCreateInfo结构配置的。
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; // Optional
rasterizer.depthBiasClamp = 0.0f; // Optional
rasterizer.depthBiasSlopeFactor = 0.0f; // Optional
光栅化器可以通过添加一个常量值或根据fragment的坡度对深度值进行偏移来改变深度值。这有时用于阴影映射,一般不使用时只需将depthBiasEnable设置为VK_FALSE。
VkPipelineMultisampleStateCreateInfo结构体用于配置vulkan中的多重采样。
是通过将光栅化为同一像素的多个多边形的片段着色器结果组合在一起实现的,这也是抗锯齿的方式之一。主要是在图形边缘地区做多重采样,这是最明显的锯齿伪影发生的地方。
如果只有一个多边形映射到一个像素,它不需要多次运行片段着色器,因此它比简单地渲染到更高的分辨率然后缩小比例的开销小得多。
启用多重采样需要启用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; // Optional
multisampling.pSampleMask = nullptr; // Optional
multisampling.alphaToCoverageEnable = VK_FALSE; // Optional
multisampling.alphaToOneEnable = VK_FALSE;
后续详细研究,当前先暂时关闭该功能。
片段着色器返回颜色后,需要将其与帧缓冲区中已有的颜色组合。这种转换称为颜色混合,有两种方法:
有两种类型的结构可以配置颜色混合。
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;
// Optional
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
// Optional
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; // Optional
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
// Optional
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
// Optional
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; // Optional
此每帧缓冲区结构允许您配置颜色混合的第一种方式。将要执行的操作使用以下伪代码演示原理:
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;
如果blendEnable设置为VK_FALSE,则片段着色器中的新颜色将不经修改地通过。否则,执行这两个混合操作来计算新颜色。
生成的颜色与colorWriteMask进行AND运算,以确定实际通过哪些通道。
使用颜色混合最常用的方法是实现alpha混合,我们希望新颜色与基于不透明度的旧颜色混合。最终颜色的计算如下:
finalColor.rgb = newAlpha * newColor + (1 - newAlpha) * oldColor;
finalColor.a = newAlpha.a;
这可以通过以下参数实现:
// 实现alpha混合
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;
可以在VkBlendFactor和VkBlendOp中找到所有可用的操作:
VkBlendFactor | RGB Blend Factors (Sr,Sg,Sb) or (Dr,Dg,Db) | Alpha Blend Factor (Sa or Da) |
---|---|---|
VK_BLEND_FACTOR_ZERO | (0,0,0) | 0 |
VK_BLEND_FACTOR_ONE | (1,1,1) | 1 |
VK_BLEND_FACTOR_SRC_COLOR | (Rs0,Gs0,Bs0) | As0 |
VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR | (1-Rs0,1-Gs0,1-Bs0) | 1-As0 |
VK_BLEND_FACTOR_DST_COLOR | (Rd,Gd,Bd) | Ad |
VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR | (1-Rd,1-Gd,1-Bd) | 1-Ad |
VK_BLEND_FACTOR_SRC_ALPHA | (As0,As0,As0) | As0 |
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA | (1-As0,1-As0,1-As0) | 1-As0 |
VK_BLEND_FACTOR_DST_ALPHA | (Ad,Ad,Ad) | Ad |
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA | (1-Ad,1-Ad,1-Ad) | 1-Ad |
VK_BLEND_FACTOR_CONSTANT_COLOR | (Rc,Gc,Bc) | Ac |
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR | (1-Rc,1-Gc,1-Bc) | 1-Ac |
VK_BLEND_FACTOR_CONSTANT_ALPHA | (Ac,Ac,Ac) | Ac |
VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA | (1-Ac,1-Ac,1-Ac) | 1-Ac |
VK_BLEND_FACTOR_SRC_ALPHA_SATURATE | (f,f,f); f = min(As0,1-Ad) | 1 |
VK_BLEND_FACTOR_SRC1_COLOR | (Rs1,Gs1,Bs1) | As1 |
VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR | (1-Rs1,1-Gs1,1-Bs1) | 1-As1 |
VK_BLEND_FACTOR_SRC1_ALPHA | (As1,As1,As1) | As1 |
VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA | (1-As1,1-As1,1-As1) | 1-As1 |
备注:
选择源和目标混合因子后,它们连同源和目标组件一起传递给混合操作。RGB和alpha组件可以使用不同的操作。指定操作的VkBlendOp的可能值为:
VkBlendOp | RGB Components | Alpha Component |
---|---|---|
VK_BLEND_OP_ADD | R = Rs0 × Sr + Rd × Dr G = Gs0 × Sg + Gd × Dg B = Bs0 × Sb + Bd × Db |
A = As0 × Sa + Ad × Da |
VK_BLEND_OP_SUBTRACT | R = Rs0 × Sr - Rd × Dr G = Gs0 × Sg - Gd × Dg B = Bs0 × Sb - Bd × Db |
A = As0 × Sa - Ad × Da |
VK_BLEND_OP_REVERSE_SUBTRACT | R = Rd × Dr - Rs0 × Sr G = Gd × Dg - Gs0 × Sg B = Bd × Db - Bs0 × Sb |
A = Ad × Da - As0 × Sa |
VK_BLEND_OP_MIN | R = min(Rs0,Rd) G = min(Gs0,Gd) B = min(Bs0,Bd) |
A = min(As0,Ad) |
VK_BLEND_OP_MAX | R = max(Rs0,Rd) G = max(Gs0,Gd) B = max(Bs0,Bd) |
A = max(As0,Ad) |
备注:
VkPipelineColorBlendStateCreateInfo结构引用所有帧缓冲区的结构数组,并允许您设置可以在上述计算中用作混合因子的混合常数。
VkPipelineColorBlendStateCreateInfo colorBlending = {};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.logicOp = VK_LOGIC_OP_COPY; // Optional
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
colorBlending.blendConstants[0] = 0.0f; // Optional
colorBlending.blendConstants[1] = 0.0f; // Optional
colorBlending.blendConstants[2] = 0.0f; // Optional
colorBlending.blendConstants[3] = 0.0f; // Optional
如果要使用第二种混合方法(按位组合),则应将logicOpEnable设置为VK_TRUE。然后可以在logicOp字段中指定位操作。请注意,这将自动禁用第一个方法,就像为每个附加的帧缓冲区将blendEnable设置为VK_FALSE一样!colorWriteMask也将用于此模式,以确定帧缓冲区中的哪些通道将实际受到影响。也可以禁用这两种模式,这种情况下,片段颜色将被不修改地写入帧缓冲区。
一小部分状态是可以不需要重新创建管道,直接修改的;比如viewport的大小,线宽和颜色混合常数等。
可以使用 VkPipelineDynamicStateCreateInfo 来更改这些动态参数:
VkDynamicState dynamicStates[] = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_LINE_WIDTH
};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = 2;
dynamicState.pDynamicStates = dynamicStates;
这将导致忽略这些值的配置,并要求在绘图时指定相应配置。
可以在着色器中使用统一的值,类似于可以在绘制时更改的动态状态变量,以更改着色器的行为,而无需重新创建它们。
通常用于将变换矩阵传递给顶点着色器,或在片段着色器中创建纹理采样器。
在创建管道期间,需要通过创建VkPipelineLayout对象来指定这些统一值。创建一个类成员来保存这个对象,稍后会从其他函数中引用它:
VkPipelineLayout pipelineLayout;
void createGraphicsPipeline() {
// ......
VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // Optional
pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr,
&pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
// ......
}
该结构还指定了push常量,这是将动态值传递给着色器的另一种方式,我们将在以后的章节中讨论。
管道布局将在整个程序的整个生命周期内被引用,因此在结束时应销毁:
void cleanup() {
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
...
}
所有的固定函数状态都是这样!这是一个很大的工作,以设置所有这些从头开始,但好处是,我们现在几乎完全了解一切正在进行的图形管道!
这减少了运行到意外行为的机会,因为某些组件的默认状态不是您所期望的。不过,在我们最终创建图形管道之前,还有一个对象需要创建,那就是渲染过程。
总结一下到目前位为止,创建图形管道应该做的操作:
iOS OC中copy和mutableCopy的使用区别// 浅复制- (IBAction)shallowCopyAction:(id)sender { NSArray *array0 = @[@"A", @"B", @"C"]; NSArray *array1 = array0; NSArray *array2 = [array0 copy]; NSLog(@..._oc copy 和mu ta
手机归属地数据文件格式是自己定义的,使用本程序将文本格式的手机号数据导入为二进制文件以节约存储空间和提高查询速度,数据文件命名为MpData.dat,然后可以使用本程序查询手机号段的归属地注: Mps.cpp是主程序, Global.cpp是一些工具函数和类, Release/MpData.txt是文本格式的手机归属地数据。 程序的使用方法请参阅Mps.cpp中printHelp()函数的代码..._c语言归属地查询代码
导读 伴随移动互联网用户爆炸式的增长,网络业务、应用场景的多样化,加之共建共享的全面推进,网络制式、用户归属、终端类型变得更加繁杂。传统的优化手段已无法快速适应网络问题的复杂多变。如何将大数据技术运用于网络优化工作中,将网优经验数学化,形成由IT引擎驱动的智能分析系统,精准定位并解决问题,成为优化工作的新方向。本次分享主题是《特定场景下的网络质量评估与预警方法介绍》,主要内容分为4部分:1. 相关背景介绍2. 评估——基于熵权法的网络综合评估3. 预警——小区扩容优先级推荐4..._场景级的网络问题评估
#20165205 实验一 Java开发环境的熟悉##一、实验报告封面 课程:Java程序设计 班级:1652班 姓名:刘喆君 学号:20165205指导教师:娄嘉鹏 实验日期:2018年4月2日实验时间:13:45 - 15:25 实验序号:一实验名称:Java开发环境的熟悉实验目的与要求: 1、完成实验、撰写实验报告,注意实验报告重点是运行结果,遇到的问题以及分析。 2、实验报告中统计自己的P..._java运行环境实验报告
基于孙鑫老师讲授的MFC第8讲的视频对属性页和向导的创建而做的一点笔记
颜博马蜂窝数仓研发总监现任马蜂窝数据仓库团队负责人,曾供职于京东、IBM、亚信等公司。数据行业老兵一名,历经传统数据仓库、大数据平台到数据中台的发展。大家好,今天分享的议题主要包括几大内...
原标题:访谈 | 信息院优秀毕业生经验分享信息学院优秀毕业生经验分享又到了一年的毕业季,学姐学长们经过了四年充实的大学生活,开始迈向新的征程。他们的现在正是我们的未来,但不同的是他们已经向着明确的目标进发,而我们很多人还处于迷茫的状态。今天,我们采访了两位优秀的毕业生,听他们分享了一些大学学习以及生活的经验。希望能给大家带来经验的同时,也能让同学们更加明确自己的未来。第一位是13级计算机专业的李丹..._计算机专业就业访谈
一般大家都知道ArrayList和LinkedList的大致区别: 1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。 3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移
#ifndef __TIMERTASK_H_#define __TIMERTASK_H_/* 定时器任务基类 */#include "asr_taskinterface.h"#include "comontype.h"class BaseTimerTask{public: BaseTimerTask():m_uiTvSec(0),m_uiTvUsec(0){} ..._c++ 实现简单任务调度 select使用
一堆介绍堆主要划分为新生代和老年代;详细划分为Eden,from survivor ,to survivor;二jdk1.8-jvm内存模型要看详细的介绍情看的介绍请看作者发布的"jvm运行时数据区一文",jdk1.8中已经取代了方法区永久带的概念转而的是在方法区中的元空间,有人也称作是直接内存。二垃圾收集器示意图图中有直线连接的说明是能够相互配合使用的垃圾收集器。三垃圾收..._javm
九宫格拼图游戏设计文档一、综合设计目的、条件、任务和内容要求:1.设计目的《Windows程序设计》是计算机科学与技术专业本科生的一门学科基础课程。Windows程序以图形用户界面(GUI)给用户提供各种功能,在各行各业有着广泛的应用。基于MFC的Windows程序设计是进行Win32程序设计的一种主流方法。本课程主要介绍Windows程序设计的思想和方法,以及MFC的常见应用。本课程综合应用算法..._九格拼图项目描述怎么写
1、图像增强 直方图均衡化 处理C 语言实现#ifndef BMP_H_3_INCLUDED#define BMP_H_3_INCLUDED typedef unsigned short WORD;typedef unsigned long DWORD;typedef long LONG;typedef unsigned char BYTE;typedef struct tagBITMAPFILE..._图象增强c语言