Part 3. Vulkan中的深度测试和Uniform缓冲区
转载自最早发布在formu.ziyuesinicization.site的文章
我首先需要创建深度图像并为他分配内存,,之后我们需要创建深度图像视图:
VkFormat depthFormat = VK_FORMAT_D32_SFLOAT;
VkImage depthImage;
VkImageCreateInfo depthImgInfo{};
depthImgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
depthImgInfo.imageType = VK_IMAGE_TYPE_2D;
depthImgInfo.extent.width = spExtent.width;
depthImgInfo.extent.height = spExtent.height;
depthImgInfo.extent.depth = 1;
depthImgInfo.mipLevels = 1;
depthImgInfo.arrayLayers = 1;
depthImgInfo.format = depthFormat;
depthImgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
depthImgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthImgInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
depthImgInfo.samples = VK_SAMPLE_COUNT_1_BIT;
depthImgInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
vkCreateImage(device, &depthImgInfo, nullptr, &depthImage);
VkMemoryRequirements depthMemReqs;
vkGetImageMemoryRequirements(device, depthImage, &depthMemReqs);
VkMemoryAllocateInfo depthAllocInfo{};
depthAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
depthAllocInfo.allocationSize = depthMemReqs.size;
depthAllocInfo.memoryTypeIndex = FindMemoryType(physicalDevice, depthMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
VkDeviceMemory depthImageMem;
vkAllocateMemory(device, &depthAllocInfo, nullptr, &depthImageMem);
vkBindImageMemory(device, depthImage, depthImageMem, 0);
VkImageView depthImageView;
VkImageViewCreateInfo depthViewInfo{};
depthViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
depthViewInfo.image = depthImage;
depthViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
depthViewInfo.format = depthFormat;
depthViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
depthViewInfo.subresourceRange.baseMipLevel = 0;
depthViewInfo.subresourceRange.levelCount = 1;
depthViewInfo.subresourceRange.baseArrayLayer = 0;
depthViewInfo.subresourceRange.layerCount = 1;
vkCreateImageView(device, &depthViewInfo, nullptr, &depthImageView);
下一步,我们创建深度附件和深度附件参考,并添加到渲染通道(Render Pass):
VkAttachmentDescription depthAttachment{};
depthAttachment.format = depthFormat;
depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentReference depthAttachRef{};
depthAttachRef.attachment = 1;
depthAttachRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
VkAttachmentDescription attachments[2] = {colorAttachment, depthAttachment};
VkSubpassDescription spDes = {};
spDes.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
spDes.colorAttachmentCount = 1;
spDes.pColorAttachments = &colorAttachRef;
spDes.pDepthStencilAttachment = &depthAttachRef;
接着,我们给帧缓冲加上深度图像视图:
VkImageView attachments[] = {
spImagesView[i],
depthImageView
};
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = 2;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = spExtent.width;
framebufferInfo.height = spExtent.height;
framebufferInfo.layers = 1;
最后在渲染管线开启深度测试即可:
VkPipelineDepthStencilStateCreateInfo depthStencil{};
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencil.depthTestEnable = VK_TRUE;
depthStencil.depthWriteEnable = VK_TRUE;
depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
depthStencil.depthBoundsTestEnable = VK_FALSE;
depthStencil.stencilTestEnable = VK_FALSE;
pipelineInfo.pDepthStencilState = &depthStencil;
在循环中添加深度清除:
VkClearValue clearValues[2];
clearValues[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}}; // 颜色清除
clearValues[1].depthStencil = {1.0f, 0}; // 1.0 表示最远
renderPassInfo.clearValueCount = 2;
renderPassInfo.pClearValues = clearValues;
最后别忘了释放资源:
vkDestroyImageView(device, depthImageView, nullptr);
vkDestroyImage(device, depthImage, nullptr);
vkFreeMemory(device, depthImageMem, nullptr);
接着我们来讨论一下Uniform缓冲。我们要从CPU端上传矩阵到GPU,先编写着色器,以HLSL为例:
#include "IO.hlsl"
cbuffer Buffer_0_s : register(b0) {
float4x4 transform;
};
VS_OUTPUT main(VS_INPUT input, uint vertexID : SV_VertexID)
{
VS_OUTPUT output;
output.position = mul(transform, float4(input.position, 1.0));
output.color = input.color;
return output;
}
我们在CPU端写好矩阵:
Matrix transform;
按照上一篇文章的方法,先创建并分配内存,拷贝数据给我们的Uniform缓冲:
VkBuffer uniBuffer;
VkDeviceMemory uniBufferMem;
CreateAndAllocateBuffer(device, physicalDevice, sizeof(Matrix), VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &uniBuffer, &uniBufferMem);
vkMapMemory(device, uniBufferMem, 0, sizeof(Matrix), 0, &data);
memcpy(data, &transform.data[0][0], sizeof(Matrix));
vkUnmapMemory(device, uniBufferMem);
我们先制作一个模板,并绑定为零,对应这刚刚着色器中的register(b0):
VkDescriptorSetLayoutBinding layoutBinding = {};
layoutBinding.binding = 0;
layoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
layoutBinding.descriptorCount = 1;
layoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
VkDescriptorSetLayout descLayout;
VkDescriptorSetLayoutCreateInfo layoutCreateInfo = {};
layoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
layoutCreateInfo.bindingCount = 1;
layoutCreateInfo.pBindings = &layoutBinding;
vkCreateDescriptorSetLayout(device, &layoutCreateInfo, nullptr, &descLayout);
接着,我们需要一个池子存放:
VkDescriptorPoolSize poolSize = {};
poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSize.descriptorCount = 1;
VkDescriptorPoolCreateInfo poolInfo = {};
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolInfo.poolSizeCount = 1;
poolInfo.pPoolSizes = &poolSize;
poolInfo.maxSets = 1;
VkDescriptorPool descPool;
vkCreateDescriptorPool(device, &poolInfo, nullptr, &descPool);
然后开始分配:
VkDescriptorSetAllocateInfo _allocInfo = {};
_allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
_allocInfo.descriptorPool = descPool;
_allocInfo.descriptorSetCount = 1;
_allocInfo.pSetLayouts = &descLayout;
VkDescriptorSet descSet;
vkAllocateDescriptorSets(device, &_allocInfo, &descSet);
最后就是运送数据了:
VkDescriptorBufferInfo bufferInfo = {};
bufferInfo.buffer = uniBuffer;
bufferInfo.offset = 0;
bufferInfo.range = sizeof(Matrix);
VkWriteDescriptorSet write = {};
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descSet;
write.dstBinding = 0;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &bufferInfo;
vkUpdateDescriptorSets(device, 1, &write, 0, nullptr);
接着,修改管线布局,把你刚刚描述集(descSet)加到管线布局上面:
pipelineLayoutInfo.pSetLayouts = &descLayout;
最后一步,在循环中绑定描述集即可:
vkCmdBindDescriptorSets(commandBuffer,
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipelineLayout,
0,
1, &descSet,
0, nullptr);