Line rasterisation: Cover all pixels, regardless of line gradient?

前端 未结 6 2059
南笙
南笙 2020-12-25 13:30

Basically, I want to use a line algo to determine which cells to check for collisions for my raycaster.

Bresenham isn\'t great for this as it uses a unified-thicknes

相关标签:
6条回答
  • 2020-12-25 14:11

    You could find all the intersections your ray has with the horizontal grid lines, and then mark all the cells on a row that either have an intersection point on one side, or are between the two cells with the intersections on the row.

    Finding the intersections can be done by starting from the origin, advancing the point to the first intersection (and marking the cells in the process), finding out the vector that takes you from an intersection to the next (both these operations are basic similar triangles (or trig)) and then advancing column by column until you've gone far enough. Advancing column by column involves one vector addition per column, and a small loop to fill in the cells between the ones with intersections. Replace "mark" with "process" if you're processing the cells on the fly - this algorithm is guaranteed to mark each cell only once.

    The same could be done with the vertical lines, but grids are generally stored in horizontal slices so I chose that. If you're using trig, you'll need to handle straight horizontal lines with a special case.

    By the way, as far as I know, this is how old grid-based raycaster "3D" games (like Wolfenstein 3D) were done. I first read about this algorithm from this book, eons ago.

    0 讨论(0)
  • 2020-12-25 14:12

    Without loss of generality, assume x2 >= x1, then

    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;
      }
    }
    

    The floor calls can probably be written just as a cast-to-integer.

    0 讨论(0)
  • 2020-12-25 14:17

    I had exactly the same problem as you and found an very simple solution. Usually, Bresenham has two consecutive if's to determine whether it should increase the coordinate for the two dimensions:

    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;
            }
        }
    }
    

    Now all you have to do is to insert an else before the second 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;
            }
        }
    }
    

    Now the algorithm doesn't change both coordinates at once anymore. I haven't thoroughly tested this but it seems to work pretty well.

    0 讨论(0)
  • 2020-12-25 14:20

    There is an interesting article available in GPU Gems, maybe it can help you: Chapter 22. Fast Prefiltered Lines

    0 讨论(0)
  • 2020-12-25 14:25

    What about Bresenham with an additional constraint that no diagonal moves are allowed: Generate the points with the traditional algorithm, then as a post-processing step insert extra steps needed to make only orthogonal movements.

    0 讨论(0)
  • 2020-12-25 14:30

    This thread old, but I thought it'd be worth putting this on the Internet:

    // 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)));
        }
    }
    
    0 讨论(0)
提交回复
热议问题