РАЗНОЕ
7.1. Субпиксельная точность
Субпиксельная точность означает следующее: только те пикселы, центры которых
лежат внутри данного многоугольника, должны быть нарисованы. На самом деле,
в этом определении можно использовать любую (зафиксированную) точку внутри
пиксела, единственным последствием этого будет сдвиг всего экрана меньше, чем
на 0.5 пиксела.
Необходимость в субпиксельной точности появляется только из-за того, что
дисплеи дискретны, состоят из пикселов. Чем меньше становятся пикселы (то
есть, чем выше разрешение), тем меньше важна эта точность. Однако разницу
можно почувствовать даже на разрешениях порядка 1280x1024, а тем более при
обычных для 3D engine 320x200 или 640x480.
Реализовать субпиксельную точность на редкость просто. Представьте себе, что
мы рисуем грань. Обычно мы начинаем рисовать ее с какого-то нецелого start_y,
так как при преобразованиях (например, проецировании) у нас получаются вовсе
не целые числа. Обычно их просто округляют. В результате типичная процедура
рисования треугольника, которая начинает отрисовку с самой верхней точки A и
идет вниз по строкам, на каждой строке пересчитывая координаты начала и конца
рисуемого отрезка как
sx_start += dx_start;
sx_end += dx_end;
теряет субпиксельную точность из-за этого самого округления, так как sx_start
инициализируется как A.sx, A.sx соотвествует линии y = A.sy, а рисовать мы
начинаем с какого-то округленного значения. Кстати, ни один из примеров к
FAQ'у потерей субпиксельной точности не страдает, так как для каждой строки
sx_start и sx_end заново вычисляются по точной формуле.
Точная формула для расчета sx_start выглядит как
sx_start = A.sx + dx_start * (current_sy - A.sy),
и для самой первой линии мы тоже должны ее честно применить, а не просто
положить sx_start = A.sx. Получаем, что
sx_start = A.sx + dx_start * (ceil(A.sy) - A.sy);
sx_end = A.sx + dx_end * (ceil(A.sy) - A.sy);
Ту же самую операцию надо сделать и со всем остальными переменными, которые
мы будем интерполировать по ребрам (например u, v, интенсивность для Гуро);
и то же самое надо сделать при переходе с ребра на ребро.
// ...
u_start += du_start * (ceil(start_y) - start_y);
u_end += du_end * (ceil(start_y) - start_y);
// ...
Ну и, разумеется, рисовать начинать надо с той строки, которую мы использовали
в формулах. То есть, ceil(start_y).
Вот и все. На скорость работы это не влияет вообще, а грани сразу перестают
мелко и противно дрожать при перемещении, особенно сильно это заметно при
маленьких поворотах (в доли градуса).