第八章 绘制
在本章你将学到:
l Vulkan中不同绘制名利的细节
l 如何通过实例化来绘制多分复制的数据
l 如何通过缓冲区传递绘制参数
绘制是Vulkan中基础的操作,由图形管线触发工作被执行。Vulkan包含多条绘制命令,每一个都以稍微不同的方式声称图形工作。本章深入研究Vulkan所支持的绘制命令。首先,我们在回顾一下第七章“”讨论过的基本绘制命令;然后,我们探索indexed和instanced绘制命令。最后,我们讨论如何为绘制命令从设备内存中取出参数,甚至在设备上生成参数。
回到第七章“”,你见到了第一个绘制命令,vkCmdDraw()。这个命令仅仅是把顶点放入Vulkan图形管线。当我们介绍这个命令时,我们粗略的讲解了它的参数。我们了透露了其他的绘制命令的存在。为了参考,vkCmdDraw()的原型如下:
voidvkCmdDraw (
VkCommandBuffer commandBuffer,
uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance);
和其他在设备上执行的命令一样,第一个参数是一个VkCommandBuffer类型数据的handle。每一次绘制的顶点个数通过vertexCount指定,顶点数据中起始顶点的索引通过firstVertex指定。需要传送到管线的顶点是从firstVertex位置开始vertexCount个连续的顶点的数据。如果你使用顶点缓冲区和顶点属性来自动的吧数据传递到顶点着色器,那么着色器就会看到从数组中获取的连续数据。如果你在着色器中直接使用顶点的索引,你将看到从firstVertex之后的数据。
8.1 准备绘制
如我们在第七章“”中提到的,所有的绘制命令都包含在一个renderpass里。尽管renderpass对象可以封装多个subpass,简单的绘制到一个输出图像也需要是renderpass的一部分。可通过调用vkCreateRenderPass()来创建renderpass对象。要准备绘制,我们需要调用vkCmdBeginRenderPass(),它将设置当前的renderpass对象,并且,更重要的是,配置将要绘制的输出图像集合。vkCmdBeginRenderPass() 的原型是:
voidvkCmdBeginRenderPass (
VkCommandBuffer commandBuffer,
const VkRenderPassBeginInfo* pRenderPassBegin,
VkSubpassContents contents);
在renderpass内包含被发送命令的命令缓冲区通过commandBuffer来传递。描述renderpass的一堆参数通过一个VkRenderPassBeginInfo类型数据的指针pRenderPassBegin来传递。VkRenderPassBeginInfo的定义是:
typedefstruct VkRenderPassBeginInfo {
VkStructureType sType;
const void* pNext;
VkRenderPass renderPass;
VkFramebuffer framebuffer;
VkRect2D renderArea;
uint32_t clearValueCount;
const VkClearValue* pClearValues;
} VkRenderPassBeginInfo;
VkRenderPassBeginInfo的sType域应置为VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,pNext应置为nullptr。被开始的renderpass通过renderPass指定,渲染所到的帧缓冲区通过framebuffer指定。如我们在第七章“”所讨论过的,帧缓冲区是图形命令绘制结果的一系列图像。
不管用什么方式使用renderpass,我们可以选择只渲染到附着的图像的一部分区域。要这样做,需使用VkRenderPassBeginInfo的renderArea成员来指定渲染目标矩形区域。设置renderArea.offset.x and renderArea.offset.y 为 0 ,renderArea.extent.width 和 renderArea.extent.height为帧缓冲区的宽度和高度告诉Vulkan你将渲染的矩形区域。
如果renderpass的任何一个附件有VK_ATTACHMENT_LOAD_OP_CLEAR这个加载操作,那么你想清除的颜色或者值通过pClearValues.所指向的VkClearValue数组指定。pClearValues的个数通过clearValueCount传递。VkClearValue的定义是:
typedefunion VkClearValue {
VkClearColorValue color;
VkClearDepthStencilValue depthStencil;
} VkClearValue;
如果附件是颜色附件,那么存储在VkClearValue的color成员的值就被使用,如果附件是深度、stencil,或者depth-stencil附件,那么depthStencil成员的值被使用。Color和depthStencil是VkClearColorValue和VkClearDepthStencilValue类型的实例,各自定义如下:
typedefunion VkClearColorValue {
float float32[4];
int32_t int32[4];
uint32_t uint32[4];
} VkClearColorValue;
typedef struct VkClearDepthStencilValue {
float depth;
uint32_t stencil;
} VkClearDepthStencilValue;
每一个附件的索引用来在VkClearValue数组中索引位置。这意味着如果一些附件有VK_ATTACHMENT_LOAD_OP_CLEAR这个加载操作,那么数组中就有未使用的元素。pClearValues数组中元素的个数至少有带有VK_ATTACHMENT_LOAD_OP_CLEAR操作的附件的最高的索引值那么多个。
对于每一个带有VK_ATTACHMENT_LOAD_OP_CLEAR操作的附件,如果它是一个附件,那么float32,int32, or uint32 数组的值被用来清除附件的内容,依数组元素类型是否为浮点、归一化格式、有符号整型或者无符号整型格式而定。如果附件是深度、stencil或者是深度-stencil附件,那么VkClearValue的depthStencil的depth和stencil成员用来清除附件里对应的内容。
一旦renderpass开始,你可以把绘制命令放入命令缓冲区。所有的绘制结果都将直接进入vkCmdBeginRenderPass()参数VkRenderPassBeginInfo所指定的帧缓冲区。要终止renderpass中的渲染,可以调用vkCmdEndRenderPass()函数,其原型如下:
voidvkCmdEndRenderPass (
VkCommandBuffer commandBuffer);
vkCmdEndRenderPass()执行后,renderpass中任何绘制都已经完成了,帧缓冲区的内容已经更新。在此之前,帧缓冲区的内容是为定义的。只有带有VK_ATTACHMENT_STORE_OP_STORE操作的附件将会影响到renderpass中心的绘制产生的内容。如果一个附件有VK_ATTACHMENT_STORE_OP_DONT_CARE操作,那么它的内容在renderpass完成后是为未定义的。