I want to implement stencil shadows, and not have to work with individual lights on CPU side (recording buffers that alternate pipelines for each light) - i want to do all the lights in one go. I see it being possible with compute shaders; however I don't have access to ROPs from them, and while using atomics should be possible, it doesn't feel right (transforming R32UINT
image into B8G8R8A8UNORM
or whatever vkGetPhysicalDeviceSurfaceFormatsKHR
may output). Having to do software rasterisation of shadow volumes also feels wrong. Simply using stencil, and outputting 0 color when drawing shadow volumes, then do a quad of actual light is nice, however i don't see any way to clear it inbetween draws. I've also thought of using blending and alpha value, but the only way i could thought of requires special clamping behaviour: not clamp blending inputs, but clamp outputs. And as far as I'm aware, its not possible to read pixels from framebuffer being drawn to in the very same draw call.

- 600
- 1
- 5
- 9
-
How do you put multiple lights in a single stencil buffer? My understanding of the stencil shadow algorithm is that front faces increment the stencil value and back faces decrement it. That seems to preclude having multiple shadow-casting lights in a single stencil buffer. And if you can't have multiple lights in a single stencil buffer, then the rest of your question is sophistry. – Nicol Bolas Feb 24 '21 at 18:06
-
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. However I dont see a way to do that without 'exiting' the `drawIndirect` and switching pipelines. My approach with alpha was to populate alpha buffer, 'validate' it by multiplying it so that any non-0 becomes 1 (using double src blending), and zero it on light draw. However the values are clamped before the blending... – user369070 Feb 24 '21 at 22:10
2 Answers
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.
You have a problem before the "somehow clear it" part. Namely, drawing the "light quad" would require changing the stencil parameters from writing stencil values to testing them. Which of course you can't do in the middle of a drawing command.
While bundling geometry into a few draw commands is always good, it's important to remember that Vulkan is not OpenGL. State changes aren't free, and full pipeline changes aren't remarkably cheap, but they're not as costly as they would be under OpenGL. So you shouldn't feel bad about having to break drawing commands up in this manner.

- 449,505
- 63
- 781
- 982
-
As far as I am aware it is possible to have both testing and writing stencil enabled at once; and because lighting normally uses additve blending, when filling stencil we can simply output 0 to not change anything and calculate actual colors afterwards; We can also specify stencil action for both passing stencil test and failing it, so i dont see any need for stencil state change here. I guess clearing stencil quickly (without 255 screen quads) is not possible then. Oh, and reason im trying to bundle is my personal challenge to move as much work as possible onto gpu - culling, etc. – user369070 Feb 24 '21 at 22:54
To clear stencil buffer within a draw command is not possible; However I was able to achieve the desired result with special stencil state, late depth-stencil tests, discard
and some extra work within shader, at a cost of doing those very things and flexibility.
How it works in my case(depth fail shadows):
- For differentiating between passes, I use
GL_ARB_shader_draw_parameters
forgl_DrawID
, but it should be possible through other means - Shadow pass:
- In fragment shader, if depth is to be passed, discard // thus, no color writes from it are ever done
- In stencil state, front-face fail(both depth and stencil) -> increment; back-face fail -> decrement; // there volumes are counted
- Light pass:
- If the light triangle is back-facing, output zero; Stencil state, back-face pass -> replace with reference; // there stencil is cleared
- Else, calculate color; Stencil state, front-face pass -> doesn't matter.

- 600
- 1
- 5
- 9