# HG changeset patch # User John Tsiombikas # Date 1401072088 -10800 # Node ID e9da2916bc79e54e6aaab529b6bc70a1235b1cb6 # Parent d2b6cee8ea5c781852bdc7a95c2334e2577c6985 fixed the normal bug diff -r d2b6cee8ea5c -r e9da2916bc79 README.md --- a/README.md Sun May 25 02:23:39 2014 +0300 +++ b/README.md Mon May 26 05:41:28 2014 +0300 @@ -1,4 +1,4 @@ -#![Erebus](http://nuclear.mutantstargoat.com/sw/erebus/img/readme-logo.png) +#![Erebus](http://nuclear.mutantstargoat.com/sw/erebus/img/erebus_banner_med.jpg) Erebus is a free photorealistic renderer, written in C++11. Copyright (C) 2014 John Tsiombikas diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/erebus.cc --- a/liberebus/src/erebus.cc Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/erebus.cc Mon May 26 05:41:28 2014 +0300 @@ -3,8 +3,7 @@ #include #include #include "erebus.h" -#include "vmath/vector.h" -#include "image.h" +#include "erebus_impl.h" #include "scene.h" #include "geomobj.h" #include "rt.h" @@ -13,37 +12,8 @@ using namespace std::chrono; -struct Rect { - int x, y, width, height; - - bool operator ==(const Rect &r) { return memcmp(this, &r, sizeof r) == 0; } - bool operator !=(const Rect &r) { return memcmp(this, &r, sizeof r) != 0; } -}; - #define INVALID_RECT Rect{0, 0, 0, 0} -struct erebus { - Scene *scn; - - Image fbimg; - Image accum; // sample accumulator per pixel - Vector4 options[ERB_NUM_OPTIONS]; - - // render state - long cur_time; - int cur_pixel_x, cur_pixel_y; - Rect cur_rect; - int cur_sample; - - // interactive input - std::vector keystate; - std::vector bnstate; - int mouse_pos[2]; - - // debugging stuff - int dbg_nodesel; -}; - static void render_pixel(struct erebus *ctx, int x, int y, int sample); static std::mt19937 rnd_gen; @@ -66,7 +36,10 @@ ctx->cur_time = 0; ctx->cur_rect = INVALID_RECT; - ctx->options[ERB_OPT_MAX_SAMPLES].x = (float)INF_SAMPLES; + erb_setoptf(ctx, ERB_OPT_GAMMA, 2.2); + erb_setopti(ctx, ERB_OPT_MAX_ITER, 6); + erb_setopti(ctx, ERB_OPT_MAX_SAMPLES, INF_SAMPLES); + erb_setopti(ctx, ERB_OPT_NUM_THREADS, -1); ctx->dbg_nodesel = -1; return ctx; @@ -79,30 +52,69 @@ void erb_setopti(struct erebus *ctx, enum erb_option opt, int val) { - ctx->options[opt].x = val; + ctx->options[opt].ival = val; + ctx->options[opt].type = Option::Type::INT; } + void erb_setoptf(struct erebus *ctx, enum erb_option opt, float val) { - ctx->options[opt].x = val; + ctx->options[opt].fval = val; + ctx->options[opt].type = Option::Type::FLOAT; } + void erb_setoptfv(struct erebus *ctx, enum erb_option opt, float *vec) { for(int i=0; i<4; i++) { - ctx->options[opt][i] = vec[i]; + ctx->options[opt].vval[i] = vec[i]; } + ctx->options[opt].type = Option::Type::VEC; } int erb_getopti(struct erebus *ctx, enum erb_option opt) { - return ctx->options[opt].x; + switch(ctx->options[opt].type) { + case Option::Type::INT: + return ctx->options[opt].ival; + case Option::Type::FLOAT: + return (int)ctx->options[opt].fval; + case Option::Type::VEC: + return (int)ctx->options[opt].vval.x; + } + return 0; // can't happen } + float erb_getoptf(struct erebus *ctx, enum erb_option opt) { - return ctx->options[opt].x; + switch(ctx->options[opt].type) { + case Option::Type::INT: + return (float)ctx->options[opt].ival; + case Option::Type::FLOAT: + return ctx->options[opt].fval; + case Option::Type::VEC: + return ctx->options[opt].vval.x; + } + return 0.0f; // can't happen } + float *erb_getoptfv(struct erebus *ctx, enum erb_option opt) { - return &ctx->options[opt].x; + switch(ctx->options[opt].type) { + case Option::Type::INT: + { + int ival = ctx->options[opt].ival; + ctx->options[opt].vval = Vector4(ival, ival, ival, ival); + } + break; + case Option::Type::FLOAT: + { + float fval = ctx->options[opt].fval; + ctx->options[opt].vval = Vector4(fval, fval, fval, fval); + } + default: + break; + } + + return &ctx->options[opt].vval.x; } float *erb_get_framebuffer(struct erebus *ctx) @@ -115,13 +127,14 @@ printf("starting new frame...\n"); ctx->cur_time = ms; - int xsz = ctx->options[ERB_OPT_WIDTH].x; - int ysz = ctx->options[ERB_OPT_HEIGHT].x; + int xsz = erb_getopti(ctx, ERB_OPT_WIDTH); + int ysz = erb_getopti(ctx, ERB_OPT_HEIGHT); ctx->fbimg.create(xsz, ysz); ctx->accum.create(xsz, ysz); ctx->cur_rect = INVALID_RECT; + ctx->inv_gamma = 1.0f / erb_getoptf(ctx, ERB_OPT_GAMMA); } int erb_render(struct erebus *ctx, long timeout) @@ -144,7 +157,7 @@ ctx->scn->update(); - int max_samples = ctx->options[ERB_OPT_MAX_SAMPLES].x; + int max_samples = erb_getopti(ctx, ERB_OPT_MAX_SAMPLES); if(timeout > 0) { auto start_time = steady_clock::now(); @@ -165,7 +178,8 @@ return 1; } - if(ctx->options[ERB_OPT_MAX_SAMPLES].x == (float)INF_SAMPLES) { + if(max_samples == INF_SAMPLES) { + // don't allow infinite samples when rendering non-progressively max_samples = 128; } @@ -191,16 +205,16 @@ // XXX for now just create a test scene here Sphere *sph = new Sphere; - sph->mtl.set_attrib("albedo", Color(1.0, 0.3, 0.2)); + sph->mtl.set_attrib("diffuse", Color(1.0, 0.3, 0.2)); SceneNode *sph_node = new SceneNode(sph); ctx->scn->add_object(sph); ctx->scn->add_node(sph_node); sph = new Sphere; - sph->mtl.set_attrib("albedo", Color(0.3, 0.4, 1.0)); + sph->mtl.set_attrib("diffuse", Color(0.3, 0.4, 1.0)); sph_node = new SceneNode(sph); - sph_node->set_position(Vector3(0, -3.0, 0)); - //sph_node->set_scaling(Vector3(4.0, 4.0, 4.0) * 0.3); + sph_node->set_position(Vector3(0, -251.0, 0)); + sph_node->set_scaling(Vector3(250.0, 250.0, 250.0)); ctx->scn->add_object(sph); ctx->scn->add_node(sph_node); @@ -208,7 +222,7 @@ lt->mtl.set_attrib("emissive", Color(10, 10, 10)); SceneNode *lt_node = new SceneNode(lt); lt_node->set_position(Vector3(-15, 15, -10)); - lt_node->set_scaling(Vector3(5, 5, 5)); + lt_node->set_scaling(Vector3(8, 8, 8)); ctx->scn->add_object(lt); ctx->scn->add_node(lt_node); @@ -331,15 +345,26 @@ float *accum = ctx->accum.get_pixels() + offs; Ray ray = cam->get_primary_ray(x, y, xsz, ysz, sample); - Color c = ray_trace(ray, ctx->scn, 0); + Color c = ray_trace(ctx, ray, 0); accum[0] += c.x; accum[1] += c.y; accum[2] += c.z; accum[3] += c.w; float inv_samples = 1.0f / (float)(sample + 1); - pix[0] = accum[0] * inv_samples; - pix[1] = accum[1] * inv_samples; - pix[2] = accum[2] * inv_samples; + pix[0] = pow(accum[0] * inv_samples, ctx->inv_gamma); + pix[1] = pow(accum[1] * inv_samples, ctx->inv_gamma); + pix[2] = pow(accum[2] * inv_samples, ctx->inv_gamma); pix[3] = accum[3] * inv_samples; } + +bool Rect::operator ==(const Rect &r) const +{ + return memcmp(this, &r, sizeof r) == 0; +} + +bool Rect::operator !=(const Rect &r) const +{ + return memcmp(this, &r, sizeof r) != 0; +} + diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/erebus_impl.h --- a/liberebus/src/erebus_impl.h Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/erebus_impl.h Mon May 26 05:41:28 2014 +0300 @@ -1,6 +1,49 @@ #ifndef EREBUS_IMPL_H_ #define EREBUS_IMPL_H_ +#include +#include +#include "erebus.h" +#include "image.h" +#include "scene.h" + +struct Option { + enum Type { INT, FLOAT, VEC } type; + int ival; + float fval; + Vector4 vval; +}; + +struct Rect { + int x, y, width, height; + + bool operator ==(const Rect &r) const; + bool operator !=(const Rect &r) const; +}; + +struct erebus { + Scene *scn; + + Image fbimg; + Image accum; // sample accumulator per pixel + Option options[ERB_NUM_OPTIONS]; + + // render state + float inv_gamma; + long cur_time; + int cur_pixel_x, cur_pixel_y; + Rect cur_rect; + int cur_sample; + + // interactive input + std::vector keystate; + std::vector bnstate; + int mouse_pos[2]; + + // debugging stuff + int dbg_nodesel; +}; + float randf(float low = 0.0f, float high = 1.0f); #endif // EREBUS_IMPL_H_ diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/geomobj.cc --- a/liberebus/src/geomobj.cc Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/geomobj.cc Mon May 26 05:41:28 2014 +0300 @@ -22,7 +22,7 @@ Vector3 GeomObject::calc_normal(const RayHit &hit) const { // when you look at singularities, the singularities always look back at you :) - return -(hit.world_ray.dir).normalized(); + return -(hit.local_ray.dir).normalized(); } Vector3 GeomObject::calc_tangent(const RayHit &hit) const @@ -45,16 +45,16 @@ float c = dot_product(ray.origin, ray.origin) - 1.0; float d = b * b - 4.0 * a * c; - if(d < 1e-4) return false; + if(d < 1e-6) return false; float sqrt_d = sqrt(d); float t0 = (-b + sqrt_d) / (2.0 * a); float t1 = (-b - sqrt_d) / (2.0 * a); - if(t0 < 1e-4) t0 = t1; - if(t1 < 1e-4) t1 = t0; + if(t0 < 1e-6) t0 = t1; + if(t1 < 1e-6) t1 = t0; float t = t0 < t1 ? t0 : t1; - if(t < 1e-4) return false; + if(t < 1e-6) return false; if(hit) { hit->dist = t; @@ -66,7 +66,7 @@ Vector3 Sphere::calc_normal(const RayHit &hit) const { - Vector3 pt = hit.world_ray.origin + hit.world_ray.dir * hit.dist; + Vector3 pt = hit.local_ray.origin + hit.local_ray.dir * hit.dist; return pt.normalized(); } @@ -88,7 +88,7 @@ Vector2 Sphere::calc_texcoords(const RayHit &hit) const { - Vector3 pt = hit.world_ray.origin + hit.world_ray.dir * hit.dist; + Vector3 pt = hit.local_ray.origin + hit.local_ray.dir * hit.dist; pt.normalize(); float theta = atan2(pt.z, pt.x); diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/object.cc --- a/liberebus/src/object.cc Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/object.cc Mon May 26 05:41:28 2014 +0300 @@ -1,4 +1,39 @@ +#include #include "object.h" +#include "geomobj.h" +#include "snode.h" + +RayHit::RayHit() +{ + dist = 0; + node = 0; + obj = subobj = 0; +} + +Vector3 RayHit::calc_normal() const +{ + assert(obj->get_type() == ObjType::geom); + Vector3 norm = ((const GeomObject*)obj)->calc_normal(*this); + + const Matrix4x4 &xform = node->get_inv_matrix(); + return norm.transformed(Matrix3x3(xform).transposed()); +} + +Vector3 RayHit::calc_tangent() const +{ + assert(obj->get_type() == ObjType::geom); + Vector3 tang = ((const GeomObject*)obj)->calc_tangent(*this); + + const Matrix4x4 &xform = node->get_matrix(); + return tang.transformed(Matrix3x3(xform).transposed()); +} + +Vector2 RayHit::calc_texcoords() const +{ + assert(obj->get_type() == ObjType::geom); + return ((const GeomObject*)obj)->calc_texcoords(*this); +} + Object::Object() { diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/object.h --- a/liberebus/src/object.h Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/object.h Mon May 26 05:41:28 2014 +0300 @@ -5,12 +5,21 @@ #include "vmath/ray.h" class Object; +class SceneNode; -struct RayHit { +class RayHit { +public: float dist; Ray world_ray, local_ray; + const SceneNode *node; const Object *obj, *subobj; + + RayHit(); + + Vector3 calc_normal() const; + Vector3 calc_tangent() const; + Vector2 calc_texcoords() const; }; enum class ObjType { null, geom, camera }; diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/rt.cc --- a/liberebus/src/rt.cc Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/rt.cc Mon May 26 05:41:28 2014 +0300 @@ -4,38 +4,45 @@ #define MAX_ITER 8 -Color ray_trace(const Ray &ray, const Scene *scn, int iter) +Color ray_trace(struct erebus *ctx, const Ray &ray, int iter) { + const Scene *scn = ctx->scn; + if(!scn) { + return Color(1, 0, 0); + } + RayHit hit; if(!(scn->intersect(ray, &hit))) { return scn->get_env_color(ray); } - return shade(hit, scn, iter); + return shade(ctx, hit, iter); } -Color shade(const RayHit &hit, const Scene *scn, int iter) +Color shade(struct erebus *ctx, const RayHit &hit, int iter) { assert(hit.obj->get_type() == ObjType::geom); + int max_iter = erb_getopti(ctx, ERB_OPT_MAX_ITER); + const Scene *scn = ctx->scn; const GeomObject *obj = (const GeomObject*)hit.obj; const Material *mtl = &obj->mtl; const Reflectance *brdf = obj->brdf; const Ray &ray = hit.world_ray; - Vector3 norm = obj->calc_normal(hit); - Vector2 texcoords = obj->calc_texcoords(hit); + Vector3 norm = hit.calc_normal(); + Vector2 texcoords = hit.calc_texcoords(); - Color color = mtl->get_attrib_color("albedo", texcoords.x, texcoords.y); + Color color = mtl->get_attrib_color("diffuse", texcoords.x, texcoords.y); Color res = mtl->get_attrib_color("emissive") + color * scn->get_env().ambient; Vector3 sample_dir; float prob = brdf->sample(norm, -hit.world_ray.dir, &sample_dir); - if(iter < MAX_ITER && randf() <= prob) { + if(iter < max_iter && randf() <= prob) { Ray sample_ray; sample_ray.origin = ray.origin + ray.dir * hit.dist; sample_ray.dir = sample_dir; - res += ray_trace(sample_ray, scn, iter + 1) * color; + res += ray_trace(ctx, sample_ray, iter + 1) * color; } return res; } diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/rt.h --- a/liberebus/src/rt.h Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/rt.h Mon May 26 05:41:28 2014 +0300 @@ -3,9 +3,9 @@ #include "geomobj.h" #include "color.h" -#include "scene.h" +#include "erebus_impl.h" -Color ray_trace(const Ray &ray, const Scene *scn, int iter); -Color shade(const RayHit &hit, const Scene *scn, int iter); +Color ray_trace(struct erebus *ctx, const Ray &ray, int iter); +Color shade(struct erebus *ctx, const RayHit &hit, int iter); #endif // RT_H_ diff -r d2b6cee8ea5c -r e9da2916bc79 liberebus/src/snode.cc --- a/liberebus/src/snode.cc Sun May 25 02:23:39 2014 +0300 +++ b/liberebus/src/snode.cc Mon May 26 05:41:28 2014 +0300 @@ -119,6 +119,16 @@ return scale; // TODO } +const Matrix4x4 &SceneNode::get_matrix() const +{ + return xform; +} + +const Matrix4x4 &SceneNode::get_inv_matrix() const +{ + return inv_xform; +} + void SceneNode::update_node(long msec) { @@ -154,6 +164,8 @@ if(!hit) return true; if(hit->dist < nearest.dist) { nearest = *hit; + nearest.node = this; + nearest.local_ray = local_ray; } } } @@ -169,7 +181,6 @@ if(nearest.dist < FLT_MAX) { *hit = nearest; - hit->local_ray = local_ray; hit->world_ray = ray; return true; }