Fish-eye effect corrections in JavaScript ray-caster engine

496 views Asked by At

I am building a ray-casting engine in JavaScript using the canvas, I've built the ray-caster and its fully working, except for one problem, there is a fish-eye effect, I tried doing corrections

var d = (Math.sqrt( Math.pow((rays[i].x - rays[i].newX),2 )+Math.pow((rays[i].y - rays[i].newY),2 )))

// my attempted corection
var correctedDis = d * Math.cos((rays[i].rayAngle - player.rot)) 

But it doesn't yield any results :

The raycaster when i run it

I double checked the math, and i didn't see any problems, but i may still be doing this wrong because i am in seventh grade and have not learned much about trigonometry yet,(only took the khan academy course) so can someone let me know if there's a better or easier way to do it, Thank you in advance.

(I also think i may be doing the shading wrong, if anyone can help with that.)

EDIT: I forgot to include you can use the WASD keys to control the demo, and the image is flipped but that will be an easy fix. My full code if you need it:

const scale = (num, in_min, in_max, out_min, out_max) => {
  return (num - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

tex_stone = document.getElementById("stone")
c3 = document.getElementById("c3");
c = document.getElementById("c");
w = c.width;
h = c.height;
ctx = c.getContext("2d");
fctx = c3.getContext("2d")
var tilesize = 30;
var walls = [];
var rays = [];
rayangle = 0.5;
lowrayangle = -0.5;

var player = {
  x: 80,
  y: 80,
  size: 10,
  speed: 1.3,
  dir: 0,
  rot: 0,
  rotSpeed: 0.05,
  rotDir: 0,
}

map = [];



for (y = 0; y < map.length; y++) {
  for (x = 0; x < map[y].length; x++) {

    ctx.fillStyle = ["white", "black"][map[y][x]];
    ctx.fillRect(x * tilesize, y * tilesize, tilesize, tilesize);


  }
}





function keydown(event) {
  switch (event.keyCode) {
    case 87:
      player.dir = 1;
      break;
    case 83:
      player.dir = -1;
      break;
    case 68:
      player.rotDir = -1;
      break;
    case 65:
      player.rotDir = 1;
      break;
  }

}

function keyup(event) {
  switch (event.keyCode) {
    case 87:
      player.dir = 0;
      break;
    case 83:
      player.dir = 0;
      break;
    case 68:
      player.rotDir = 0;
      break;
    case 65:
      player.rotDir = 0;
      break;
  }

}

window.requestAnimationFrame(update)

function update() {
  if (player.rot == 6.28319) {
    player.rot = 0;
  }
  var map = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],

  ]

  fctx.clearRect(0, 0, w, h)

  ctx.clearRect(0, 0, w, h);
  rays = [];
  //move


  rotstep = player.rotSpeed * player.rotDir
  movestep = player.speed * player.dir;
  player.rot += rotstep;
  rayangle += rotstep;
  lowrayangle += rotstep;
  newY = player.y - Math.cos(player.rot) * movestep;
  newX = player.x - Math.sin(player.rot) * movestep;

  leftbY = (player.y - 10) - Math.cos(player.rot) * movestep;
  leftbX = (player.x - 10) - Math.sin(player.rot) * movestep;
  rightbY = (player.y + 10) - Math.cos(player.rot) * movestep;
  rightbX = (player.x + 10) - Math.sin(player.rot) * movestep;



  //borders 
  var leftcol = Math.floor((leftbX) / tilesize)
  var leftrow = Math.floor((leftbY) / tilesize)
  var rightcol = Math.floor((rightbX) / tilesize)
  var rightrow = Math.floor((rightbY) / tilesize)
  if (map[rightcol][rightrow] == 0 && map[leftcol][leftrow] == 0) {
    player.y = newY;
    player.x = newX;
  }

  //map
  for (y = 0; y < map.length; y++) {
    for (x = 0; x < map[y].length; x++) {

      ctx.fillStyle = ["white", "black"][map[y][x]];
      ctx.fillRect(x * tilesize, y * tilesize, tilesize, tilesize);

    }
  }



  //rays  

  for (i = lowrayangle; i < rayangle; i += 0.01) {

    var detail = 0.5;
    var rayLn = 1000;
    var memX;
    var memY;
    for (j = 0; j < rayLn; j += detail) {
      var lY = player.y - Math.cos(i) * j;
      var lX = player.x - Math.sin(i) * j;

      memX = lX;
      memY = lY;

      lX = lX - (lX % tilesize);
      lY = lY - (lY % tilesize);

      if (map[lY / tilesize][lX / tilesize] == 1) {
        break;
      }
    }

    var ray = {
      rayAngle: i,
      newY: memY,
      newX: memX,
      x: player.x,
      y: player.y
    }
    rays.push(ray)
  }

  for (i = 0; i < rays.length; i++) {
    if (rays[i].rayAngle == 6.28319) {
      rays[i].rayAngle = 0;
    }
  }
  for (i = 0; i < rays.length; i++) {
    ctx.beginPath();
    ctx.moveTo(rays[i].x, rays[i].y)
    ctx.lineTo(rays[i].newX, rays[i].newY)
    ctx.stroke();

    var d = (Math.sqrt(Math.pow((rays[i].x - rays[i].newX), 2) + Math.pow((rays[i].y - rays[i].newY), 2)))

    // my attempted corection
    var correctedD = d * Math.cos((rays[i].rayAngle - player.rot))

    var rayW = w / rays.length
    var light = scale(correctedD, 0, 700, 255, 0)
    var threeDRayHeight = scale(correctedD, 0, 700, h, 10)
    fctx.fillStyle = 'rgb(' + light + ',' + light + ',' + light + ')';
    fctx.fillRect(i * rayW, 200 - threeDRayHeight * 0.5, rayW + 1, threeDRayHeight)
    fctx.fillStyle = "blue"
    fctx.fillRect(i * rayW, (200 - threeDRayHeight * 0.5) + threeDRayHeight, rayW + 1, 1000)
    fctx.fillStyle = "red"
    fctx.fillRect(i * rayW, (200 - threeDRayHeight * 0.5) - 1000, rayW + 1, 1000)


  }
  //player
  markerY = player.y - Math.cos(player.rot) * 30;
  markerX = player.x - Math.sin(player.rot) * 30;
  ctx.fillStyle = "red";
  ctx.beginPath();
  ctx.ellipse(player.x, player.y, player.size, player.size, player.rot * (Math.PI / 180), 0, 2 * Math.PI);
  ctx.fill();

  window.requestAnimationFrame(update)
}

document.addEventListener("keydown", function(event) {
  keydown(event);
})
document.addEventListener("keyup", function(event) {
  keyup(event);
})
#c3 {
  background-color: black;
}
<img src="https://i.pinimg.com/originals/16/e1/6b/16e16b80294d07b81cfafaefdf1d2909.jpg" width=4 00px; style="display: none;" id="stone">
<canvas width="600px" height="450px" id="c3"></canvas>
<canvas width="600px" height="450px" id="c"></canvas>

0

There are 0 answers