0
\$\begingroup\$

I come for theory help with a classic raycast algorithm (currently, based on DDA). The problem is not with the algorithm itself, but with the floor rendering code.

I left the code of my raycast render method below (actual DDA implementation is left out, I think that's working as expected). The basic idea it uses PLAYER_HEIGHT (position of camera, currently set to HORIZON), and the formula PLAYER_HEIGHT/(y- HORIZON). In the canvas, the bottom pixel of y is equal to SCREEN_HEIGHT, and goes “up” until it reaches HORIZON - 1. Canvas element pixels go from 0,0 (top left corner) to screen_width, screen_height (bottom right corner). That's the distance that will be used for interpolating the floor coordinates x and y according to the vector direction of the player and a camera plane perpendicular to the direction vector.

function render(canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D, currentTime: number) {
  const deltaTime = (currentTime - lastTime) / 1000;
  lastTime = currentTime;
  movePlayer(deltaTime);
  renderingActions(deltaTime);
  // Clear the canvas
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

  const buffer = FLOOR_TEXTURE_IMAGE_DATA.data;
  const rayIncrement = FOV / SCREEN_WIDTH;
  const wallsArray: {wallHeight: number,distance:number}[] = [];
  const rayDirsArray: {rayDirX: number,rayDirY: number, distance: number, normRayAngle: number, wallHeight: number, side:number, wallStart:number}[] = [];
  
  const dirX = Math.cos(normalizeAngle(playerAngle));
  const dirY = Math.sin(normalizeAngle(playerAngle));
  const planeX = -dirY ;
  const planeY = dirX;

    nearPlaneX1 = dirX - planeX;
    nearPlaneX2 = dirX + planeX;
    nearPlaneY1 = dirY - planeY;
    nearPlaneY2 = dirY + planeY;
    const dotDirLeft = (dirX * nearPlaneX1 + dirY * nearPlaneY1);
    const dotDirRight = (dirX * nearPlaneX2 + dirY * nearPlaneY2);
    const angleLeft = Math.acos(dotDirLeft);
    const angleRight = Math.acos(dotDirRight);
    

    const normalizedLeftX = nearPlaneX1/Math.sqrt((nearPlaneX1*nearPlaneX1) + (nearPlaneY1*nearPlaneY1));
    const normalizedLeftY = nearPlaneY1/Math.sqrt((nearPlaneX1*nearPlaneX1) + (nearPlaneY1*nearPlaneY1));

    const normalizedRightX = nearPlaneX2/Math.sqrt((nearPlaneX2*nearPlaneX2) + (nearPlaneY2*nearPlaneY2));
    const normalizedRightY = nearPlaneY2/Math.sqrt((nearPlaneX2*nearPlaneX2) + (nearPlaneY2*nearPlaneY2));

  for (let x = 0; x < SCREEN_WIDTH; x++) {
      const rayAngle = playerAngle - FOV / 2 + x * rayIncrement;
      const {distance, rayDirX, rayDirY, normRayAngle, side} = castRay(rayAngle);
      
      // Calculate wall height once
      const wallHeight = Math.min(SCREEN_HEIGHT,
          (SCREEN_HEIGHT * WALL_SCALE) / (distance * DISTANCE_SCALE / WORLD_SCALE));
          

      const wallStart = (HORIZON - wallHeight) / 2  
      wallsArray.push({wallHeight, distance});  
      rayDirsArray.push({rayDirX, rayDirY, distance, normRayAngle, wallHeight, side, wallStart});
  }

  for(let y = SCREEN_HEIGHT ; y > HORIZON; y--) {

      const yDistance = ((HORIZON / (y - HORIZON)));
      const  floorXDirInc = yDistance*(normalizedRightX - normalizedLeftX)/ (SCREEN_WIDTH);
      const floorYDirInc =  yDistance*(normalizedRightY - normalizedLeftY) / (SCREEN_WIDTH);
      floorYStart = worldToGrid(playerY) + yDistance*(normalizedLeftY);
      floorXStart = worldToGrid(playerX) + yDistance*(normalizedLeftX);


      for(let x = 0; x < SCREEN_WIDTH; x++){

        const cellX = Math.floor(Math.abs(floorXStart));
        const cellY = Math.floor(Math.abs(floorYStart));


        floorXStart+= floorXDirInc;
        floorYStart+=floorYDirInc;


        const pixelDestIndex = (y * SCREEN_WIDTH + x) * 4;

        if (debug) {
          console.log(`FloorStartX ${floorXStart} FloorStartY ${floorYStart}`)
          console.log(`CellX ${cellX} CellY ${cellY}`)
          console.log(`WORLD posX ${playerX} posY${playerY} GRID posX ${worldToGrid(playerX)} posY ${worldToGrid(playerY)}`)
          console.log(`FloorXInd ${floorXDirInc} FloorYInc ${floorYDirInc}`)
          console.log(`MapRatio ${mapRatio} MapAcc ${mapAcc}`)
          debug = false;
        }
        if((cellX + cellY) % 2){

            buffer[pixelDestIndex] = 0;     // Using fixed color for testing
            buffer[pixelDestIndex + 1] = 0;
            buffer[pixelDestIndex + 2] = 0;
            buffer[pixelDestIndex + 3] = 255;
        }else
            buffer[pixelDestIndex] = 255;     // Using fixed color for testing
            buffer[pixelDestIndex + 1] = 255;
            buffer[pixelDestIndex + 2] = 255;
            buffer[pixelDestIndex + 3] = 255;
        
      }
  }
  ctx.putImageData(FLOOR_TEXTURE_IMAGE_DATA, 0,0);
  if(RENDER_WALLS)
    wallsArray.forEach((wallProperties, index) => {
      const brightness = Math.max(255 - (wallProperties.distance * DISTANCE_SCALE / WORLD_SCALE) *  BRIGHT_FACTOR, 0);
      ctx.fillStyle = `rgb(${brightness}, 0, 0)`;
      ctx.fillRect(index, (SCREEN_HEIGHT - wallProperties.wallHeight) / 2, 1, wallProperties.wallHeight);
    })
  requestAnimationFrame((time) => render(canvas, ctx, time));
}             

Walls and floors

enter image description here

The main problem is in my rendering floor technique. New tiles “appear” below the walls. The desired effect would be, if there is a wall in a map position (7,4) the floor tiles around that should be always the same. But right now, as the player moves, “new” tiles slide from behind the wall, so depending on the player position and angle, near to (7,4) the floor tile may be appear white or blue, so it is not “in sync”.

I think the problem is the distance of the wall I get using DDA and the distance related to the difference between the current row in the screen space and my HORIZON is different, they are not “in sync” and it is normal because the are two different “types” of distances? I'm lost here. Thank you all for taking your time with this.

\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.