Why is the floor in my raycaster seemingly "misaligned"?

Viewed 56

I have been working on a doom/wolfenstein style raycaster for a while now. I implemented the "floor raycasting" to the best of my ability, roughly following a well known raycaster tutorial. It almost works, but the floor tiles seem slightly bigger than they should be, and they don't "stick", as in they don't seem to align properly and they slide slightly as the player moves/rotates. Additionally, the effect seems worsened as the FOV is increased. I cannot figure out where my floor casting is going wrong, so any help is appreciated.

Here is a (crappy) gif of the glitch happening

Here is the most relevant part of my code:

void render(PVector pos, float dir) {
    ArrayList<FloatList> dists = new ArrayList<FloatList>();
    
    for (int i = 0; i < numColumns; i++) {
      float curDir = atan((i - (numColumns/2.0)) / projectionDistance) + dir;
      
      // FloatList because it returns a few pieces of data
      FloatList curHit = cast(pos, curDir);
      
      // normalize distances with cos
      curHit.set(0, curHit.get(0) * cos(curDir - dir));
      dists.add(curHit);
    }
    
    screen.beginDraw();
    
    screen.background(50);
    screen.fill(0, 30, 100);
    screen.noStroke();
    screen.rect(0, 0, screen.width, screen.height/2);
    
    screen.loadPixels();
    
    PImage floor = textures.get(4);
    
    // DRAW FLOOR
    for (int y = screen.height/2 + 1; y < screen.height; y++) {
      float rowDistance = 0.5 * projectionDistance / ((float)y - (float)rY/2);
      
      // leftmost and rightmost (on screen) floor positions
      PVector left = PVector.fromAngle(dir - fov/2).mult(rowDistance).add(p.pos);
      PVector right = PVector.fromAngle(dir + fov/2).mult(rowDistance).add(p.pos);
      
      // current position on the floor
      PVector curPos = left.copy();
      PVector stepVec = right.sub(left).div(screen.width);
      
      float b = constrain(map(rowDistance, 0, maxDist, 1, 0), 0, 1);        

      for (int x = 0; x < screen.width; x++) {        
        color sample = floor.get(floor((curPos.x - floor(curPos.x)) * floor.width), floor((curPos.y - floor(curPos.y)) * floor.height));
        
        screen.pixels[x + y*screen.width] = color(red(sample) * b, green(sample) * b, blue(sample) * b);
        
        curPos.add(stepVec);
      }
    }
    updatePixels();
  }

If anyone wants to look at the full code or has any questions, ask away.

1 Answers

Ok, I seem to have found a "solution". I will be the first to admit that I do not understand why it works, but it does work. As per my comment above, I noticed that my rowDistance variable was off, which caused all of the problems. In desperation, I changed the FOV and then hardcoded the rowDistance until things looked right. I plotted the ratio between the projectionDistance and the numerator of the rowDistance. I noticed that it neatly conformed to a scaled cos function. So after some simplification, here is the formula I came up with:

float rowDistance = (rX / (4*sin(fov/2))) / ((float)y - (float)rY/2); 

where rX is the width of the screen in pixels.

If anyone has an intuitive explanation as to why this formula makes sense, PLEASE enlighten me. I hope this helps anyone else who may have this problem.

Related