Проблемы растеризации треугольника
Я пытаюсь написать пару функций для рисования (заполненных) треугольников с плоским верхом и плоским дном. По большей части они работают, однако трещины все еще иногда видны между соседними треугольниками, которые имеют общую сторону. Я использую метод интерполяции, в котором я построчно растряю треугольник, вычисляя новые левый и правый пределы на каждом шаге. Многое из этого объясняется в коде, но вот общая идея (для плоского дна):
1) Найти DY (высота)
2) Найти dy/dx (наклон) от верхней точки до каждой нижней точки
3) Переместитесь в начальную строку (пол (верхняя точка)) и найдите начальные координаты x-start и x-end
4) Перейти к следующей строке, рассчитать новые начальные и конечные пределы...
Я должен отметить, что трещины видны только между треугольниками, которые соединяются по бокам, а не сверху / снизу. Я был в этом так долго, я не знаю, что попробовать больше. Я думаю, что логика является твердой, возможно, любые трещины из-за ошибки с плавающей запятой. Буду очень признателен за отзыв.
typedef unsigned int uint32;
void Line(uint32 y, uint32 x_left, uint32 x_right); //Draws a horizontal line
struct vector2
{
float x,y;
};
//--------------------------------------------------------------------------------
// Draws a flat bottom triangle from top to bottom
//--------------------------------------------------------------------------------
void Draw_Bottom_Tri_SOLID(Vector2 p0, Vector2 p1, Vector2 p2)
{
//Point order:
//Bottom left: p0
//Bottom right: p1
//Top point: p2
//calculate dy
float dy = p2.y - p0.y;
//dx/dy for the left and right edges
float dxdy_left = (p0.x - p2.x)/dy;
float dxdy_right = (p1.x - p2.x)/dy;
//Since we start the raster process at floor(p2.y)
//we need to shift the initial x start and x end
//postions along by this factor:
float y_bump = p2.y - floor(p2.y);
//Initial start and end x values
float xs = p2.x + dxdy_left*y_bump; //x left (start)
float xe = p2.x + dxdy_right*y_bump; //x right (end)
uint32 yb = uint32(p0.y) + 1; //y bottom, +1 for top left fill convention
uint32 yt = uint32(p2.y); //y top, use casting instead of std::floor
//Draw lines
for (uint32 i = yt; i >= yb; i--)
{
//Set left and right limits, use casting instead of std::floor
uint32 left = uint32(xs) + 1; //+1 for top left fill convention
uint32 right = uint32(xe);
//Draw line, can also be std::fill or simply a for loop.
Line(i, left, right);
//Increment limits
xs += dxdy_left;
xe += dxdy_right;
}
} //End: Draw_Bottom_Tri_SOLID()
//--------------------------------------------------------------------------------
// Draws a flat top triangle from bottom to top
//--------------------------------------------------------------------------------
void Draw_Top_Tri_SOLID(Vector2 p0, Vector2 p1, Vector2 p2)
{
//Point order:
//Top left: p0
//Top right: p1
//Bottom point: p2
//calculate dy (height)
float dy = p0.y - p2.y;
//dx/dy for the left and right edges
float dxdy_left = (p0.x - p2.x)/dy;
float dxdy_right = (p1.x - p2.x)/dy;
//Find shifting factor
float y_bump = ceil(p2.y) - p2.y;
//Initial start and end x values
float xs = p2.x + dxdy_left*y_bump; //x left (start)
float xe = p2.x + dxdy_right*y_bump; //x right (end)
uint32 yb = uint32(p2.y) + 1; //y bottom, +1 for top left fill convention
uint32 yt = uint32(p0.y) ; //y top
//Draw lines
for (uint32 i = yb; i <= yt; i++)
{
//Set left and right limits
uint32 left = uint32(xs) + 1; //+1 for top left fill convention
uint32 right = uint32(xe);
//Draw line, can be std::fill or simply a for loop.
Line(i, left, right);
//Increment limits
xs += dxdy_left;
xe += dxdy_right;
}
} //End: Draw_Top_Tri_SOLID()
1 ответ
Ошибки с плавающей точкой кажутся вероятными. Вам нужно убедиться, что вы получите одинаковое значение для двух соседних ребер. Это может быть немного сложно исправить с помощью значений с плавающей запятой, когда вы накапливаете таким образом. У вас есть несколько вариантов в зависимости от ваших потребностей:
- Вместо этого используйте фиксированную точку. Это также, вероятно, будет быстрее.
- Если перерасход не является проблемой, вы можете просто добавить достаточно большой эпсилон к вашим значениям с плавающей запятой.