erebus

changeset 17:e9da2916bc79

fixed the normal bug
author John Tsiombikas <nuclear@member.fsf.org>
date Mon, 26 May 2014 05:41:28 +0300
parents d2b6cee8ea5c
children 09028848f276
files README.md liberebus/src/erebus.cc liberebus/src/erebus_impl.h liberebus/src/geomobj.cc liberebus/src/object.cc liberebus/src/object.h liberebus/src/rt.cc liberebus/src/rt.h liberebus/src/snode.cc
diffstat 9 files changed, 202 insertions(+), 72 deletions(-) [+]
line diff
     1.1 --- a/README.md	Sun May 25 02:23:39 2014 +0300
     1.2 +++ b/README.md	Mon May 26 05:41:28 2014 +0300
     1.3 @@ -1,4 +1,4 @@
     1.4 -#![Erebus](http://nuclear.mutantstargoat.com/sw/erebus/img/readme-logo.png)
     1.5 +#![Erebus](http://nuclear.mutantstargoat.com/sw/erebus/img/erebus_banner_med.jpg)
     1.6  
     1.7  Erebus is a free photorealistic renderer, written in C++11.
     1.8  Copyright (C) 2014  John Tsiombikas <nuclear@member.fsf.org>
     2.1 --- a/liberebus/src/erebus.cc	Sun May 25 02:23:39 2014 +0300
     2.2 +++ b/liberebus/src/erebus.cc	Mon May 26 05:41:28 2014 +0300
     2.3 @@ -3,8 +3,7 @@
     2.4  #include <chrono>
     2.5  #include <random>
     2.6  #include "erebus.h"
     2.7 -#include "vmath/vector.h"
     2.8 -#include "image.h"
     2.9 +#include "erebus_impl.h"
    2.10  #include "scene.h"
    2.11  #include "geomobj.h"
    2.12  #include "rt.h"
    2.13 @@ -13,37 +12,8 @@
    2.14  
    2.15  using namespace std::chrono;
    2.16  
    2.17 -struct Rect {
    2.18 -	int x, y, width, height;
    2.19 -
    2.20 -	bool operator ==(const Rect &r) { return memcmp(this, &r, sizeof r) == 0; }
    2.21 -	bool operator !=(const Rect &r) { return memcmp(this, &r, sizeof r) != 0; }
    2.22 -};
    2.23 -
    2.24  #define INVALID_RECT	Rect{0, 0, 0, 0}
    2.25  
    2.26 -struct erebus {
    2.27 -	Scene *scn;
    2.28 -
    2.29 -	Image<float> fbimg;
    2.30 -	Image<float> accum;	// sample accumulator per pixel
    2.31 -	Vector4 options[ERB_NUM_OPTIONS];
    2.32 -
    2.33 -	// render state
    2.34 -	long cur_time;
    2.35 -	int cur_pixel_x, cur_pixel_y;
    2.36 -	Rect cur_rect;
    2.37 -	int cur_sample;
    2.38 -
    2.39 -	// interactive input
    2.40 -	std::vector<bool> keystate;
    2.41 -	std::vector<bool> bnstate;
    2.42 -	int mouse_pos[2];
    2.43 -
    2.44 -	// debugging stuff
    2.45 -	int dbg_nodesel;
    2.46 -};
    2.47 -
    2.48  static void render_pixel(struct erebus *ctx, int x, int y, int sample);
    2.49  
    2.50  static std::mt19937 rnd_gen;
    2.51 @@ -66,7 +36,10 @@
    2.52  	ctx->cur_time = 0;
    2.53  	ctx->cur_rect = INVALID_RECT;
    2.54  
    2.55 -	ctx->options[ERB_OPT_MAX_SAMPLES].x = (float)INF_SAMPLES;
    2.56 +	erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2);
    2.57 +	erb_setopti(ctx, ERB_OPT_MAX_ITER, 6);
    2.58 +	erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES);
    2.59 +	erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1);
    2.60  
    2.61  	ctx->dbg_nodesel = -1;
    2.62  	return ctx;
    2.63 @@ -79,30 +52,69 @@
    2.64  
    2.65  void erb_setopti(struct erebus *ctx, enum erb_option opt, int val)
    2.66  {
    2.67 -	ctx->options[opt].x = val;
    2.68 +	ctx->options[opt].ival = val;
    2.69 +	ctx->options[opt].type = Option::Type::INT;
    2.70  }
    2.71 +
    2.72  void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val)
    2.73  {
    2.74 -	ctx->options[opt].x = val;
    2.75 +	ctx->options[opt].fval = val;
    2.76 +	ctx->options[opt].type = Option::Type::FLOAT;
    2.77  }
    2.78 +
    2.79  void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec)
    2.80  {
    2.81  	for(int i=0; i<4; i++) {
    2.82 -		ctx->options[opt][i] = vec[i];
    2.83 +		ctx->options[opt].vval[i] = vec[i];
    2.84  	}
    2.85 +	ctx->options[opt].type = Option::Type::VEC;
    2.86  }
    2.87  
    2.88  int erb_getopti(struct erebus *ctx, enum erb_option opt)
    2.89  {
    2.90 -	return ctx->options[opt].x;
    2.91 +	switch(ctx->options[opt].type) {
    2.92 +	case Option::Type::INT:
    2.93 +		return ctx->options[opt].ival;
    2.94 +	case Option::Type::FLOAT:
    2.95 +		return (int)ctx->options[opt].fval;
    2.96 +	case Option::Type::VEC:
    2.97 +		return (int)ctx->options[opt].vval.x;
    2.98 +	}
    2.99 +	return 0;	// can't happen
   2.100  }
   2.101 +
   2.102  float erb_getoptf(struct erebus *ctx, enum erb_option opt)
   2.103  {
   2.104 -	return ctx->options[opt].x;
   2.105 +	switch(ctx->options[opt].type) {
   2.106 +	case Option::Type::INT:
   2.107 +		return (float)ctx->options[opt].ival;
   2.108 +	case Option::Type::FLOAT:
   2.109 +		return ctx->options[opt].fval;
   2.110 +	case Option::Type::VEC:
   2.111 +		return ctx->options[opt].vval.x;
   2.112 +	}
   2.113 +	return 0.0f;	// can't happen
   2.114  }
   2.115 +
   2.116  float *erb_getoptfv(struct erebus *ctx, enum erb_option opt)
   2.117  {
   2.118 -	return &ctx->options[opt].x;
   2.119 +	switch(ctx->options[opt].type) {
   2.120 +	case Option::Type::INT:
   2.121 +		{
   2.122 +			int ival = ctx->options[opt].ival;
   2.123 +			ctx->options[opt].vval = Vector4(ival, ival, ival, ival);
   2.124 +		}
   2.125 +		break;
   2.126 +	case Option::Type::FLOAT:
   2.127 +		{
   2.128 +			float fval = ctx->options[opt].fval;
   2.129 +			ctx->options[opt].vval = Vector4(fval, fval, fval, fval);
   2.130 +		}
   2.131 +	default:
   2.132 +		break;
   2.133 +	}
   2.134 +
   2.135 +	return &ctx->options[opt].vval.x;
   2.136  }
   2.137  
   2.138  float *erb_get_framebuffer(struct erebus *ctx)
   2.139 @@ -115,13 +127,14 @@
   2.140  	printf("starting new frame...\n");
   2.141  	ctx->cur_time = ms;
   2.142  
   2.143 -	int xsz = ctx->options[ERB_OPT_WIDTH].x;
   2.144 -	int ysz = ctx->options[ERB_OPT_HEIGHT].x;
   2.145 +	int xsz = erb_getopti(ctx, ERB_OPT_WIDTH);
   2.146 +	int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT);
   2.147  
   2.148  	ctx->fbimg.create(xsz, ysz);
   2.149  	ctx->accum.create(xsz, ysz);
   2.150  
   2.151  	ctx->cur_rect = INVALID_RECT;
   2.152 +	ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA);
   2.153  }
   2.154  
   2.155  int erb_render(struct erebus *ctx, long timeout)
   2.156 @@ -144,7 +157,7 @@
   2.157  
   2.158  	ctx->scn->update();
   2.159  
   2.160 -	int max_samples = ctx->options[ERB_OPT_MAX_SAMPLES].x;
   2.161 +	int max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES);
   2.162  
   2.163  	if(timeout > 0) {
   2.164  		auto start_time = steady_clock::now();
   2.165 @@ -165,7 +178,8 @@
   2.166  		return 1;
   2.167  	}
   2.168  
   2.169 -	if(ctx->options[ERB_OPT_MAX_SAMPLES].x == (float)INF_SAMPLES) {
   2.170 +	if(max_samples == INF_SAMPLES) {
   2.171 +		// don't allow infinite samples when rendering non-progressively
   2.172  		max_samples = 128;
   2.173  	}
   2.174  
   2.175 @@ -191,16 +205,16 @@
   2.176  
   2.177  	// XXX for now just create a test scene here
   2.178  	Sphere *sph = new Sphere;
   2.179 -	sph->mtl.set_attrib("albedo", Color(1.0, 0.3, 0.2));
   2.180 +	sph->mtl.set_attrib("diffuse", Color(1.0, 0.3, 0.2));
   2.181  	SceneNode *sph_node = new SceneNode(sph);
   2.182  	ctx->scn->add_object(sph);
   2.183  	ctx->scn->add_node(sph_node);
   2.184  
   2.185  	sph = new Sphere;
   2.186 -	sph->mtl.set_attrib("albedo", Color(0.3, 0.4, 1.0));
   2.187 +	sph->mtl.set_attrib("diffuse", Color(0.3, 0.4, 1.0));
   2.188  	sph_node = new SceneNode(sph);
   2.189 -	sph_node->set_position(Vector3(0, -3.0, 0));
   2.190 -	//sph_node->set_scaling(Vector3(4.0, 4.0, 4.0) * 0.3);
   2.191 +	sph_node->set_position(Vector3(0, -251.0, 0));
   2.192 +	sph_node->set_scaling(Vector3(250.0, 250.0, 250.0));
   2.193  	ctx->scn->add_object(sph);
   2.194  	ctx->scn->add_node(sph_node);
   2.195  
   2.196 @@ -208,7 +222,7 @@
   2.197  	lt->mtl.set_attrib("emissive", Color(10, 10, 10));
   2.198  	SceneNode *lt_node = new SceneNode(lt);
   2.199  	lt_node->set_position(Vector3(-15, 15, -10));
   2.200 -	lt_node->set_scaling(Vector3(5, 5, 5));
   2.201 +	lt_node->set_scaling(Vector3(8, 8, 8));
   2.202  	ctx->scn->add_object(lt);
   2.203  	ctx->scn->add_node(lt_node);
   2.204  
   2.205 @@ -331,15 +345,26 @@
   2.206  	float *accum = ctx->accum.get_pixels() + offs;
   2.207  
   2.208  	Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample);
   2.209 -	Color c = ray_trace(ray, ctx->scn, 0);
   2.210 +	Color c = ray_trace(ctx, ray, 0);
   2.211  	accum[0] += c.x;
   2.212  	accum[1] += c.y;
   2.213  	accum[2] += c.z;
   2.214  	accum[3] += c.w;
   2.215  
   2.216  	float inv_samples = 1.0f / (float)(sample + 1);
   2.217 -	pix[0] = accum[0] * inv_samples;
   2.218 -	pix[1] = accum[1] * inv_samples;
   2.219 -	pix[2] = accum[2] * inv_samples;
   2.220 +	pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma);
   2.221 +	pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma);
   2.222 +	pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma);
   2.223  	pix[3] = accum[3] * inv_samples;
   2.224  }
   2.225 +
   2.226 +bool Rect::operator ==(const Rect &r) const
   2.227 +{
   2.228 +	return memcmp(this, &r, sizeof r) == 0;
   2.229 +}
   2.230 +
   2.231 +bool Rect::operator !=(const Rect &r) const
   2.232 +{
   2.233 +	return memcmp(this, &r, sizeof r) != 0;
   2.234 +}
   2.235 +
     3.1 --- a/liberebus/src/erebus_impl.h	Sun May 25 02:23:39 2014 +0300
     3.2 +++ b/liberebus/src/erebus_impl.h	Mon May 26 05:41:28 2014 +0300
     3.3 @@ -1,6 +1,49 @@
     3.4  #ifndef EREBUS_IMPL_H_
     3.5  #define EREBUS_IMPL_H_
     3.6  
     3.7 +#include <vector>
     3.8 +#include <vmath/vmath.h>
     3.9 +#include "erebus.h"
    3.10 +#include "image.h"
    3.11 +#include "scene.h"
    3.12 +
    3.13 +struct Option {
    3.14 +	enum Type { INT, FLOAT, VEC } type;
    3.15 +	int ival;
    3.16 +	float fval;
    3.17 +	Vector4 vval;
    3.18 +};
    3.19 +
    3.20 +struct Rect {
    3.21 +	int x, y, width, height;
    3.22 +
    3.23 +	bool operator ==(const Rect &r) const;
    3.24 +	bool operator !=(const Rect &r) const;
    3.25 +};
    3.26 +
    3.27 +struct erebus {
    3.28 +	Scene *scn;
    3.29 +
    3.30 +	Image<float> fbimg;
    3.31 +	Image<float> accum;	// sample accumulator per pixel
    3.32 +	Option options[ERB_NUM_OPTIONS];
    3.33 +
    3.34 +	// render state
    3.35 +	float inv_gamma;
    3.36 +	long cur_time;
    3.37 +	int cur_pixel_x, cur_pixel_y;
    3.38 +	Rect cur_rect;
    3.39 +	int cur_sample;
    3.40 +
    3.41 +	// interactive input
    3.42 +	std::vector<bool> keystate;
    3.43 +	std::vector<bool> bnstate;
    3.44 +	int mouse_pos[2];
    3.45 +
    3.46 +	// debugging stuff
    3.47 +	int dbg_nodesel;
    3.48 +};
    3.49 +
    3.50  float randf(float low = 0.0f, float high = 1.0f);
    3.51  
    3.52  #endif	// EREBUS_IMPL_H_
     4.1 --- a/liberebus/src/geomobj.cc	Sun May 25 02:23:39 2014 +0300
     4.2 +++ b/liberebus/src/geomobj.cc	Mon May 26 05:41:28 2014 +0300
     4.3 @@ -22,7 +22,7 @@
     4.4  Vector3 GeomObject::calc_normal(const RayHit &hit) const
     4.5  {
     4.6  	// when you look at singularities, the singularities always look back at you :)
     4.7 -	return -(hit.world_ray.dir).normalized();
     4.8 +	return -(hit.local_ray.dir).normalized();
     4.9  }
    4.10  
    4.11  Vector3 GeomObject::calc_tangent(const RayHit &hit) const
    4.12 @@ -45,16 +45,16 @@
    4.13  	float c = dot_product(ray.origin, ray.origin) - 1.0;
    4.14  
    4.15  	float d = b * b - 4.0 * a * c;
    4.16 -	if(d < 1e-4) return false;
    4.17 +	if(d < 1e-6) return false;
    4.18  
    4.19  	float sqrt_d = sqrt(d);
    4.20  	float t0 = (-b + sqrt_d) / (2.0 * a);
    4.21  	float t1 = (-b - sqrt_d) / (2.0 * a);
    4.22  
    4.23 -	if(t0 < 1e-4) t0 = t1;
    4.24 -	if(t1 < 1e-4) t1 = t0;
    4.25 +	if(t0 < 1e-6) t0 = t1;
    4.26 +	if(t1 < 1e-6) t1 = t0;
    4.27  	float t = t0 < t1 ? t0 : t1;
    4.28 -	if(t < 1e-4) return false;
    4.29 +	if(t < 1e-6) return false;
    4.30  
    4.31  	if(hit) {
    4.32  		hit->dist = t;
    4.33 @@ -66,7 +66,7 @@
    4.34  
    4.35  Vector3 Sphere::calc_normal(const RayHit &hit) const
    4.36  {
    4.37 -	Vector3 pt = hit.world_ray.origin + hit.world_ray.dir * hit.dist;
    4.38 +	Vector3 pt = hit.local_ray.origin + hit.local_ray.dir * hit.dist;
    4.39  	return pt.normalized();
    4.40  }
    4.41  
    4.42 @@ -88,7 +88,7 @@
    4.43  
    4.44  Vector2 Sphere::calc_texcoords(const RayHit &hit) const
    4.45  {
    4.46 -	Vector3 pt = hit.world_ray.origin + hit.world_ray.dir * hit.dist;
    4.47 +	Vector3 pt = hit.local_ray.origin + hit.local_ray.dir * hit.dist;
    4.48  	pt.normalize();
    4.49  
    4.50  	float theta = atan2(pt.z, pt.x);
     5.1 --- a/liberebus/src/object.cc	Sun May 25 02:23:39 2014 +0300
     5.2 +++ b/liberebus/src/object.cc	Mon May 26 05:41:28 2014 +0300
     5.3 @@ -1,4 +1,39 @@
     5.4 +#include <assert.h>
     5.5  #include "object.h"
     5.6 +#include "geomobj.h"
     5.7 +#include "snode.h"
     5.8 +
     5.9 +RayHit::RayHit()
    5.10 +{
    5.11 +	dist = 0;
    5.12 +	node = 0;
    5.13 +	obj = subobj = 0;
    5.14 +}
    5.15 +
    5.16 +Vector3 RayHit::calc_normal() const
    5.17 +{
    5.18 +	assert(obj->get_type() == ObjType::geom);
    5.19 +	Vector3 norm = ((const GeomObject*)obj)->calc_normal(*this);
    5.20 +
    5.21 +	const Matrix4x4 &xform = node->get_inv_matrix();
    5.22 +	return norm.transformed(Matrix3x3(xform).transposed());
    5.23 +}
    5.24 +
    5.25 +Vector3 RayHit::calc_tangent() const
    5.26 +{
    5.27 +	assert(obj->get_type() == ObjType::geom);
    5.28 +	Vector3 tang = ((const GeomObject*)obj)->calc_tangent(*this);
    5.29 +
    5.30 +	const Matrix4x4 &xform = node->get_matrix();
    5.31 +	return tang.transformed(Matrix3x3(xform).transposed());
    5.32 +}
    5.33 +
    5.34 +Vector2 RayHit::calc_texcoords() const
    5.35 +{
    5.36 +	assert(obj->get_type() == ObjType::geom);
    5.37 +	return ((const GeomObject*)obj)->calc_texcoords(*this);
    5.38 +}
    5.39 +
    5.40  
    5.41  Object::Object()
    5.42  {
     6.1 --- a/liberebus/src/object.h	Sun May 25 02:23:39 2014 +0300
     6.2 +++ b/liberebus/src/object.h	Mon May 26 05:41:28 2014 +0300
     6.3 @@ -5,12 +5,21 @@
     6.4  #include "vmath/ray.h"
     6.5  
     6.6  class Object;
     6.7 +class SceneNode;
     6.8  
     6.9 -struct RayHit {
    6.10 +class RayHit {
    6.11 +public:
    6.12  	float dist;
    6.13  	Ray world_ray, local_ray;
    6.14  
    6.15 +	const SceneNode *node;
    6.16  	const Object *obj, *subobj;
    6.17 +
    6.18 +	RayHit();
    6.19 +
    6.20 +	Vector3 calc_normal() const;
    6.21 +	Vector3 calc_tangent() const;
    6.22 +	Vector2 calc_texcoords() const;
    6.23  };
    6.24  
    6.25  enum class ObjType { null, geom, camera };
     7.1 --- a/liberebus/src/rt.cc	Sun May 25 02:23:39 2014 +0300
     7.2 +++ b/liberebus/src/rt.cc	Mon May 26 05:41:28 2014 +0300
     7.3 @@ -4,38 +4,45 @@
     7.4  
     7.5  #define MAX_ITER	8
     7.6  
     7.7 -Color ray_trace(const Ray &ray, const Scene *scn, int iter)
     7.8 +Color ray_trace(struct erebus *ctx, const Ray &ray, int iter)
     7.9  {
    7.10 +	const Scene *scn = ctx->scn;
    7.11 +	if(!scn) {
    7.12 +		return Color(1, 0, 0);
    7.13 +	}
    7.14 +
    7.15  	RayHit hit;
    7.16  	if(!(scn->intersect(ray, &hit))) {
    7.17  		return scn->get_env_color(ray);
    7.18  	}
    7.19  
    7.20 -	return shade(hit, scn, iter);
    7.21 +	return shade(ctx, hit, iter);
    7.22  }
    7.23  
    7.24 -Color shade(const RayHit &hit, const Scene *scn, int iter)
    7.25 +Color shade(struct erebus *ctx, const RayHit &hit, int iter)
    7.26  {
    7.27  	assert(hit.obj->get_type() == ObjType::geom);
    7.28 +	int max_iter = erb_getopti(ctx, ERB_OPT_MAX_ITER);
    7.29 +	const Scene *scn = ctx->scn;
    7.30  	const GeomObject *obj = (const GeomObject*)hit.obj;
    7.31  	const Material *mtl = &obj->mtl;
    7.32  	const Reflectance *brdf = obj->brdf;
    7.33  	const Ray &ray = hit.world_ray;
    7.34  
    7.35 -	Vector3 norm = obj->calc_normal(hit);
    7.36 -	Vector2 texcoords = obj->calc_texcoords(hit);
    7.37 +	Vector3 norm = hit.calc_normal();
    7.38 +	Vector2 texcoords = hit.calc_texcoords();
    7.39  
    7.40 -	Color color = mtl->get_attrib_color("albedo", texcoords.x, texcoords.y);
    7.41 +	Color color = mtl->get_attrib_color("diffuse", texcoords.x, texcoords.y);
    7.42  	Color res = mtl->get_attrib_color("emissive") + color * scn->get_env().ambient;
    7.43  
    7.44  	Vector3 sample_dir;
    7.45  	float prob = brdf->sample(norm, -hit.world_ray.dir, &sample_dir);
    7.46 -	if(iter < MAX_ITER && randf() <= prob) {
    7.47 +	if(iter < max_iter && randf() <= prob) {
    7.48  		Ray sample_ray;
    7.49  		sample_ray.origin = ray.origin + ray.dir * hit.dist;
    7.50  		sample_ray.dir = sample_dir;
    7.51  
    7.52 -		res += ray_trace(sample_ray, scn, iter + 1) * color;
    7.53 +		res += ray_trace(ctx, sample_ray, iter + 1) * color;
    7.54  	}
    7.55  	return res;
    7.56  }
     8.1 --- a/liberebus/src/rt.h	Sun May 25 02:23:39 2014 +0300
     8.2 +++ b/liberebus/src/rt.h	Mon May 26 05:41:28 2014 +0300
     8.3 @@ -3,9 +3,9 @@
     8.4  
     8.5  #include "geomobj.h"
     8.6  #include "color.h"
     8.7 -#include "scene.h"
     8.8 +#include "erebus_impl.h"
     8.9  
    8.10 -Color ray_trace(const Ray &ray, const Scene *scn, int iter);
    8.11 -Color shade(const RayHit &hit, const Scene *scn, int iter);
    8.12 +Color ray_trace(struct erebus *ctx, const Ray &ray, int iter);
    8.13 +Color shade(struct erebus *ctx, const RayHit &hit, int iter);
    8.14  
    8.15  #endif	// RT_H_
     9.1 --- a/liberebus/src/snode.cc	Sun May 25 02:23:39 2014 +0300
     9.2 +++ b/liberebus/src/snode.cc	Mon May 26 05:41:28 2014 +0300
     9.3 @@ -119,6 +119,16 @@
     9.4  	return scale;	// TODO
     9.5  }
     9.6  
     9.7 +const Matrix4x4 &SceneNode::get_matrix() const
     9.8 +{
     9.9 +	return xform;
    9.10 +}
    9.11 +
    9.12 +const Matrix4x4 &SceneNode::get_inv_matrix() const
    9.13 +{
    9.14 +	return inv_xform;
    9.15 +}
    9.16 +
    9.17  
    9.18  void SceneNode::update_node(long msec)
    9.19  {
    9.20 @@ -154,6 +164,8 @@
    9.21  			if(!hit) return true;
    9.22  			if(hit->dist < nearest.dist) {
    9.23  				nearest = *hit;
    9.24 +				nearest.node = this;
    9.25 +				nearest.local_ray = local_ray;
    9.26  			}
    9.27  		}
    9.28  	}
    9.29 @@ -169,7 +181,6 @@
    9.30  
    9.31  	if(nearest.dist < FLT_MAX) {
    9.32  		*hit = nearest;
    9.33 -		hit->local_ray = local_ray;
    9.34  		hit->world_ray = ray;
    9.35  		return true;
    9.36  	}