+
+int ray_trace(const cgm_ray *ray, int maxiter, cgm_vec3 *res)
+{
+ struct rayhit hit;
+
+ if(!scn_intersect(scn, ray, &hit)) {
+ *res = bgcolor(ray);
+ return 0;
+ }
+
+ *res = shade(ray, &hit, maxiter);
+ return 1;
+}
+
+cgm_vec3 bgcolor(const cgm_ray *ray)
+{
+ return cgm_vvec(0, 0, 0);
+}
+
+cgm_vec3 shade(const cgm_ray *ray, const struct rayhit *hit, int maxiter)
+{
+ int i, num_lights;
+ cgm_vec3 color, dcol, scol, texel, vdir;
+ struct material *mtl;
+ struct light *lt;
+
+ dcol = ambient;
+ cgm_vcons(&scol, 0, 0, 0);
+
+ mtl = hit->obj->mtl;
+
+ vdir = ray->dir;
+ cgm_vneg(&vdir);
+ cgm_vnormalize(&vdir);
+
+ if(!(num_lights = scn_num_lights(scn))) {
+ calc_light(hit, &def_light, &vdir, &dcol, &scol);
+ }
+ for(i=0; i<num_lights; i++) {
+ lt = scn->lights[i];
+ calc_light(hit, lt, &vdir, &dcol, &scol);
+ }
+
+ if(mtl->texmap) {
+ texel = mtl->texmap->lookup(mtl->texmap, hit);
+ cgm_vmul(&dcol, &texel);
+ }
+
+ color = dcol;
+ cgm_vadd(&color, &scol);
+ return color;
+}
+
+static int calc_light(const struct rayhit *hit, const struct light *lt,
+ const cgm_vec3 *vdir, cgm_vec3 *dcol, cgm_vec3 *scol)
+{
+ float ndotl, ndoth, spec;
+ cgm_vec3 ldir, hdir;
+ cgm_ray ray;
+ struct material *mtl = hit->obj->mtl;
+
+ ldir = lt->pos;
+ cgm_vsub(&ldir, &hit->pos);
+
+ ray.origin = hit->pos;
+ ray.dir = ldir;
+
+ if(scn_intersect(scn, &ray, 0)) {
+ return 0; /* in shadow */
+ }
+
+ cgm_vnormalize(&ldir);
+
+ hdir = *vdir;
+ cgm_vadd(&hdir, &ldir);
+ cgm_vnormalize(&hdir);
+
+ ndotl = cgm_vdot(&hit->norm, &ldir);
+ if(ndotl < 0.0f) ndotl = 0.0f;
+ ndoth = cgm_vdot(&hit->norm, &hdir);
+ if(ndoth < 0.0f) ndoth = 0.0f;
+
+ spec = pow(ndoth, mtl->shin);
+
+ dcol->x += mtl->kd.x * ndotl * lt->color.x;
+ dcol->y += mtl->kd.y * ndotl * lt->color.y;
+ dcol->z += mtl->kd.z * ndotl * lt->color.z;
+
+ scol->x += mtl->ks.x * spec * lt->color.x;
+ scol->y += mtl->ks.y * spec * lt->color.y;
+ scol->z += mtl->ks.z * spec * lt->color.z;
+
+ return 1;
+}