24.6. Polygons

A polygon results from the decomposition of a triangle strip, triangle fan or a series of independent triangles. Like points and line segments, polygon rasterization is controlled by several variables in the VkPipelineRasterizationStateCreateInfo structure.

24.6.1. Basic Polygon Rasterization

The first step of polygon rasterization is to determine whether the triangle is back-facing or front-facing. This determination is made based on the sign of the (clipped or unclipped) polygon’s area computed in framebuffer coordinates. One way to compute this area is:

\[ a = -{1 \over 2}\sum_{i=0}^{n-1} x_f^i y_f^{i \oplus 1} - x_f^{i \oplus 1} y_f^i \]

where $x_f^i$ and $y_f^i$ are the $x$ and $y$ framebuffer coordinates of the $i$ th vertex of the $n$ -vertex polygon (vertices are numbered starting at zero for the purposes of this computation) and $i \oplus 1$ is $(i + 1)~ \textrm{mod}~ n$ . The interpretation of the sign of this value is determined by the frontFace property of the VkPipelineRasterizationStateCreateInfo in the currently active pipeline, which takes the following values:

 

typedef enum VkFrontFace {
    VK_FRONT_FACE_COUNTER_CLOCKWISE = 0,
    VK_FRONT_FACE_CLOCKWISE = 1,
} VkFrontFace;

When this is set to VK_FRONT_FACE_COUNTER_CLOCKWISE, a triangle with positive area is considered front-facing. When it is set to VK_FRONT_FACE_CLOCKWISE, a triangle with negative area is considered front-facing. Any triangle which is not front-facing is back-facing, including zero-area triangles.

Once the orientation of triangles is determined, they are culled according to the setting of cullMode property in the VkPipelineRasterizationStateCreateInfo of the currently active pipeline, which takes the following values:

 

typedef enum VkCullModeFlagBits {
    VK_CULL_MODE_NONE = 0,
    VK_CULL_MODE_FRONT_BIT = 0x00000001,
    VK_CULL_MODE_BACK_BIT = 0x00000002,
    VK_CULL_MODE_FRONT_AND_BACK = 0x00000003,
} VkCullModeFlagBits;

If the cullMode is set to VK_CULL_MODE_NONE no triangles are discarded, if it is set to VK_CULL_MODE_FRONT_BIT front-facing triangles are discarded, if it is set to VK_CULL_MODE_BACK_BIT then back-facing triangles are discarded and if it is set to VK_CULL_MODE_FRONT_AND_BACK then all triangles are discarded. Following culling, fragments are produced for any triangles which have not been discarded.

The rule for determining which fragments are produced by polygon rasterization is called point sampling. The two-dimensional projection obtained by taking the x and y framebuffer coordinates of the polygon’s vertices is formed. Fragments are produced for any pixels for which any sample points lie inside of this polygon. Coverage bits that correspond to sample points that satisfy the point sampling criteria are 1, other coverage bits are 0. Special treatment is given to a sample whose sample location lies on a polygon edge. In such a case, if two polygons lie on either side of a common edge (with identical endpoints) on which a sample point lies, then exactly one of the polygons must result in a covered sample for that fragment during rasterization. As for the data associated with each fragment produced by rasterizing a polygon, we begin by specifying how these values are produced for fragments in a triangle. Define barycentric coordinates for a triangle. Barycentric coordinates are a set of three numbers, $a$ , $b$ , and $c$ , each in the range $\lbrack 0, 1\rbrack$ , with $a + b + c = 1$ . These coordinates uniquely specify any point $p$ within the triangle or on the triangle’s boundary as

\[ p = ap_a + bp_b + cp_c \]

where $p_a$ , $p_b$ , and $p_c$ are the vertices of the triangle. $a$ , $b$ , and $c$ are determined by:

