【问题标题】:Vulkan texture rendering on multiple meshes多个网格上的 Vulkan 纹理渲染
【发布时间】:2016-08-14 19:58:24
【问题描述】:

我正在为一个模型的多个网格渲染不同的纹理,但我对过程没有太多线索。有人建议为每个网格创建自己的描述符集并调用 vkCmdBindDescriptorSets() 和 vkCmdDrawIndexed() 进行渲染,如下所示:

    // Pipeline with descriptor set layout that matches the shared descriptor sets
vkCmdBindPipeline(...pipelines.mesh...);
...
// Mesh A
vkCmdBindDescriptorSets(...&meshA.descriptorSet... );
vkCmdDrawIndexed(...);
// Mesh B
vkCmdBindDescriptorSets(...&meshB.descriptorSet... );
vkCmdDrawIndexed(...);

但是,上述方法与 chopper 样本和 vulkan 的样本有很大不同,这让我不知道从哪里开始改变。我非常感谢任何帮助引导我走向正确方向的帮助。

干杯

【问题讨论】:

  • 那些样本使用什么方法?

标签: texture-mapping texture2d vulkan


【解决方案1】:

我猜是我提出上述代码片段的人;)

这只是一种方法。您不必为每个网格或每个纹理创建一个描述符集。如果你的网格例如使用 4 种不同的纹理,您可以一次将它们全部绑定到不同的绑定点并在着色器中选择它们。

如果您看一下 NVIDIA 的 Chopper 样本,他们的做法几乎相同,只是多了一些抽象。

该示例还为使用的纹理设置描述符集:

VkDescriptorSet *textureDescriptors = m_renderer->getTextureDescriptorSets();

在几行之后绑定它们:

VkDescriptorSet sets[3] = { sceneDescriptor, textureDescriptors[0], m_transform_descriptor_set };
vkCmdBindDescriptorSets(m_draw_command[inCommandIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, 3, sets, 0, NULL);

然后使用绑定的描述符集渲染网格:

vkCmdDrawIndexedIndirect(m_draw_command[inCommandIndex], sceneIndirectBuffer, 0, inCount, sizeof(VkDrawIndexedIndirectCommand));
vkCmdDraw(m_draw_command[inCommandIndex], 1, 1, 0, 0);

如果您查看 initDescriptorSets,您会发现它们还为立方体贴图、地形等创建单独的描述符集。

LunarG 示例应该类似,但如果我没记错的话,它们从不使用多个纹理?

【讨论】:

  • 非常感谢您的回复。 m_texture_descriptor_sets 应该是处理 chopper 模型的材质/纹理的描述符集,但它的大小看起来只能用于 m_texture_descriptor_sets = (VkDescriptorSet*)malloc(sizeof(VkDescriptorSet)); 的一个子网格。我想知道我是否理解正确。
  • 我没有深入研究 chopper 样本,但是如果你看一下片段着色器,你会发现它使用了一个采样器数组:layout(set = 1, binding = 0) uniform sampler2D tex[16]; 所以我猜 submeshes 只使用了一个该采样器数组中的不同索引以在纹理之间进行选择。这是为每个(子)网格实现不同纹理的另一种方法。
  • 感谢您的提示。我现在正在研究关于多个描述符集的 LunarG 示例的 Mesh 项目。我创建了一个 VkDescriptorSet 数组来绑定每个网格的纹理并尝试为它们分配内存为VkDescriptorSet* texDescriptorSets = (VkDescriptorSet*)malloc(sizeof(VkDescriptorSet) * g_MeshCount);,但是,似乎设备对象对其大小有严格的限制,因此我有一个设备内存不足的断言调用 vkAllocateDescriptorSets() 函数。如果我做得对,请告诉我。
  • @MichaelWei:因此,如果您想要一种适用于所有硬件的方法,则必须为不同的对象使用不同的描述符集。
  • @MichaelWei :如果您在分配描述符集时得到内存不足的结果,请确保您设置了正确的池大小(请参阅here)。如果您想为您的纹理使用 4 个组合图像采样器,您还必须从描述符池中请求至少 4 *(使用此布局的描述符集的数量)VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER。
【解决方案2】:

您有一个概念对象,它由具有不同纹理需求的多个网格组成。处理这种情况的一般方法是:

  1. 更改对象各部分之间的描述符集。很痛苦,但它适用于所有支持 Vulkan 的硬件。

  2. 使用数组纹理。每个单独的网格从阵列纹理中的特定层获取其数据。当然,这会限制您让每个子网格使用相同大小的纹理。但它适用于所有支持 Vulkan 的硬件(最多 128 个数组元素,最少)。特定网格的数组层可以作为 push-constant 或基本实例(如果可用)提供。

    请注意,如果您设法通过基础实例来实现,那么您可以使用多重绘制间接命令渲染整个对象。虽然目前尚不清楚短的多重绘图间接是否比仅将短的绘图命令序列烘焙到命令缓冲区中更快。

  3. 按照Sascha Willems 的建议,使用采样器阵列。据推测,子网格的数组索引作为推送常量或多重绘制的绘制索引提供。问题是,无论该数组索引是如何提供的,它都必须是一个动态统一的表达式。 Vulkan 实现不是必需的,它允许您使用动态统一的表达式来索引采样器数组。基本要求只是一个常量表达式。

    这将您限制在支持shaderSampledImageArrayDynamicIndexing 功能的硬件上。所以你必须提出这个要求,如果它不可用,那么你必须使用#1 或#2 来解决这个问题。或者只是不要在该硬件上运行。但最后一个意味着你不能在任何移动硬件上运行,因为most of them don't support this feature 到目前为止。

    请注意,我并不是说您不应该使用这种方法。我只是想让你知道有成本。有很多硬件无法做到这一点。所以你需要为此做好计划。

【讨论】:

  • 非常感谢您的建议,这对我很有帮助。
  • 关于 1.:这是否意味着创建多个包含不同 VkImageViews 的 VkDescriptorSets 并在填充命令缓冲区时使用 vkCmdBindDescriptorSets 在它们之间切换,然后再发出相应的 vkCmdDraw* 绘制单个网格的命令?
  • 回答我自己的问题 - 对于不支持shaderSampledImageArrayDynamicIndexing 的硬件(在我的例子中,是 NXP i. MX8M 处理器)。只需要一个VkSampler,并且必须注意使用适当大小的描述符集池。单独的描述符集还可以包含单独的统一缓冲区对象,这些对象可以包含单独的网格/对象的不同模型矩阵。
猜你喜欢
  • 1970-01-01
  • 2023-01-20
  • 1970-01-01
  • 2023-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多