diff options
author | Martin Stensgård <mastensg@mastensg.net> | 2016-11-02 04:24:50 +0100 |
---|---|---|
committer | Martin Stensgård <mastensg@mastensg.net> | 2016-11-02 04:24:50 +0100 |
commit | 8643df48c69d764fd592ae80602a89fe5852febb (patch) | |
tree | 78851684534568c81e8c7e03d5cb4da410180613 | |
parent | 63a2b7998c61ce7614446045aa0d0c00cb0b4d1f (diff) |
-rw-r--r-- | examples/readme_loop/ray.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/examples/readme_loop/ray.c b/examples/readme_loop/ray.c new file mode 100644 index 0000000..17f81d4 --- /dev/null +++ b/examples/readme_loop/ray.c @@ -0,0 +1,223 @@ +#include "ray.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <pthread.h> +#include <unistd.h> + +#include "3dmath.h" + +#define LENGTH(array) (sizeof(array) / sizeof(array[0])) +#define MAX(x, y) (x > y ? x : y) +#define MIN(x, y) (x < y ? x : y) + +#define TAU 6.28318531 + +#if __GNUC__ >= 3 +# define unlikely(cond) __builtin_expect ((cond), 0) +# define likely(cond) __builtin_expect ((cond), 1) +#else +# define unlikely(cond) (cond) +# define likely(cond) (cond) +#endif + +typedef struct { + float position[3]; + float radius; + float diffuse[3]; + float specular[3]; + int subtract; +} Object; + +typedef struct { + float position[3]; + float diffuse[3]; +} Light; + +typedef struct { + pthread_mutex_t mutex; + + int width, height; + unsigned char* buffer; + long next_line; +} ThreadArg; + +static float* trace_vectors; +static int trace_vectors_width, trace_vectors_height; + +static Object objects[] = { + {.position={-1.414, -1, -3}, .radius=1, .diffuse={.8, .0, .8}, .specular={.7, .6, .7}, .subtract=0}, + {.position={0, 1.414, -3}, .radius=1, .diffuse={.0, .8, .8}, .specular={.6, .7, .7}, .subtract=0}, + {.position={0, 0, -3}, .radius=1.5, .diffuse={.8, .8, .8}, .specular={.7, .7, .7}, .subtract=1}, + {.position={1.414, -1, -3}, .radius=1, .diffuse={.8, .8, .0}, .specular={.7, .7, .6}, .subtract=0}, + {.position={0, 0, -3}, .radius=1.1, .diffuse={.9, .9, .9}, .specular={.9, .9, .9}, .subtract=2} +}; +static const Light lights[] = { + {.position={-3, 3, -4}, .diffuse={0, .6, .6}}, + {.position={0, 30, -4}, .diffuse={1, 1, 1}} +}; +static const float ambient[3] = {0.2, 0.1, 0.1}; + +static void +trace(const float s[3], const float d[3], float pixel[3], int n) { + // Reflections in concave objects can go really deep, so we need to limit + // the recursion depth. + if (n > 6) return; + + float nearest = HUGE_VAL; + int nearest_object = -1; + float nearest_y[3]; + float nearest_r[3]; + + for(size_t j = 0; j < LENGTH(objects); ++j) { + float r[3], t, y[3]; + + if (objects[j].subtract == 1) continue; + + t = sphere_intersect(y, r, s, d, objects[j].position, objects[j].radius, 0); + + if(likely(t <= 0) || t > nearest) + continue; + + if (objects[j].subtract == 0) { + size_t k; + for (k = 0; k < LENGTH(objects); ++k) { + if (!objects[k].subtract) continue; + if (POW2(y[0] - objects[k].position[0]) + POW2(y[1] - objects[k].position[1]) + POW2(y[2] - objects[k].position[2]) > POW2(objects[k].radius)) continue; + + t = sphere_intersect(y, r, s, d, objects[k].position, objects[k].radius, 1); + + break; + } + + if(likely(t <= 0) || t > nearest) + continue; + } + + nearest = t; + nearest_object = j; + memcpy(nearest_y, y, sizeof(nearest_y)); + memcpy(nearest_r, r, sizeof(nearest_y)); + } + + if (nearest_object == -1) return; + + trace(nearest_y, nearest_r, pixel, n + 1); + + for (int k = 0; k < 3; ++k) + pixel[k] = pixel[k] * objects[nearest_object].specular[k] + ambient[k] * objects[nearest_object].diffuse[k]; + + for(int m = 0; m < LENGTH(lights); ++m) { + float l[3]; + for(int i = 0; i < 3; ++i) + l[i] = lights[m].position[i] - nearest_y[i]; + + float lr_dot = dot(l, nearest_r); + if (lr_dot <= 0) continue; + + float scale = lr_dot / sqrtf(dot(l, l)) / (1 << n); + // The cutoff at 0.05 is for artistic reasons; 0.0 would be more + // realistic. + if (scale <= 0.05) continue; + + for(int k = 0; k < 3; ++k) + pixel[k] += lights[m].diffuse[k] * objects[nearest_object].diffuse[k] * scale; + } +} + +static void +trace_line(int l, int width, unsigned char *buf) { + static const float s[3] = {0, 0, 8}; + + for(int i = 0; i < width; ++i, buf += 4) { + float pixel[3] = { 0, 0, 0 }; + + trace(s, &trace_vectors[(l * width + i) * 3], pixel, 1); + + buf[0] = MIN(pixel[0], 1.0f) * 255; + buf[1] = MIN(pixel[1], 1.0f) * 255; + buf[2] = MIN(pixel[2], 1.0f) * 255; + } +} + +static void * +thread(void *arg) { + ThreadArg* thread_arg = arg; + + for (;;) { + pthread_mutex_lock(&thread_arg->mutex); + if (thread_arg->next_line == thread_arg->height) break; + long line = thread_arg->next_line++; + pthread_mutex_unlock(&thread_arg->mutex); + + trace_line(line, thread_arg->width, thread_arg->buffer + line * 4 * thread_arg->width); + } + + pthread_mutex_unlock(&thread_arg->mutex); + + return NULL; +} + +static void +initialize_trace_vectors(int width, int height) { + trace_vectors = calloc(width * height, 3 * sizeof(float)); + trace_vectors_width = width; + trace_vectors_height = height; + for(int y = 0; y < height; ++y) { + for(int x = 0; x < width; ++x) { + float* d = &trace_vectors[(y * width + x) * 3]; + d[0] = ((float)x / width - 0.5f) * 0.5f * ((float)width / height); + d[1] = ((float)y / height - 0.5f) * 0.5f; + d[2] = -1; + normalize(d); + } + } +} + +void +trace_scene(float time, int width, int height, unsigned char *buf, int threaded) { + if (trace_vectors && (trace_vectors_width != width || trace_vectors_height != height)) { + free(trace_vectors); + trace_vectors = 0; + } + if (!trace_vectors) + initialize_trace_vectors(width, height); + + objects[0].position[0] = (1.5 + 0.35 * sin(1.0 * time + 0.0)) * cos(0.5 * time); + objects[0].position[1] = (1.5 + 0.35 * sin(1.0 * time + 2.5)) * sin(0.5 * time); + objects[1].position[0] = (1.5 + 0.35 * sin(1.0 * time + 2.0)) * cos(0.5 * time + 1/3. * TAU); + objects[1].position[1] = (1.5 + 0.35 * sin(1.0 * time + 1.5)) * sin(0.5 * time + 1/3. * TAU); + objects[3].position[0] = (1.5 + 0.35 * sin(1.0 * time + 1.0)) * cos(0.5 * time + 2/3. * TAU); + objects[3].position[1] = (1.5 + 0.35 * sin(1.0 * time + 0.5)) * sin(0.5 * time + 2/3. * TAU); + objects[2].position[2] = -3 + 0.2 * sin(time * 1.0); + memcpy(objects[4].position, objects[2].position, sizeof(objects[4].position)); + + if(threaded) { + ThreadArg arg; + memset(&arg, 0, sizeof(arg)); + arg.width = width; + arg.height = height; + pthread_mutex_init(&arg.mutex, NULL); + arg.buffer = buf; + + int num_threads = sysconf(_SC_NPROCESSORS_CONF) - 1; + pthread_t* threads = NULL; + if (num_threads > 0) { + threads = calloc(sizeof(*threads), num_threads); + + for (int i = 0; i < num_threads; ++i) + pthread_create(&threads[i], NULL, thread, &arg); + } + + thread(&arg); + + for(int i = 0; i < num_threads; ++i) + pthread_join(threads[i], NULL); + free(threads); + } else { + for(int i = 0; i < height; ++i) + trace_line(i, width, buf + i * 4 * width); + } +} |