I'm developing a basic raycasting engine for HTML5 canvas, of the variety used in games like Wolfenstein 3D and Doom, as a learning exercise / hobby project. I've got to the point where I have walls rendering on the canvas with texture mapping, which is working pretty well after a fair bit of effort getting the intersection testing functions right.
I am correcting for the "fishbowl" / "fisheye" distortion effect (caused by increasing distances to intersection points as the angle from the centre of the screen increases), but I still have a very slight, but noticeable, curved distortion at the edges of the screen. This can be seen in the following image (I have drawn straight red lines to make the effect more obvious):
Can anyone shed light on what the source of this distortion is? It's not a huge issue but I haven't been able to figure out the cause, so I'm clearly missing something and I'm sure someone must know the answer. I've searched the problem pretty extensively and there isn't much information online, but I did find the following snippet in a forum post:
"The warping caused by using constant angular increments instead of horizon projection is another matter entirely - thats a lateral stretching/bunching effect and although usualy its only a barely noticable effect (for reasonable FOV, although the fact that you can define a 999999 degree FOV should ring bells), there simply isnt any reasonable way at all to compensate besides doing it right to begin with.. the angles are wrong using fixed increments and thats all there is to that."
This sounds like it might be referring to the same distortion I am experiencing, but it doesn't provide much help or insight other than suggesting fixed angle increments are the source of the problem (it's a curved distortion which increases towards the edges of the screen, which seems to fit with what this suggests). The function I am using to correct the distortions is:
function m_CorrectRayLengthDistortion( dist, angleFromCentre ){
return dist * Math.cos( MOD_Maths.degToRad( angleFromCentre ) );
}
MOD_Maths being a utility module (used in this case to convert the angle from degrees to radians so the cosine function can use it).
Any help on this is greatly appreciated, and if anyone answers this it will hopefully provide a guide to anyone experiencing the issue in future, given the aparrent lack of information on the subject that is available online.
Thanks :)
However, most modern video game 3D engines still use an advanced form of ray casting, because it remains the most efficient method to render a 3D image.
Ray casting is the most basic of many computer graphics rendering algorithms that use the geometric algorithm of ray tracing. Ray tracing-based rendering algorithms operate in image order to render three-dimensional scenes to two-dimensional images.
Ray casting is faster than ray tracing. Ray casting is faster because its world is limited by one or more geometric constraints (simple geometric shapes); a ray-tracing world can be almost any shape.
Later games such as Doom and Duke Nukem 3D also used raycasting, but much more advanced engines that allowed sloped walls, different heights, textured floors and ceilings, transparent walls, etc...
I solved this problem properly quite a while ago, but haven't got round to updating the answer until now. I have removed my previous answer which was incorrect (it gave almost correct results but by an indirect method, thanks to my lack of understanding of the root cause of the problem).
As Sam mentioned in his earlier comment, the root cause of the issue is that fixed angle increments are actually not correct if you want to achieve equally-spaced columns (which are necessary for the rendered result to look undistorted). This was mentioned in a forum post here, but although I found this I didn't fully understand why this was the case, or how to remedy the problem, until much later.
To achieve equally spaced columns on the screen, it stands to reason that each ray must travel from the point of view and pass through a pixel which is equally spaced along the projection surface, which means that as the rays move further from the central pixel of the screen, the increment by which the angle from the look direction increases gets gradually smaller. This is illustrated by the following picture (apologies, it isn't exactly a work of art):
With small fields of view the problem is not very noticeable, but becomes more problematic as the field of view increases (in my diagram the field of view is quite large to clearly illustrate the problem). To correctly calculate the ray angle increments, the following process must be used:
Where:
ang = ray angle from the look direction, whose ray passes through the central x coordinate of the screen;
opp = opposite side (equivalent to the distance of the screen X coordinate through which the ray passes from the screen X coordinate of the central pixel);
adj = adjacent side (equivalent to the distance from the point of view to the projection surface, which will be predetermined in code somewhere);
We can use the following formula (derivation included for clarity):
tan( ang ) = opp / adj
ang = atan( opp / adj )
ang = atan( ( pixel x coord - half screen width ) / dist to projection surface )
Javascript code example from my engine:
for( var x = 0; x < canvasSizeX; x++ ){
var xAng = _atan( ( x - canvasSizeHalfX ) / m_DistToProjSurf );
xRayAngles.push( xAng );
}
Due to the somewhat scarce nature of information on raycasting engines which is available online, and also due to the fact this particular issue isn't explicitly covered in any of the main tutorials which are out there, I wanted to update this post with the correct information in case anyone else has the same problem I did and doesn't understand why. Hopefully this will help someone.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With