首页 > 解决方案 > 哪种方法最适合使用 Vulkan API 实现粒子着色器?

问题描述

我正在实现粒子着色器,这意味着为每个矩形绘制大量具有唯一数据的矩形。现在我看到了不同的方法来做到这一点。


方式一:

  1. 对所有粒子使用一个统一的缓冲区
  2. 在绘图开始时执行单个 vkCmdBindDescriptorSets
  3. 调用单个 vkCmdDraw 实例数等于粒子数
  4. 在顶点着色器中使用仅由单个数组组成的统一结构:
struct Particle
{
    //...
};

layout(binding = 0) uniform ParticleUniformBufferObject
{
    Particle particles[MAX_PARTICLES_TO_DRAW];
} ubo;
  1. 然后在绘图时使用 glsl 变量来访问统一数据
vec3 particlePosition = ubo.particles[gl_InstanceID].position;

方式二:

  1. 对所有粒子使用一个统一的缓冲区
  2. 使用动态偏移为每个粒子做 vkCmdBindDescriptorSets
  3. 对实例计数为 1 的每个粒子执行 vkCmdDraw
  4. 在没有数组的情况下以通用方式访问顶点着色器中的缓冲区数据

方式3:

通过顶点输入完成所有操作,无需任何对象统一。可能会有很多不必要的数据开销。在这种情况下,顶点缓冲区应该在每一帧更新。


我的问题 - 在性能方面哪种方式更好?

或者您是否看到了另一种使用 Vulkan API 进行粒子渲染的好方法?

标签: c++vulkanparticlesparticle-system

解决方案


另一种方法是使用间接绘图:vkCmdDrawIndirect. 如果您只能在 GPU 上修改粒子,那么这种方法可能会获得回报。如果您需要动画/修改/等。它们在 CPU 上,那么间接绘图将无济于事。

就您的粒子数据而言,您可以将它们存储在VK_BUFFER_USAGE_STORAGE_BUFFER_BIT缓冲区中,在计算着色器通道中修改它们,并使用间接绘制调用来渲染它们。要访问粒子的数据,您必须使用GLSL's built-in gl_DrawID


一般来说,就性能而言,无法判断哪种方式比另一种方式更好。这取决于很多因素:你将如何修改粒子,它们依赖什么,粒子数量,使用的 GPU 等等。如果你真的想知道,你必须实现所有方法并测量.

如果您要执行后者,请分享您的结果并描述您的设置!像这样的东西总是很有趣。


推荐阅读