I always remembered that when doing particles you have to involve the CPU in updating the vertex buffer for the newly created and the destroyed particles. Well things have changed since and nowadays you can create and destroy the particles entirely on the GPU.
The shader pipeline has something called “Stream Output” and this allows for the geometry shader to write and update GPU memory buffers such as the vertex buffer. Instead of updating the vertex buffer using the CPU every frame, we can now simply update the vertex buffer using a Geometry shader. Pretty neat, right?
In order to write into a vertex buffer, we need to bind the vertex buffer using the flag D3D11_BIND_STREAM_OUTPUT.
We write a Geometry shader in a similar way to a standard Geometry shader, only this time the Geometry shader is our final programmable shader in our draw call. In order for this to work we need to bind the stream out view of the vertex buffer using SOSetTargets.
This is a quick dry review of how to do this, but the important thing is to know that Stream out exists and that it can be used for particles.
Notice how the particles are abruptly cut when they intersect with the wall\floor. We can fix this by making the particles more soft. We do this by simply comparing the current pixel to the depth buffer and fade it if the depth of other objects are close to the depth of the particle at that pixel.
There is an issue though, how can we read from the depth texture as it is part of our render target? In DX9 we could not read from the depth buffer and we would have to create a copy of the depth map by redrawing he whole scene into an off-screen texture.
However, in DX11 we can simply create a shader resource view for the Depth map. But we still cannot bind the depth map as both the Z Buffer for the render target and as a shader resource on the same draw.
What we will do is have no depth map bound to the render target but since we do want to clip pixels that are hidden behind solid objects, we will clip them in the pixel shader.
‘clip’ is an intrinsic function that allows you to clip pixels inside the pixel shader. A pixel that is clipped will stop the executing of the pixel shader and will not be drawn into the back buffer.
So now we can use the depth buffer as a shader resource for both clipping and fading the particles near objects.
In order to fade the particles we need a linear Z value, but the values in the depth map are not linear. We would need to convert the depth map values into linear Z using the projection matrix.
I will not go into details on this, but I will paste my pixel shader code.
There is still a lot that can be done, but this is a good start.
With more than one pass we could probably make a lot more, but we will have to see how it will affect performance.