I'm not really sure if to round them up ( +0.05f ) or down ( -0.05f ) or even if to use higher values
Plain c has only two ways to perform rounding.
Using "ceil" always rounds up (when the fractional part is > .0)
int i= (int) ceil( f );Using "floor" always rounds down (thus removes the fractional part):
int i= (int) floor( f );When doing a simple integer-cast (that's what you're doing at the moment), you're rounding down:
float f= ...;
int i= (int)f;When filling polygons I always found it convenient to address the upper left of a pixel, assuming a pixel ranges from [0.0 ... 1.0[.
But when rounding down, this position can move *outside* of the triangle (compare
here: by rounding up the upper left of a pixel is always inside the triangle).
If you imagine some simple gradient interpolation ranging from 0..255 inside your triangle, you suddenly have to deal with values outside of the triangle, values going negative and other terrible things.
So I always round up.
Alternatively you can look at the pixel centers but it makes things just more complicated...
Which bits are missing sub pixel accuracy?
Let's assume you're drawing a triangle which consists of three floating point vertices v1 (top), v2 (middle), v3 (bottom).
So when you start drawing at the top (v1), the left and right edges have both the same x-coordinate (v1.x) and the current y-position is some floating point value v1.y.
For this particular scanline there's nothing to draw because the length of the scanline is 0 (left == right).
Now we have to progress to the next (integer!) scanline.
So instead of just adding the y-deltas to get to the next scanline, we just add as much as is necessary to reach the next integer y.
And when drawing a scanline, it's exactly the same principle:
The scanline starts at some floating point x coordinate but the first pixel you're plotting is at the next integer coordinate. So you just add a fraction of your scanline-deltas to reach the first actual pixel.
That being said you never really need floating point precision in screen space.
Simply convert all your coordinates down to integer in the projection stage and just leave a reasonable amount of extra bits for sub-pixel precision.
This way you get all the fractional-part math with a bit of shifts and bit-masking.