Raycaster Competition

BlitzMax Forums/BlitzMax Programming/Raycaster Competition

Allow me to restate what Remz said in an easier to grasp manner:

If your screen is 640 pixels wide, and your FOV is 90, instead of casting one ray every 0.140625 degrees, what you need to do is imagine that the screen is suspended in front of the player, and cast the rays through the individual pixels in that screen.

If you do that, then the change in angle from one ray to the next will be greatest near the middle of the screen, and less towards the edges.


----

Now that you understand that...


Calculating the correct angle for each pixel is fairly simple if you know the rules dealing with how the lengths of the sides of a right triangle (a triangle with one corner that is 90 degrees) relates to the angles.

And those rules are:

SohCahToa

or

Sine = Opposite/Hypoteneuse
Cosine = Adjacent/Hypotenuse
Tangent = Opposite/Adjacent

Let's use the first equation as an example:

Sine = Opposite/Hypoteneuse

This means that the Sine of an angle (Which is a number you can convert into an angle) equals the length of the side across from it, divided by the hypoteneuse, which is the side across from the 90 degree corner.

(The adjacent side in the other equations is the side touching the corner you're trying to find the angle of.)

In other words:

|\
|_\

The side at an angle is the hypoteneuse. The side on the left and the side on the bottom could either be the opposite or the adjacent, depending on which corner you're trying to find the angle of.


Now let's flip that triangle over and get to calculating our angle:

``/
|/
P

P is our player. That corner there is the one we're trying to find the angle of.

H is the length of our hypoteneuse on the right. We don't know H.

A is the length of our adjacent side. For this example, that would be the left side. It is the side which is not the hypoteneuese which is touching the angle that we want to find. We do know the length of this side. Here, it is 1.

O is the length of our opposite side. This is the length of the top side, which is opposite the corner at the bottom of the triangle. We also know the length of this. Here it is 2, but this length will vary. It will be 0.5 for the centers of the first two columns of the screen to the left and right of the center of the screen which is between two pixels. And it will increase by 1 each pixel away from the center two pixels.

So, which of the three euqations will let us compute our angle with opposite and adjacent?

This one!

Tangent = Opposite/Adjacent

So we could just plug in our values into this and get the tangent of our angle, but we want an actual angle. To get that, you pass that value into a function called ArcTan.

So:
Angle = ArcTan(Opposite/Adjacent)

You can also use the ArcTan2 function, like so:

Angle = ArcTan2(Opposite, Adjacent)

ArcTan2 exists beccause adjacent approaches 0 as the angle approaches 90 degrees, and ArcTan2 can use the two values it is passed to compensate for this. (An adjacent approaching 0 causes the value passed to the ArcTan function to approach infinity if you just do the division method.)

You won't be going more than 45 degrees in either direction from center, and your adjacent length will always be 1, so this won't be an issue for you, but if you ever need to calculate the angle an object is at in relation to another object, ArcTan2 is your function.

I don't know which is faster, but I personally always use ArcTan2.

And so that's it. Now you have your angle, and you can use that to cast your ray.


Now, you might think "I don't need to do any of that angle stuff! I've gone as far as calculating the opposite and adjacent for a specific pixel, and I therefore know the X and Y of that pixel. And that means I know the endpoint of a very short vector that travels in exactly the same direction that I want my ray to go in. And that's all I need for my infinite ray!"


But that will only work if you can't turn in place at all.

So you do need the angles. You need to calculate the angle for each column, and store that in an array at the start of your program for quick access. Then at runtime, you add the current view angle to the angle for each column, and calculate your vector for your ray like so:

X = Radius * Cos(Angle)
Y = Radius * Sin(Angle)

You can just assume a radius of 1 and exclude that from the equations.

And that gives you your ray vectors, with no fisheye effect.

Ta da!