有不同的方法,但最直接的方法只是您已经所做的事情的扩展,即为每个对象提供多个统一缓冲区,并为这些统一缓冲区提供单独的描述符。在绘制时,您可以为要绘制的对象绑定适当的描述符集,该描述符集指向它的统一缓冲区。
这可能看起来像这样:
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, offsets);
vkCmdBindIndexBuffer(commandBuffer, indexBuffer, 0, VK_INDEX_TYPE_UINT32);
for (Object object : objects)
{
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &object.descriptorSet[currentImage], 0, nullptr);
vkCmdDrawIndexed(commandBuffer, object.indexCount, 1, object.firstIndex, 0, 0);
}
你的对象定义可能如下所示:
struct Object {
uint32_t indexCount;
uint32_t firstIndex;
VkBuffer buffer;
VkDescriptorSet descriptorSet;
};
std::vector<Object> objects;
因此,您不是只有一个统一缓冲区和一个指向它的描述符集,而是为每个对象创建一组,然后更新彼此分开的缓冲区:
for (Object object : objects) {
memcpy(bufferPtr[currentImage], &ubo, sizeof(ubo));
}
一个小注意事项:您不需要在每个帧上映射和取消映射缓冲区。您可以在创建后安全地映射缓冲区一次(“持久映射”)。
虽然这可能不是为每个对象提供统一缓冲区的完美方式,但它是基于您通过 vulkan 教程学到的知识的良好开端。
还有其他方法可以为每个对象传递数据,例如通过提到的推送常量(尽管需要在更改时重建命令缓冲区),但与往常一样,具体方法取决于您的用例。
另请注意,由于实现特定的限制,您希望将内存/缓冲区分配保持在最低限度。因此,如果您计划绘制大量对象,您应该查看动态统一缓冲区或子分配,而不是为每个对象分配单独的缓冲区。