\[ a = {{\rm A}(p p_b p_c) \over {\rm A}(p_a p_b p_c)}, \quad b = {{\rm A}(p p_a p_c) \over {\rm A}(p_a p_b p_c)}, \quad c = {{\rm A}(p p_a p_b) \over {\rm A}(p_a p_b p_c)}, \]

where $A(lmn)$ denotes the area in framebuffer coordinates of the triangle with vertices $l$ , $m$ , and $n$ .

Denote an associated datum at $p_a$ , $p_b$ , or $p_c$ as $f_a$ , $f_b$ , or $f_c$ , respectively. Then the value $f$ of a datum at a fragment produced by rasterizing a triangle is given by:

Equation 24.3. triangle_perspective_interpolation

\[ f = {{a{f_a / w_a} + b{f_b / w_b} + c{f_c / w_c}} \over {a / w_a} + {b / w_b} + {c / w_c}} \]

where $w_a$ , $w_b$ , and $w_c$ are the clip $w$ coordinates of $p_a$ , $p_b$ , and $p_c$ , respectively. $a$ , $b$ , and $c$ are the barycentric coordinates of the location at which the data are produced - this must be a pixel center or the location of a sample. When rasterizationSamples is VK_SAMPLE_COUNT_1_BIT, the pixel center must be used. Depth values for triangles must be interpolated by

Equation 24.4. triangle_noperspective_interpolation

\[ z = a z_a + b z_b + c z_c \]

where $z_a$ , $z_b$ , and $z_c$ are the depth values of $p_a$ , $p_b$ , and $p_c$ , respectively.

The NoPerspective and Flat interpolation decorations can be used with fragment shader inputs to declare how they are interpolated. When neither decoration is applied, interpolation is performed as described in Equation triangle_perspective_interpolation. When the NoPerspective decoration is used, interpolation is performed in the same fashion as for depth values, as described in Equation triangle_noperspective_interpolation. When the Flat decoration is used, no interpolation is performed, and outputs are taken from the corresponding input value of the provoking vertex corresponding to that primitive.

For a polygon with more than three edges, such as are produced by clipping a triangle, a convex combination of the values of the datum at the polygon’s vertices must be used to obtain the value assigned to each fragment produced by the rasterization algorithm. That is, it must be the case that at every fragment

\[ f = \sum_{i=1}^{n} a_i f_i \]

where $n$ is the number of vertices in the polygon and $f_i$ is the value of $f$ at vertex $i$ . For each $i$ , $0 \leq a_i \leq 1$ and $\sum_{i=1}^{n}a_i = 1$ . The values of $a_i$ may differ from fragment to fragment, but at vertex $i$ , $a_i = 1$ and $a_j = 0$ for $j \neq i$ .

[Note]Note

One algorithm that achieves the required behavior is to triangulate a polygon (without adding any vertices) and then treat each triangle individually as already discussed. A scan-line rasterizer that linearly interpolates data along each edge and then linearly interpolates data across each horizontal span from edge to edge also satisfies the restrictions (in this case, the numerator and denominator of equation Equation triangle_perspective_interpolation are iterated independently and a division performed for each fragment).

24.6.2. Polygon Mode

The interpretation of polygons for rasterization is controlled using the polygonMode member of VkPipelineRasterizationStateCreateInfo, which takes the following values:

 

typedef enum VkPolygonMode {
    VK_POLYGON_MODE_FILL = 0,
    VK_POLYGON_MODE_LINE = 1,
    VK_POLYGON_MODE_POINT = 2,
} VkPolygonMode;

The polygonMode selects which method of rasterization is used for polygons. If polygonMode is VK_POLYGON_MODE_POINT, then the vertices of polygons are treated, for rasterization purposes, as if they had been drawn as points. VK_POLYGON_MODE_LINE causes polygon edges to be drawn as line segments. VK_POLYGON_MODE_FILL causes polygons to render using the polygon rasterization rules in this section.

Note that these modes affect only the final rasterization of polygons: in particular, a polygon’s vertices are shaded and the polygon is clipped and possibly culled before these modes are applied.

