Update
This commit is contained in:
parent
5134e55998
commit
a4a9624abe
11 changed files with 2078 additions and 1825 deletions
215
raycasting.md
215
raycasting.md
|
@ -1,6 +1,6 @@
|
|||
# Raycasting
|
||||
|
||||
In [computer graphics](graphics.md) raycasting refers to a rendering technique in which we determine which parts of the scene should be drawn according to which parts of the scene are hit by rays cast from the camera; it is a simpler version of **[raytracing](raytracing.md)**. The whole idea is based on the observation that we can trace rays of light that enter the camera by going BACKWARDS, i.e. instead of tracing light from light sources we rather start from the camera and go towards the parts of the scene that reflected the light (by which we ensure we are only considering the RELEVANT paths of light that actually end up hitting the camera) -- that is we are asking the question "in order for this screen pixel to light up, where would the light be coming from?", and then computing the answer to the question. A simplified way to quickly imagine what's going on is therefore to think of drawing the scene via "scanning" it with some kind of laser beam. Despite perhaps sounding intimidating at first, raycasting is one of the [simplest](minimalism.md) rendering methods, and for that it is also quite elegant -- [we](lrs.md) definitely do recommend it.
|
||||
In [computer graphics](graphics.md) raycasting refers to a rendering technique in which we determine which parts of the scene should be drawn according to which parts of the scene are hit by rays cast from the camera; it is a simpler version of **[raytracing](raytracing.md)**. The whole idea is based on the observation that we can trace rays of light that enter the camera by going BACKWARDS, i.e. instead of tracing light from light sources we rather start from the camera and go towards the parts of the scene that reflected the light (by which we ensure we are only considering the RELEVANT paths of light that actually end up hitting the camera) -- that is we are asking the question "in order for this screen pixel to light up, where would the light be coming from?", and then computing the answer to the question. A simplified way to quickly imagine what's going on is therefore to think of drawing the scene via "scanning" it with some kind of laser beam originating from the camera -- of course we do this [mathematically](math.md), using [analytic geometry](analytic_geometry.md), i.e. finding intersections of the rays with geometric shapes by solving algebraic equations. Despite perhaps sounding intimidating at first, raycasting is one of the [simplest](minimalism.md) rendering methods, and for that it is also quite elegant -- [we](lrs.md) definitely do recommend it.
|
||||
|
||||
Raycasting is an **image order** rendering method, meaning that we iterate over the pixels of the screen and for each determine its [color](color.md) (as opposed to object order methods that iterate over 3D objects that are then "pasted" to the screen). I.e. the image can be drawn in any order -- let's say from top left to bottom right -- and without drawing any pixel more than once or leaving out any. This is advantageous as we may leave out [double buffering](double_buffering.md) and save A LOT of memory on the extra frame buffer. We may also utilize for example [frameless rendering](frameless.md). All these attributes are why we consider raycasting so nice.
|
||||
|
||||
|
@ -298,3 +298,216 @@ How to make this more advanced? Here are some hints and tips:
|
|||
- **adding [billboards](billboard.md) (sprites)**: TODO
|
||||
- **reflections**: We can make our 2D raycaster a 2D [raytracer](raytracing.md), i.e. when we cast a camera ray and it hits a reflective wall (a mirror), we cast another, secondary reflected ray and trace it to see which wall it hits, i.e. which wall will get reflected in the reflective wall.
|
||||
- **partly transparent walls**: We can make some walls partially transparent, both with [alpha blending](alpha.md) or textures with transparent pixels. In both cases we'll have to look not just for the first hit of the ray, but also for the next.
|
||||
|
||||
## 3D Raycasting
|
||||
|
||||
Here is a simple example of 3D raycasting in [C](c.md). To show it's possible it's only written using [fixed point](fixed_point.md). We only have two kinds of shapes in the scene: [spheres](sphere.md) and [planes](plane.md). For simplicity we also don't do many [optimizations](optimization.md). Here's the code:
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
|
||||
#define RES_X 64 // picture width
|
||||
#define RES_Y 30 // picture height
|
||||
#define U 1024 // fixed point unit
|
||||
#define INF 0x00ffffff // infinity value
|
||||
|
||||
char palette[] = "WM0KXkxocl;:,'. "; // ASCII shading palette
|
||||
|
||||
typedef struct { int e[3]; } V3; // 3D vector
|
||||
typedef struct { V3 p0; V3 p1; } Ray; // ray
|
||||
#define V(vec,el) (vec).e[el] // short for accessing vector elements
|
||||
|
||||
unsigned int sqrtInt(unsigned int x) // simple square root
|
||||
{
|
||||
unsigned int a = 1, b = x;
|
||||
while (b > a) { a <<= 1; b >>= 1; }
|
||||
a = (a + b) >> 1;
|
||||
while (a * a > x) a--;
|
||||
return a;
|
||||
}
|
||||
|
||||
int squared(int x) { return x * x; }
|
||||
|
||||
V3 v3(int x, int y, int z) // creates a vector
|
||||
{
|
||||
V3 v; V(v,0) = x; V(v,1) = y; V(v,2) = z; return v;
|
||||
}
|
||||
|
||||
int v3Len(V3 v) // vector length
|
||||
{
|
||||
return sqrtInt(V(v,0) * V(v,0) + V(v,1) * V(v,1) + V(v,2) * V(v,2));
|
||||
}
|
||||
|
||||
V3 v3Minus(V3 a, V3 b) // vector subtraction
|
||||
{
|
||||
return v3(V(a,0) - V(b,0),V(a,1) - V(b,1),V(a,2) - V(b,2));
|
||||
}
|
||||
|
||||
int v3Dot(V3 a, V3 b) // dot product
|
||||
{
|
||||
return (V(a,0) * V(b,0) + V(a,1) * V(b,1) + V(a,2) * V(b,2)) / U;
|
||||
}
|
||||
|
||||
V3 v3Interpolate(V3 a, V3 b, int t)
|
||||
{
|
||||
return v3(V(a,0) + (t * (V(b,0) - V(a,0))) / U,
|
||||
V(a,1) + (t * (V(b,1) - V(a,1))) / U,V(a,2) + (t * (V(b,2) - V(a,2))) / U);
|
||||
}
|
||||
|
||||
V3 v3Normalize(V3 v)
|
||||
{
|
||||
int l = v3Len(v);
|
||||
|
||||
return l != 0 ? v3((V(v,0) * U) / l,(V(v,1) * U) / l,
|
||||
(V(v,2) * U) / l) : v3(U,0,0);
|
||||
}
|
||||
|
||||
V3 rayVsPlane(Ray r, int coord, int n, V3 *normal)
|
||||
{
|
||||
int t = V(r.p1,coord) - V(r.p0,coord);
|
||||
|
||||
if (t == 0) // prevent division by zero
|
||||
return v3(INF,INF,INF);
|
||||
|
||||
t = ((n - V(r.p0,coord)) * U) / t;
|
||||
|
||||
if (t < 0)
|
||||
return v3(INF,INF,INF);
|
||||
|
||||
if (normal)
|
||||
{
|
||||
V3 dir = v3Minus(r.p1,r.p0);
|
||||
*normal = v3(0,0,0);
|
||||
V(*normal,coord) = V(dir,coord);
|
||||
*normal = v3Normalize(*normal);
|
||||
}
|
||||
|
||||
return v3Interpolate(r.p0,r.p1,t);
|
||||
}
|
||||
|
||||
V3 rayVsSphere(Ray ray, V3 center, int radius, V3 *normal)
|
||||
{
|
||||
V3 dir = v3Normalize(v3Minus(ray.p1,ray.p0)); // normalized direction
|
||||
int tmp = v3Dot(dir,v3Minus(ray.p0,center));
|
||||
|
||||
int diff = // for solving quadratic equation
|
||||
squared(tmp) - (squared(v3Len(v3Minus(ray.p0,center))) - squared(radius));
|
||||
|
||||
if (diff < 0) // no solution to quadratic equation
|
||||
return v3(INF,INF,INF);
|
||||
|
||||
diff = sqrtInt(diff);
|
||||
tmp *= -1;
|
||||
tmp += (tmp + diff > tmp - diff) ? -1 * diff : diff;
|
||||
|
||||
if (tmp < 0) // hit behind camera?
|
||||
return v3(INF,INF,INF);
|
||||
|
||||
V3 result =
|
||||
v3((tmp * V(dir,0)) / U,(tmp * V(dir,1)) / U,(tmp * V(dir,2) / U));
|
||||
|
||||
if (normal)
|
||||
*normal = v3Normalize(v3Minus(result,center));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
V3 viewport = v3(U,(3 * U) / 4,U); // bottom right point of vieport
|
||||
V3 light = v3Normalize(v3(-U,U/2,-U/2)); // light direction
|
||||
|
||||
Ray r; r.p0 = v3(0,0,0); r.p1 = viewport;
|
||||
|
||||
for (int y = 0; y < RES_Y; ++y) // draw columns
|
||||
{
|
||||
for (int x = 0; x < RES_X; ++x) // draw lines
|
||||
{
|
||||
V(r.p1,0) = -1 * V(viewport,0) + (x * 2 * V(viewport,0)) / (RES_X - 1);
|
||||
V(r.p1,1) = -1 * V(viewport,1) + (y * 2 * V(viewport,1)) / (RES_Y - 1);
|
||||
|
||||
int closestDist = INF;
|
||||
int object = 0;
|
||||
V3 hit = v3(INF,INF,INF); // intersection point
|
||||
V3 n; // hit normal
|
||||
char pixel = ' ';
|
||||
|
||||
while (object >= 0) // cast ray against each object
|
||||
{
|
||||
switch (object) // here we define scene objects
|
||||
{
|
||||
case 0: hit = rayVsSphere(r,v3(2 * U/3,-U/3,3 * U/2),U/2,&n); break;
|
||||
case 1: hit = rayVsSphere(r,v3(-U,1,6 * U),3 * U,&n); break;
|
||||
case 2: hit = rayVsSphere(r,v3(-3 * U/2,U,3 * U),3 * U/2,&n); break;
|
||||
case 3: hit = rayVsPlane(r,1,U,&n); break;
|
||||
case 4: hit = rayVsPlane(r,0,-2 * U,&n); break;
|
||||
case 5: hit = rayVsPlane(r,2,6 * U,&n); break;
|
||||
default: object = -123; break; // this stops further checking
|
||||
}
|
||||
|
||||
int dist = V(hit,2); // Z component as perpendicular dist.
|
||||
|
||||
if (dist < closestDist)
|
||||
{
|
||||
pixel = V(hit,0) == INF ? 15 :
|
||||
8 + (8 * v3Dot(n,light)) / U // shade by angle of light
|
||||
+ object; // this gives each object a bit different color
|
||||
|
||||
pixel = palette[pixel < 0 ? 0 : (pixel > 15 ? 15 : pixel)];
|
||||
closestDist = dist;
|
||||
}
|
||||
|
||||
object++;
|
||||
}
|
||||
|
||||
putchar(pixel);
|
||||
}
|
||||
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
And voila, this is what we get:
|
||||
|
||||
```
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;ccccccco;;;;;;;;;;;
|
||||
;lllllllccco;;;;;;;;;;;;:::;;;;llccco;;;;;;;;
|
||||
::;;;;lllllllcccoox;;;;:,,,,,:::::;;llcccx;;;;;;
|
||||
::::;;;;;lllllllcccoxx;,''''',,,,:::;;;llcco;;;;;
|
||||
,,::::;;;;;lllllllcccoo''..''''',,,,:::;;llccx;;;;
|
||||
,,,:::::;;;;lllllllccc'.......'''',,,:::;;lcco;;;;
|
||||
,,,,,:::::;;;;lllllllcc. ......''',,,::;;llcc;;;;
|
||||
',,,,,:::::;;;;lllllll. ....'''',,:::;llcc;;;;
|
||||
'',,,,,::::;;;;;llllll. ....''',,,::;;lc;;;;;
|
||||
;;;llllcccooxxx:::;;;;;llllll ....'',,,::;llc;;;;;
|
||||
:;;;;;;;lllccccoooxxkk:;;;;;lllll ....'',,,::;lc;;;;;;
|
||||
:::;;;;;;;llllccccoooxxkkX;;;;llllll ...''',,::;lc;;;;;;;
|
||||
,:::;;;;;;;;llllccccoooxxkkXX;;;lllllll ....'',,::;l;;;;;;;;;
|
||||
,::::;;;;;;;;llllcccooooxxkkXX;;;llllllcc....''',,:;;;;;;;;;;;;;
|
||||
,::::;;;;;;;;;llllcccoooxxxkkXK;;llllllccoo;;;;;;;;;;;;;;;;;;;;;
|
||||
,::::;;;;;;;;;llllccccoooxxxkkX;;;llllllcox.....................
|
||||
,:::::;;;;;;;;;llllccccoooxxxkkX;;llllllc.......................
|
||||
,,:::::;;;;;;;;;llllccccoooxxkkX;;;.............................
|
||||
,,:::::;;;;;;;;;;llllccccoooxxkk................................
|
||||
,,,:::::;;;;;;;;;lllllcccoooxxx.................................
|
||||
,,,,:::::;;;;;;;;;llllccccooo...................................
|
||||
,,,,:::::;;;;;;;;;;llllcccc.....................................
|
||||
,,,,,:::::;;;;;;;;;;lll.........................................
|
||||
',,,,,:::::;;;;;;...............................................
|
||||
'',,,,,::.......................................................
|
||||
................................................................
|
||||
................................................................
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [raytracing](raytracing.md)
|
||||
- [pathtracing](pathtracing.md)
|
||||
- [conetracing](conetracing.md)
|
||||
- [raymarching](raymarching.md)
|
Loading…
Add table
Add a link
Reference in a new issue