是否可以在间接绘制调用中清除模板缓冲区?

Is it possible to clear stencil buffer within indirect draw call?

我想实现模板阴影,而不必在 CPU 侧使用单独的灯(记录缓冲区为每个灯交替管道)- 我想一次性完成所有灯。我认为计算着色器是可能的;但是我无法访问它们的 ROP,虽然应该可以使用原子,但感觉不对(将 R32UINT 图像转换为 B8G8R8A8UNORMvkGetPhysicalDeviceSurfaceFormatsKHR 可能输出的任何内容) .必须对阴影体积进行软件光栅化也感觉不对。简单地使用模板,并在绘制阴影体积时输出 0 颜色,然后做一个四边形的实际光很好,但是我看不出有任何方法可以在两次绘制之间清除它。我也考虑过使用混合和 alpha 值,但我能想到的唯一方法需要特殊的钳制行为:不是钳制混合输入,而是钳制输出。据我所知,不可能从同一个绘制调用中绘制的帧缓冲区中读取像素。

I was planning to draw lights one by one: fill the stencil buffer with a draw, draw a light quad on second draw from same draw inderect command, somehow clear it, and continue.

您在“以某种方式清除它”部分之前遇到了问题。即,绘制“光四边形”需要将模板参数从写入模板值更改为测试它们。这当然不能在绘图命令的中间执行。

虽然将几何图形捆绑到几个绘图命令中总是好的,但请务必记住 Vulkan 不是 OpenGL。状态更改不是免费的,完整的管道更改也不是特别便宜,但它们不像在 OpenGL 下那样昂贵。所以你不应该因为不得不以这种方式中断绘图命令而感到难过。

无法在绘图命令中清除模板缓冲区;然而,我能够通过特殊的模板状态、后期深度模板测试、discard 和着色器中的一些额外工作来实现预期的结果,代价是做这些事情和灵活性。

在我的案例中它是如何工作的(深度失败阴影):

  1. 为了区分pass,我用GL_ARB_shader_draw_parameters代替gl_DrawID,但应该可以通过其他方式
  2. 影子传球:
    1. 在片段着色器中,如果要传递深度,则丢弃 // 因此,不会从中写入任何颜色
    2. 在模板状态下,正面失败(深度和模板)->增量;背面失败 -> 递减; // 计算数量
  3. 轻传:
    1. 如果发光三角形是背面的,则输出零;模板状态,背面通过 -> 用参考替换; // 模板被清除
    2. 否则,计算颜色;模板状态,正面通过 -> 无关紧要。