diff options
author | Franklin Wei <me@fwei.tk> | 2017-04-02 20:20:43 -0400 |
---|---|---|
committer | Franklin Wei <me@fwei.tk> | 2017-04-02 20:20:43 -0400 |
commit | d6e9b142e014c3d171932894dbb0db0410e91af7 (patch) | |
tree | 31aedf3a79d8071cb638533e5e220540cbdc1a6a | |
download | raytrace-d6e9b142e014c3d171932894dbb0db0410e91af7.zip raytrace-d6e9b142e014c3d171932894dbb0db0410e91af7.tar.gz raytrace-d6e9b142e014c3d171932894dbb0db0410e91af7.tar.bz2 raytrace-d6e9b142e014c3d171932894dbb0db0410e91af7.tar.xz |
initial commit
-rw-r--r-- | main.c | 286 | ||||
-rw-r--r-- | vector.c | 110 | ||||
-rw-r--r-- | vector.h | 27 |
3 files changed, 423 insertions, 0 deletions
@@ -0,0 +1,286 @@ +#include <stdbool.h> +#include <stdlib.h> +#include <assert.h> +#include <stdio.h> +#include "vector.h" +#include <SDL/SDL.h> +#include <SDL/SDL_video.h> + +#define WIDTH 320 +#define HEIGHT 240 + +struct rgb_t { unsigned char r, g, b; }; + +struct object_t { + enum { SPHERE, TRI } type; + union { + struct { vector center; scalar radius; } sphere; + struct { vector points[3]; } tri; + }; + struct rgb_t color; + int specularity; /* 0-255 */ +}; + +struct light_t { + struct rgb_t color; + vector position; +}; + +struct scene_t { + struct rgb_t bg; + struct object_t *objects; + size_t n_objects; + struct light_t *lights; + size_t n_lights; +}; + +struct camera_t { + vector origin; + vector offset; /* direction to center of camera */ + scalar fov_x, fov_y; /* radians */ +}; + +#define MAX(a, b) ((a>b)?(a):(b)) +#define MIN(a, b) ((a<b)?(a):(b)) +#define SQR(a) ((a)*(a)) +#define MAX_BOUNCES 0 + +/* { o, d } form a ray */ +bool object_intersects(struct object_t *obj, const vector* o, const vector* d, scalar *t) +{ + assert(o->type == RECT); + assert(d->type == RECT); + switch(obj->type) + { + case SPHERE: + { + scalar a = SQR(d->rect.x) + SQR(d->rect.y) + SQR(d->rect.z), + b = 2 * (o->rect.x * d->rect.x + o->rect.y * d->rect.y + o->rect.z * d->rect.z), + c = SQR(o->rect.x) + SQR(o->rect.y) + SQR(o->rect.z) - SQR(obj->sphere.radius); + scalar disc = b*b - 4*a*c; + if(disc < 0) + { + //printf("no intersection (%f)\n", disc); + return false; + } + scalar t1 = (-b - sqrt(disc)) / (2*a), t2 = (-b + sqrt(disc)) / (2*a); + /* both are negative */ + if(t1 < 0 && t2 < 0) + { + //printf("no intersection\n"); + return false; + } + /* one is negative */ + if(t1 * t2 < 0) + { + //printf("camera is inside sphere (%f, %f)!\n", t1, t2); + *t = MAX(t1, t2); + return true; + } + vector prod = *d; + vect_mul(&prod, t2); + vect_add(&prod, o); + //printf("ray from (%f, %f, %f) intersects sphere at point %f, %f, %f (%f)\n", o->rect.x, o->rect.y, o->rect.z, prod.rect.x, prod.rect.y, prod.rect.z, vect_abs(&prod)); + *t = MIN(t1, t2); + return true; + } + } +} + +/* return the direction of the reflected ray */ +vector reflect_ray(const vector *pt, const vector *d, const struct object_t *obj) +{ + vector normal; + switch(obj->type) + { + case SPHERE: + { + vector p = *pt; + vect_sub(&p, &obj->sphere.center); + normal = p; + break; + } + default: + assert(false); + } + + scalar c = -2 * vect_dot(d, &normal); + vector reflected = normal; + vect_mul(&reflected, c); + vect_add(&reflected, d); + return reflected; +} + +struct rgb_t blend(struct rgb_t a, struct rgb_t b, int alpha) +{ + struct rgb_t ret; + ret.r = ((a.r * alpha) + (b.r * (255 - alpha))) >> 8; + ret.g = ((a.g * alpha) + (b.g * (255 - alpha))) >> 8; + ret.b = ((a.b * alpha) + (b.b * (255 - alpha))) >> 8; + return ret; +} + +struct rgb_t trace_ray(const struct scene_t *scene, const vector *orig, const vector *d, int max_iters) +{ + struct rgb_t primary = scene->bg; + scalar closest = -1; /* distance from camera in terms of d */ + const struct object_t *closest_obj = NULL; + + /* check intersections */ + for(int i = 0; i < scene->n_objects; ++i) + { + scalar t; + if(object_intersects(scene->objects + i, orig, d, &t)) + { + if(closest < 0 || t < closest) + { + closest = t; + closest_obj = scene->objects + i; + } + } + } + + if(closest_obj) + { + //printf("pow!\n"); + primary = closest_obj->color; + } + + struct rgb_t reflected = {0, 0, 0}; + /* shade */ + if(closest_obj && closest_obj->specularity && max_iters > 0) + { + vector pt = *d; + vect_mul(&pt, closest); + vect_add(&pt, orig); + vector ref = reflect_ray(&pt, d, closest_obj); + reflected = trace_ray(scene, &pt, &ref, max_iters - 1); + } + + return blend(primary, reflected, closest_obj?(255 - closest_obj->specularity):255); +} + +unsigned char *render_scene(int w, int h, const struct scene_t *scene, + const struct camera_t *cam) +{ + unsigned char *fb = malloc(w * h * 3); + scalar scale_x = cam->fov_x / w, scale_y = cam->fov_y / h; + + for(int y = 0; y < h; ++y) + { + for(int x = 0; x < w; ++x) + { + /* trace a ray from the camera into the scene */ + /* figure out how to rotate the offset vector to suit the + * current pixel */ + + /* rot in range of [-fov / 2, fov / 2) */ + scalar rot_x = (x - w / 2) * scale_x, rot_y = (y - h / 2) * scale_y; + + /* rotate the offset vector */ + vector d = cam->offset; + vect_to_sph(&d); + + d.sph.elevation -= rot_y; + d.sph.azimuth += rot_x; + + vect_to_rect(&d); + + //printf("(%d, %d) maps to (%f, %f, %f)\n", x, y, d.rect.x, d.rect.y, d.rect.z); + + /* cam->origin and d now form the camera ray */ + + struct rgb_t color = trace_ray(scene, &cam->origin, &d, MAX_BOUNCES); + + fb[y * w * 3 + 3 * x] = color.r; + fb[y * w * 3 + 3 * x + 1] = color.g; + fb[y * w * 3 + 3 * x + 2] = color.b; + } + } + return fb; +} + +int main() +{ + vector test = (vector) { RECT, { 0, 1, 1 } }; + vect_to_sph(&test); + //printf("1, 0, 0, is r = %f, elev = %f, azi = %f", test.sph.r, test.sph.elevation, test.sph.azimuth); + //return 0; + + struct scene_t scene; + scene.bg.r = 0xff; + scene.bg.g = 0x00; + scene.bg.b = 0xff; + + struct object_t sph; + sph.type = SPHERE; + sph.sphere.center = (vector) { RECT, {0, 0, 0 } }; + sph.sphere.radius = 1; + sph.color = (struct rgb_t){0, 0, 0xff}; + sph.specularity = 100; + + scene.objects = &sph; + scene.n_objects = 1; + scene.n_lights = 0; + + struct camera_t cam; + cam.origin = (vector){ RECT, {-5, 0, 0} }; + cam.offset = (vector){ RECT, {0, 0, 1} }; + cam.fov_x = M_PI / 2; + cam.fov_y = M_PI / 2; + +#if 0 + unsigned char *fb = render_scene(WIDTH, HEIGHT, &scene, &cam); + FILE *f = fopen("test.ppm", "w"); + fprintf(f, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, 255); + fwrite(fb, WIDTH * HEIGHT, 3, f); + fclose(f); + return 0; + +#else + SDL_Init(SDL_INIT_VIDEO); + SDL_Surface *screen = SDL_SetVideoMode(WIDTH, HEIGHT, 24, SDL_SWSURFACE); + SDL_EnableKeyRepeat(500, 100); + + while(1) + { + unsigned char *fb = render_scene(WIDTH, HEIGHT, &scene, &cam); + memcpy(screen->pixels, fb, WIDTH * HEIGHT * 3); + SDL_UpdateRect(screen, 0, 0, 0, 0); + free(fb); + SDL_Event e; + printf("camera at %f, %f, %f\n", cam.origin.rect.x, cam.origin.rect.y, cam.origin.rect.z); + while(SDL_PollEvent(&e)) + { + switch(e.type) + { + case SDL_QUIT: + return 0; + case SDL_KEYDOWN: + switch(e.key.keysym.sym) + { + case SDLK_LEFT: + cam.origin.rect.x -= .1; + break; + case SDLK_RIGHT: + cam.origin.rect.x += .1; + break; + case SDLK_UP: + cam.origin.rect.y += .1; + break; + case SDLK_DOWN: + cam.origin.rect.y -= .1; + break; + case SDLK_w: + cam.origin.rect.z += .1; + break; + case SDLK_s: + cam.origin.rect.z -= .1; + break; + } + break; + } + } + } +#endif +} diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..da644fd --- /dev/null +++ b/vector.c @@ -0,0 +1,110 @@ +#include "vector.h" + +void vect_to_rect(vector *v) +{ + if(v->type == RECT) + return; + switch(v->type) + { + case SPH: + { + vector old = *v; + v->type = RECT; + v->rect.x = old.sph.r * cos(old.sph.elevation) * sin(old.sph.azimuth); + v->rect.y = old.sph.r * sin(old.sph.elevation); + v->rect.z = old.sph.r * cos(old.sph.elevation) * cos(old.sph.azimuth); + break; + } + } +} + +void vect_to_sph(vector *v) +{ + if(v->type == SPH) + return; + switch(v->type) + { + case RECT: + { + vector old = *v; + v->type = SPH; + v->sph.r = vect_abs(&old); + v->sph.elevation = atan2(old.rect.y, sqrt(old.rect.x*old.rect.x + old.rect.z*old.rect.z)); + v->sph.azimuth = atan2(old.rect.z, old.rect.x); + break; + } + } +} + +scalar vect_abs(const vector *v) +{ + switch(v->type) + { + case SPH: + return v->sph.r; + case RECT: + return sqrt(v->rect.x * v->rect.x + v->rect.y * v->rect.y + v->rect.z * v->rect.z); + } +} + +void vect_mul(vector *v, scalar s) +{ + switch(v->type) + { + case SPH: + v->sph.r *= s; + break; + case RECT: + v->rect.x *= s; + v->rect.y *= s; + v->rect.z *= s; + break; + } +} + +void vect_add(vector *v1, const vector *v2) +{ + int old_type = v1->type; + vector tmp1 = *v1; + vector tmp2 = *v2; + vect_to_rect(&tmp1); + vect_to_rect(&tmp2); + tmp1.rect.x += tmp2.rect.x; + tmp1.rect.y += tmp2.rect.y; + tmp1.rect.z += tmp2.rect.z; + + *v1 = tmp1; + if(old_type == SPH) + vect_to_sph(v1); +} + +void vect_negate(vector *v) +{ + switch(v->type) + { + case SPH: + v->sph.r = -v->sph.r; + break; + case RECT: + v->rect.x = -v->rect.x; + v->rect.y = -v->rect.y; + v->rect.z = -v->rect.z; + break; + } +} + +/* v1 = v1 - v2 */ +void vect_sub(vector *v1, const vector *v2) +{ + vector neg = *v2; + vect_negate(&neg); + vect_add(v1, &neg); +} + +scalar vect_dot(const vector *v1, const vector *v2) +{ + vector a = *v1, b = *v2; + vect_to_rect(&a); + vect_to_rect(&b); + return a.rect.x * b.rect.x + a.rect.y * b.rect.y + a.rect.z * b.rect.z; +} diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..24773c7 --- /dev/null +++ b/vector.h @@ -0,0 +1,27 @@ +#include <math.h> + +typedef double scalar; + +typedef struct vector_t { + enum { RECT, SPH } type; + union { + struct { + scalar x, y, z; + } rect; + struct { + scalar r, elevation, azimuth; + } sph; + }; +} vector; + +scalar vect_abs(const vector*); + +void vect_mul(vector*, scalar); +void vect_add(vector*, const vector*); + +void vect_to_rect(vector*); +void vect_to_sph(vector*); +void vect_sub(vector*, const vector*); +void vect_negate(vector*); + +scalar vect_dot(const vector *v1, const vector *v2); |