24.6.3. Depth Bias

The depth values of all fragments generated by the rasterization of a polygon can be offset by a single value that is computed for that polygon. This behavior is controlled by the depthBiasEnable, depthBiasConstantFactor, depthBiasClamp, and depthBiasSlopeFactor members of VkPipelineRasterizationStateCreateInfo, or by the corresponding parameters to the vkCmdSetDepthBias command if depth bias state is dynamic.

 

void vkCmdSetDepthBias(
    VkCommandBuffer                             commandBuffer,
    float                                       depthBiasConstantFactor,
    float                                       depthBiasClamp,
    float                                       depthBiasSlopeFactor);

  • commandBuffer is the command buffer into which the command will be recorded.
  • depthBiasConstantFactor is a scalar factor controlling the constant depth value added to each fragment.
  • depthBiasClamp is the maximum (or minimum) depth bias of a fragment.
  • depthBiasSlopeFactor is a scalar factor applied to a fragment’s slope in depth bias calculations.

If depthBiasEnable is VK_FALSE, no depth bias is applied and the fragment’s depth values are unchanged.

depthBiasSlopeFactor scales the maximum depth slope of the polygon, and depthBiasConstantFactor scales an implementation-dependent constant that relates to the usable resolution of the depth buffer. The resulting values are summed to produce the depth bias value which is then clamped to a minimum or maximum value specified by depthBiasClamp. depthBiasSlopeFactor, depthBiasConstantFactor, and depthBiasClamp can each be positive, negative, or zero.

The maximum depth slope $m$ of a triangle is

\begin{equation} m = \sqrt{ \left({\partial z_f \over \partial x_f}\right)^2 + \left({\partial z_f \over \partial y_f}\right)^2} \end{equation}

where $(x_f, y_f, z_f)$ is a point on the triangle. $m$ may be approximated as

\begin{equation} m = \max( \left |{\partial z_f \over \partial x_f} \right |, \left |{\partial z_f \over \partial y_f} \right | ). \end{equation}

The minimum resolvable difference $r$ is an implementation-dependent parameter that depends on the depth buffer representation. It is the smallest difference in framebuffer coordinate $z$ values that is guaranteed to remain distinct throughout polygon rasterization and in the depth buffer. All pairs of fragments generated by the rasterization of two polygons with otherwise identical vertices, but $z_f$ values that differ by $r$, will have distinct depth values.

For fixed-point depth buffer representations, $r$ is constant throughout the range of the entire depth buffer. For floating-point depth buffers, there is no single minimum resolvable difference. In this case, the minimum resolvable difference for a given polygon is dependent on the maximum exponent, $e$ , in the range of $z$ values spanned by the primitive. If $n$ is the number of bits in the floating-point mantissa, the minimum resolvable difference, $r$ , for the given primitive is defined as

\begin{equation} r = 2^{e - n} \end{equation}

If no depth buffer is present, $r$ is undefined.

The bias value $o$ for a polygon is

\begin{equation} o = \begin{cases} m \times depthBiasSlopeFactor + r \times depthBiasConstantFactor & depthBiasClamp = 0\ or\ NaN \\ \min(m \times depthBiasSlopeFactor + r \times depthBiasConstantFactor, depthBiasClamp) & depthBiasClamp > 0 \\ \max(m \times depthBiasSlopeFactor + r \times depthBiasConstantFactor, depthBiasClamp) & depthBiasClamp < 0 \\ \end{cases} \end{equation}

$m$ is computed as described above. If the depth buffer uses a fixed-point representation, $m$ is a function of depth values in the range $[0,1]$ , and $o$ is applied to depth values in the same range.

For fixed-point depth buffers, fragment depth values are always limited to the range $[0,1]$ by clamping after depth bias addition is performed. Fragment depth values are clamped even when the depth buffer uses a floating-point representation.