OpenGL pixels/fragments are conceptually 1x1 squares centered on half-integer pixels. The OpenGL 4.5 specification states:
A fragment is located by its lower left corner, which lies on integer grid coordinates. Rasterization operations also refer to a fragment’s center, which is offset by (1/2,1/2) from its lower left corner (and so lies on half-integer coordinates).
Rasterizers typically assume that pixel centers lie on the integer grid. Since I am attempting to implement a correct OpenGL triangle fill I want to know if the following procedures seems sound.
Let's take the simple case of a triangle with clip coordinates (-1,-1), (+1,-1), (0,+1) as shown on the left of the figure below (assume orthographic projection and z=0). Assume we have a (small 5x5 frame buffer) that we map our triangle to via glViewport(0,0,5,5)
as shown on the right yielding the triangle in device coordinates with vertices (0,0), (5,0), (2.5,5).
As you can see, the 13 fragments (shaded pixels in image) with centers inside the triangle should be generated by the rasterizer. Note that the fragment centers are on half-integer coordinates. To implement the OpenGL spec, this is what the result needs to be.
A scan-line polygon fill would determine the x-spans where the scan lines intersect the triangle, but the scan lines are at half-integer y-values as shown in the following figure:
A hardware/firmware rasterizer will assume the pixel centers are on the integer grid since this is the most efficient way to perform the fill. In the figure below I have shifted the devices coordinates of the triangle by (-0.5, -0.5) to place the centers on the integer grid:
Note that the pixels centers are now indeed on the integer grid. This rasterizer would simply add back (0.5,0.5) to each fragment center before being passed to the fragment shader. At least that is my plan.
Handling texture coordinates seems to be straight-forward. Imagine the I assigned texture coordinates (0,0), (1,0), (0.5,1) as shown below. The image on the left is using half-integer pixel-centers (the OpenGL way) and the image on the right is using integer pixel centers (the hardware way). The texture coordinates (any attached fragment attributes for that matter) end up having the same values either way -- i.e., nothing special needs to be done.
So does my approach seem correct?
- Add (-0.5,-0.5) to each fragment coordinate,
- use the hardware efficient fill,
- add (0.5, 0.5) back in when generating fragment centers, and
- don't sweat the other fragment attributes (they just work out).
ceil(y)
which can be easily done in floating-point or fixed point astruncate(y + 0.9999)
-- in fixed point with a 16-bit fraction this is just(y + 0x0FFFF) >> 16
. With half-integer scan lines you would subtract 0.5, do the ceil op, and add 1/2 back out. It seemed easier just just do the subtractions once up front. – Horalx
asceil(x)
(e.g., on scan liney = 4
, the first integer for span[1.75, 2.75]
isceil(1.75) = 2
. If I am using half-integer pixels then I would find the first half-integer >= x asceil(x + 0.5) - 0.5
(e.g., on scan liney = 4.5
the first half-integer on span[2.25,2.75]
isceil(2.25 + 0.5) - 0.5 = 2.5
). In fixed point((x + 0x17FFF) >> 16) << 16) - 0x8000
. Yuck. – Horal