基本上,我想使用行算法来确定哪些单元格可以检查我的raycaster的碰撞。
Bresenham并不是很好,因为它使用统一厚度方法,这意味着它忽略了至少半覆盖线的单元格。根本不是很好,因为这意味着我的线的某些部分没有被检查与单元格的交叉点,导致错误。
我似乎无法找到任何“粗线”算法,任何人都可以帮我找到一个吗?
格林:我想要什么。
红色:我目前拥有和不想要的东西。
答案 0 :(得分:7)
我和你有完全相同的问题,并找到了一个非常简单的解决方案。通常,Bresenham有两个连续的if来确定它是否应该增加两个维度的坐标:
public void drawLine(int x0, int y0, int x1, int y1, char ch) {
int dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2; // error value e_xy
for (;;) {
put(x0, y0, ch);
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
// horizontal step?
if (e2 > dy) {
err += dy;
x0 += sx;
}
// vertical step?
if (e2 < dx) {
err += dx;
y0 += sy;
}
}
}
现在您要做的就是在第二个else
之前插入if
:
public void drawLineNoDiagonalSteps(int x0, int y0, int x1, int y1, char ch) {
int dx = Math.abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
int dy = -Math.abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
int err = dx + dy, e2;
for (;;) {
put(x0, y0, ch);
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
// EITHER horizontal OR vertical step (but not both!)
if (e2 > dy) {
err += dy;
x0 += sx;
} else if (e2 < dx) { // <--- this "else" makes the difference
err += dx;
y0 += sy;
}
}
}
现在算法不再同时改变两个坐标。 我没有对此进行过彻底的测试,但似乎效果很好。
答案 1 :(得分:5)
这个帖子很老了,但我认为将它放在互联网上是值得的:
// This prints the pixels from (x, y), increasing by dx and dy.
// Based on the DDA algorithm (uses floating point calculations).
void pixelsAfter(int x, int y, int dx, int dy)
{
// Do not count pixels |dx|==|dy| diagonals twice:
int steps = Math.abs(dx) == Math.abs(dy)
? Math.abs(dx) : Math.abs(dx) + Math.abs(dy);
double xPos = x;
double yPos = y;
double incX = (dx + 0.0d) / steps;
double incY = (dy + 0.0d) / steps;
System.out.println(String.format("The pixels after (%d,%d) are:", x, y));
for(int k = 0; k < steps; k++)
{
xPos += incX;
yPos += incY;
System.out.println(String.format("A pixel (%d) after is (%d, %d)",
k + 1, (int)Math.floor(xPos), (int)Math.floor(yPos)));
}
}
答案 2 :(得分:2)
不失一般性,假设x2> = x1,那么
int x = floor(x1);
int y = floor(y1);
double slope = (x2 - x1) / (y2 - y1);
if (y2 >= y1) {
while (y < y2) {
int r = floor(slope * (y - y1) + x1);
do {
usepixel(x, y);
++x;
} while (x < r);
usepixel(x, y);
++y;
}
}
else {
while (y > y2) {
int r = floor(slope * (y - y1) + x1);
do {
usepixel(x, y);
++x;
} while (x < r);
usepixel(x, y);
--y;
}
}
楼层调用可能只是作为一个整数写入。
答案 3 :(得分:1)
GPU Gems中有一篇有趣的文章,也许它可以帮助你:Chapter 22. Fast Prefiltered Lines
答案 4 :(得分:0)
Bresenham还有一个额外的约束条件,即不允许对角线移动:使用传统算法生成点,然后作为后处理步骤插入仅进行正交移动所需的额外步骤。
答案 5 :(得分:0)
您可以找到光线与水平网格线相交的所有交点,然后标记一行上有交点的行上的所有单元格,或者在行上有交点的两个单元格之间。
找到交叉点可以通过从原点开始,将点前进到第一个交点(并标记过程中的单元格),找出从交叉点到下一个交点的向量(这两个操作都是基本相似的三角形(或三角形))然后逐列前进,直到你走得足够远。逐列前进涉及每列一个向量加法,以及一个小循环以填充具有交叉点的单元格之间的单元格。如果您正在动态处理单元格,请将“mark”替换为“process” - 此算法保证仅标记每个单元格一次。
垂直线也可以这样做,但网格通常存储在水平切片中,所以我选择了它。如果您正在使用trig,则需要使用特殊情况处理直线水平线。
顺便说一下,据我所知,这是基于网格的raycaster“3D”游戏(如Wolfenstein 3D)的完成程度。我在很久以前就从this book开始阅读这个算法。