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 }