Update
This commit is contained in:
parent
9005259ff3
commit
df80221a15
29 changed files with 1864 additions and 1808 deletions
|
@ -61,7 +61,7 @@ The classic version of 2D raycasting -- as seen in the early 90s games -- only r
|
|||
|
||||
The core element to implement is the code for casting rays, i.e. given the square plan of the environment (e.g. game level), in which each square is either empty or a wall (which can possibly be of different types, to allow e.g. different textures), we want to write a function that for any ray (defined by its start position and direction) returns the information about the first wall it hits. This information most importantly includes the distance of the hit, but can also include additional things such as the type of the wall, texturing coordinate or its direction (so that we can [shade](shading.md) differently facing walls with different brightness for better realism). The environment is normally represented as a 2 dimensional [array](array.md), but instead of explicit data we can also use e.g. a function that [procedurally](procgen.md) generates infinite levels (i.e. we have a function that for given square coordinates computes what kind of square it is). As for the algorithm for tracing the ray in the grid we may actually use some kind of line [rasterization](rasterization.md) algorithm, e.g. the **[DDA](dda.md) algorithm** (tracing a line through a grid is analogous to drawing a line in a pixel grid). This can all be implemented with [fixed point](fixed_point.md), i.e. integer only! No need for [floating point](float.md).
|
||||
|
||||
**Note on distance calculation and distortion**: When computing the distance of ray hit from the camera, we usually DO NOT want to use the [Euclidean](euclidean.md) distance of that point from the camera position (as is tempting) -- that would create a so called fish eye effect, i.e. looking straight into a perpendicular wall would make the wall look warped/bowled (as the part of the wall in the middle of the screen is actually closer to the camera position so it would, by perspective, look bigger). For non-distorted rendering we have to compute a distance that's perpendicular to the camera plane -- we can see the camera plane as a "canvas" onto which we project the scene, in 2D it is a line (unlike in 3D where it really is a plane) at a certain distance from the camera (usually conveniently chosen to be e.g. 1) whose direction is perpendicular to the direction the camera is facing. The good news is that with a little trick this distance can be computed even more efficiently than Euclidean distance, as we don't need to compute a square root! Instead we can utilize the similarity of triangles. Consider the following situation:
|
||||
**Note on distance calculation and distortion**: When computing the distance of ray hit from the camera, we usually DO NOT want to use the [Euclidean](euclidean.md) distance of that point from the camera position (as is tempting) -- that would create a so called fish eye effect, i.e. looking straight into a perpendicular wall would make the wall look warped/bowled (as the part of the wall in the middle of the screen is actually closer to the camera position than the wall sides off the center, so it would by perspective laws look bigger). For non-distorted rendering we have to compute a distance that's perpendicular to the camera projection plane -- we can see the camera plane as a "canvas" onto which we project the scene (it's actually the flat computer screen that determines that we shall use such a flat projection plane), in 2D "top-down view" it is a line segment (unlike in 3D where it really is a plane, a rectangle) at a certain distance from the camera (usually conveniently chosen to be e.g. 1) whose direction is perpendicular to the direction the camera is facing. The good news is that with a little trick this distance can be computed even more efficiently than Euclidean distance, as we don't need to compute a square root! Instead we can utilize the similarity of triangles. Consider the following situation:
|
||||
|
||||
```
|
||||
I-_
|
||||
|
@ -78,6 +78,8 @@ The core element to implement is the code for casting rays, i.e. given the squar
|
|||
|
||||
In the above *V* is the position of the camera (viewer) which is facing towards the point *I*, *p* is the camera plane perpendicular to *VI* at the distance 1 from *V*. Ray *r* is cast from the camera and hits the point *X*. The length of the line *r* is the Euclidean distance, however we want to find out the distance *JX = VI*, which is perpendicular to *p*. There are two similar triangles: *VCA* and *VIB*; from this it follows that *1 / VA = VI / VB*, from which we derive that *JX = VB / VA*. We can therefore calculate the perpendicular distance just from the ratio of the distances along one principal axis (X or Y). However watch out for the case when *VA = VB = 0* to not divide by zero! In such case use the other principal axis (Y).
|
||||
|
||||
NOTE: Take your time to think about the above and how the projection works, it's how it also works in cameras and our own eyes (though our eyes actually have a curved projection plane -- the eyeball's retina; our brain is just used to seeing with the fish eye effect). Seeing this will make you get to the fundamental understanding of how 3D projection works. Try to observe for example the following: if we were using an ideally curved monitor (one that would would be part of a sphere in whose center we sit), we would rather want to use the Euclidean distance for rendering, i.e. a curved projection plane that would creates the fish eye effect, because the effect would then be canceled out by the curvature of the monitor in real life (the walls at the sides would be rendered smaller but then they would be made bigger again to our eyes by the curved monitor that on the sides gets closer). Ultimately our goal is to create a program that TOGETHER with the rendering device (which we suppose to be a flat monitor) will shoot rays of light in a way that real life environment would. That's basically it. That's also why using curved monitors for 3D games that assume flat monitor is capitalist retardation.
|
||||
|
||||
Here is a complete **[C](c.md) example** that uses only [fixed point](fixed_point.md) with the exception of the stdlib [sin](sin.md)/[cos](cos.md) functions, for simplicity's sake (these can easily be replaced by custom fixed point implementation):
|
||||
|
||||
```
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue