I try to understand each member of the struct VkPipelineRasterizationStateCreateInfo, but its depth bias related member confused me, especially about the depthBiasSlopeFactor. I find description about it from https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPipelineRasterizationStateCreateInfo.html and it says,
depthBiasSlopeFactor is a scalar factor applied to a fragment’s slope in depth bias calculations
I have two questions about this statement.
Firstly, what is the slope of a fragment? More exactly, slope of what property? Fragment color? Depth value?
Secondly, how is the depth bias calculation performed? Is it a form like Clamp(original-depth + bias)?
Microsoft has a pretty good explanation of it for D3D11; it should be more or less identical to how Vulkan handles it. https://learn.microsoft.com/en-us/windows/win32/direct3d11/d3d10-graphics-programming-guide-output-merger-stage-depth-bias (GitHub Version)
The slope of a pixel is referring to slope of the polygon that pixel came from. Hence why this doesn't apply to points and lines.
The exact equation for a unsigned normalized format is
Bias = (float) depthBiasConstantFactor * r + depthBiasSlopeFactor * MaxDepthSlope;
[W]here r is the minimum representable value > 0 in the depth-buffer format converted to float32.
MaxDepthSlope
is approximately
max(abs(dZ / dX), abs(dz / dy))
D3D 11.3 Spec. In case you're not familiar with calculus, this is basically choosing the max of the absolute value of the slope of Z in relation to X, and the absolute value of the slope of Z in relation to Y. Derivatives let you view the change of a function, which for a triangle - which is always planar - is simply it's slope.
If you're using a floating point depth bufer format, then the equation is a little more complicated
Bias = (float) depthBiasConstantFactor * pow(exp(max_z_in_primitive) - r, 2)
+ depthBiasSlopeFactor * MaxDepthSlope;
[W]here r is the number of mantissa bits in the floating point representation (excluding the hidden bit); for example, 23 for float32.
Depth biasing is inteded for shadow mapping, where the shadow would have the same depth value as the surface it lies on. On a flat surface this should work fairly well, but a more complex surface could have some artifacts if you're simply biasing the depth value, thus adding an amount proportional to the slope can aleviate these artifacts.
One of the artifacts with shadow buffer based shadows is shadow acne, or a surface shadowing itself due to minor differences between the depth computation in a shader, and the depth of the same surface in the shadow buffer. One way to alleviate this is to use [depthBiasConstantFactor] and [depthBiasSlopeFactor] when rendering a shadow buffer. The idea is to push surfaces out enough while rendering a shadow buffer so that the comparison result (between the shadow buffer z and the shader z) is consistent across the surface, and avoid local self-shadowing.
However, using [depthBiasConstantFactor] and [depthBiasSlopeFactor] can introduce new rendering problems when a polygon viewed at an extremely sharp angle causes the bias equation to generate a very large z value. This in effect pushes the polygon extremely far away from the original surface in the shadow map. One way to help alleviate this particular problem is to use [depthBiasClamp], which provides an upper bound (positive or negative) on the magnitude of the z bias calculated.