aboutsummaryrefslogtreecommitdiff
path: root/ray.c
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2020-02-21 06:55:27 +0100
committerJulian T <julian@jtle.dk>2020-02-21 06:55:27 +0100
commit623fee395425ab33f14fb9cd8ffa790e362f59d7 (patch)
treec70b64e13fd5e00c2317fa40ee4b8cd786e635d5 /ray.c
parent63a84080f9f0e3d719d5470e370584a5eff18a47 (diff)
Added pgm drawing and got light ray tracing working
Still needs correct light simulation and reflections
Diffstat (limited to 'ray.c')
-rw-r--r--ray.c141
1 files changed, 132 insertions, 9 deletions
diff --git a/ray.c b/ray.c
index d2b15cc..3736773 100644
--- a/ray.c
+++ b/ray.c
@@ -1,3 +1,4 @@
+#include <stdio.h>
#include <math.h>
#include "vector.h"
@@ -5,7 +6,8 @@
// https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
// http://viclw17.github.io/2018/07/16/raytracing-ray-sphere-intersection/
-COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray)
+// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
+COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray, bool skip_dist)
{
// Vector between vector start and center of circle
vector_t oc;
@@ -13,8 +15,8 @@ COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray)
// Solve quadratic function
// TODO Not sure if this step i neccesary because dir is unit
- COORD_T a = vector_dot(&ray->direction, &ray->direction);
- COORD_T b = 2 * vector_dot(&oc, &ray->direction);
+ COORD_T a = vector_dot(ray->direction, ray->direction);
+ COORD_T b = 2 * vector_dot(&oc, ray->direction);
COORD_T c = vector_dot(&oc, &oc) - s->radius * s->radius;
COORD_T d = b * b - 4 * a * c;
@@ -23,29 +25,150 @@ COORD_T ray_intersect_sphere(sphere_t *s, ray_t *ray)
if (d < 0) {
return -1;
}
+ if (skip_dist) {
+ return 1;
+ }
+
+ // Else take the closest intersection, reuse d
+ COORD_T q = (b > 0) ?
+ -0.5 * (b + sqrt(d)) :
+ -0.5 * (b - sqrt(d));
+
+ COORD_T x0 = q / a;
+ COORD_T x1 = c / q;
+
+ // Take the correct result. If one is zero take the other.
+ if (x0 <= 0) {
+ if (x1 <= 0) {
+ return -1;
+ }
+
+ x0 = x1;
+ }
+
+ // If point is on sphere it will be zero close to zero
+ if (x0 < 1e-3) {
+ return -1;
+ }
- // Else take the closest intersection
- return (-b - sqrt(d) ) / (2.0 * a);
+ return x0;
}
// Requires that vectors are normalized
// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection
-COORD_T ray_intersect_plane(plane_t *p, ray_t *ray)
+COORD_T ray_intersect_plane(plane_t *p, ray_t *ray, bool skip_dist)
{
// If zero ray is parralel to plane
- COORD_T nr = vector_dot(&p->norm, &ray->direction);
- vector_print(&ray->direction);
+ COORD_T nr = vector_dot(p->norm, ray->direction);
//
// Take care of rounding errors
if (nr < ZERO_APROX && nr > -ZERO_APROX) {
return -1;
}
+ if (skip_dist) {
+ return 1;
+ }
// Calculate distance
vector_t tmp;
vector_copy(&tmp, p->start);
vector_sub(&tmp, &tmp, ray->start);
- COORD_T t = vector_dot(&tmp, &p->norm) / nr;
+ COORD_T t = vector_dot(&tmp, p->norm) / nr;
return t;
}
+
+COORD_T ray_intersect(object_t *o, ray_t *ray, bool skip_dist)
+{
+ switch (o->type) {
+ case TYPE_PLANE:
+ return ray_intersect_plane(&o->pl, ray, skip_dist);
+ case TYPE_SPHERE:
+ return ray_intersect_sphere(&o->sph, ray, skip_dist);
+ default:
+ printf("Unknown object type %d\n", o->type);
+ return -1;
+ }
+}
+
+// If chk is true, will return at first hit less than chk_dist
+object_t *ray_cast(space_t *s, ray_t *r, COORD_T *dist_ret, bool chk, COORD_T chk_dist)
+{
+ object_t *o = s->objects;
+
+ object_t *smallest = NULL;
+ COORD_T dist = 0;
+
+ while (o) {
+ COORD_T d = ray_intersect(o, r, false);
+
+ if (d > 0) {
+ if (chk && chk_dist > d) {
+ if (dist_ret) {
+ *dist_ret = d;
+ }
+ return o;
+ }
+ if (d < dist || smallest == NULL) {
+ dist = d;
+ smallest = o;
+ }
+ }
+
+ o = o->next;
+ }
+
+ if (chk) {
+ return NULL;
+ }
+
+ if (dist_ret) {
+ *dist_ret = dist;
+ }
+ return smallest;
+}
+
+color_t *ray_trace(space_t *s, unsigned int x, unsigned int y)
+{
+ // Setup primary ray
+ ray_t r;
+ r.start = &s->view.position;
+ r.direction = vector_copy(NULL, NULL);
+ viewpoint_ray(&s->view, r.direction, x, y);
+
+ // Cast it
+ COORD_T dist;
+ object_t *o = ray_cast(s, &r, &dist, false, 0);
+ if (!o) {
+ return NULL;
+ }
+ //printf("dist: %f\n", dist);
+
+ // Calculate new ray point
+ r.start = vector_scale(NULL, r.direction, dist);
+ vector_add(r.start, r.start, &s->view.position);
+
+ // Cast light rays
+ light_t *l = s->lights;
+ while (l) {
+ vector_t tmp;
+ // Calculate distance to light
+ vector_sub(&tmp, l->pos, r.start);
+ COORD_T d = vector_len(&tmp);
+
+ // Calculate unit
+ vector_scale_inv(&tmp, &tmp, vector_len(&tmp));
+
+ // Find obstacles
+ r.direction = &tmp;
+ object_t *obs = ray_cast(s, &r, NULL, true, d);
+
+ if (!obs) {
+ return color_set(NULL, 100, 100, o->type == TYPE_SPHERE ? 200 :0);
+ }
+
+ l = l->next;
+ }
+
+ return NULL;
+}