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 :
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>