From 5b0b916c561f602723b9ae80f5462a7939b652a1 Mon Sep 17 00:00:00 2001 From: Julian T Date: Thu, 13 Aug 2020 20:06:29 +0200 Subject: Pathtracing working with defuse and emissive lighting --- src/object.cpp | 8 +++--- src/object.hpp | 17 +++++++++--- src/render.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++------------ src/render.hpp | 20 ++++++++++++-- src/vector.cpp | 28 ++++++++++++++++++++ src/vector.hpp | 7 +++++ 6 files changed, 139 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/object.cpp b/src/object.cpp index 6c61d07..1449cbb 100644 --- a/src/object.cpp +++ b/src/object.cpp @@ -10,13 +10,15 @@ void Color::clamp() { if (m_z > 1) { m_z = 1; } } -Material::Material(Color color, double defuse) { +Material::Material(Color color, double defuse, double emissive) { m_color = color; m_defuse = defuse; + m_emissive = emissive; } -Color Material::reflect(const Vec3d &normal, const Vec3d &in, const Vec3d &out) const { - return Vec3d(m_color) * (out.dot(normal) * m_defuse); +Color Material::reflect(const Vec3d &normal, const Vec3d &in, const Vec3d &out, const Color &incol) const { + return Vec3d(m_color) * m_emissive + + (Vec3d(m_color) * Vec3d(incol)) * (out.dot(normal) * m_defuse); } Sphere::Sphere(const Material &mat, Vec3d center, double radius) : Shape(mat) { diff --git a/src/object.hpp b/src/object.hpp index 2957acb..e697ba2 100644 --- a/src/object.hpp +++ b/src/object.hpp @@ -15,19 +15,30 @@ class Color : public Vec3d { uint8_t g() { return m_y * 255; } uint8_t b() { return m_z * 255; } + Color& operator+=(const Color& op) { + Vec3d::operator+=(op); + return *this; + } void clamp(); - }; // Implements phong BRDF class Material { public: - Material(Color color, double defuse); + Material(Color color, double defuse, double emissive=0); + + Color reflect(const Vec3d &normal, const Vec3d &in, const Vec3d &out, const Color &incol) const; + + Color emits() const { + return m_color * m_emissive; + } - Color reflect(const Vec3d &normal, const Vec3d &in, const Vec3d &out) const; + // Whether the material is reflective + bool reflects() const { return m_defuse > 0; } private: Color m_color; double m_defuse; + double m_emissive; }; class Shape { diff --git a/src/render.cpp b/src/render.cpp index 197226e..0bbe5cb 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -2,20 +2,59 @@ #include "vector.hpp" #include "common.hpp" +#include #include #include #define FOV 1.74533 +// Uniform sampling +#define SAMPLING_POWER 0 + const Vec3d up = Vec3d(0, 1, 0); -Renderer::Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height) : +Sampler::Sampler() { + m_seed = 0; +} + +void Sampler::seed(unsigned seed) { + for (unsigned i = 0; i < seed; i++) { + rand_r(&m_seed); + } +} + +double Sampler::random() { + return (double)rand_r(&m_seed) / (double)RAND_MAX; +} + +Vec3d Sampler::sample(const Vec3d &norm) { + /* + auto theta = asin(pow(1 - random(), (double)1 / (1 + SAMPLING_POWER))); + auto phi = 2 * M_PI * random(); + */ + + auto theta = 2.0 * M_PI * random(); + auto phi = acos(2.0 * random() - 1.0); + + auto sinphi = sin(phi); + + auto newvec = Vec3d(cos(theta) * sinphi, sin(theta) * sinphi, cos(phi)); + + if (newvec.dot(norm) <= 0) { + newvec = -newvec; + } + + return newvec; +} + +Renderer::Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height, unsigned maxhops) : m_scn(scn) { m_eye = eye; m_target = target; m_width = width; m_height = height; + m_maxhops = maxhops; recalculate(); } @@ -48,12 +87,27 @@ Ray Renderer::findray(double x, double y) const { return Ray(m_eye, dir, true); } -Color Renderer::render(unsigned x, unsigned y) { +Color Renderer::render(unsigned x, unsigned y, unsigned samples) { auto r = findray(x, y); - return pathtrace_sample(r, 0); + + Color sum(0, 0, 0); + + for (unsigned i = 0; i < samples; i++) { + sum += pathtrace_sample(r, 0); + } + + if (samples < 2) { + return sum; + } else { + return Vec3d(sum) / (double)samples; + } } Color Renderer::pathtrace_sample(const Ray &r, unsigned hop) { + if (hop >= m_maxhops) { + return Color(0, 0, 0); + } + double dist; auto res = cast_ray(r, 0, &dist); @@ -61,24 +115,20 @@ Color Renderer::pathtrace_sample(const Ray &r, unsigned hop) { return Color(0, 0, 0); } - // Calculate endpoint - auto end = r.m_start + r.m_direction * dist; + auto col = res->m_mat.emits(); + if (res->m_mat.reflects()) { + // Calculate endpoint + auto end = r.m_start + r.m_direction * dist; + auto norm = res->norm_at(end, r.m_direction); - auto norm = res->norm_at(end, r.m_direction); + auto randdir = m_sampler.sample(norm); + auto newray = Ray(end, randdir, true); + auto incol = pathtrace_sample(newray, hop+1); - // Simulate single light - auto l = Vec3d(0, 7, 0); - - auto tolight = l - end; - auto distance = tolight.length(); - tolight.normalize(); - auto lightray = Ray(end, tolight, false); - - if (cast_ray(lightray, distance, nullptr)) { - return Color(0, 0, 0); + col += res->m_mat.reflect(norm, r.m_direction, newray.m_direction, incol); } - return res->m_mat.reflect(norm, r.m_direction, tolight); + return col; } const Shape* Renderer::cast_ray(const Ray &r, double chk_dist, double *dist_ret) { diff --git a/src/render.hpp b/src/render.hpp index bb4e9c5..1274bb5 100644 --- a/src/render.hpp +++ b/src/render.hpp @@ -5,13 +5,28 @@ #include "ray.hpp" #include "scene.hpp" +// Samples a random direction in a hemisphere, cosine weighed +// https://blog.thomaspoulet.fr/uniform-sampling-on-unit-hemisphere/ +class Sampler { + public: + Sampler(); + void seed(unsigned seed); + + Vec3d sample(const Vec3d &norm); + + private: + double random(); + unsigned m_seed; +}; + class Renderer { public: - Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height); + Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height, unsigned maxhops); - Color render(unsigned x, unsigned y); + Color render(unsigned x, unsigned y, unsigned samples); unsigned m_width, m_height; + Sampler m_sampler; private: void recalculate(); @@ -27,6 +42,7 @@ class Renderer { // User options Vec3d m_eye, m_target; + unsigned m_maxhops; // Calculated values Vec3d m_qx, m_qy, m_blc; diff --git a/src/vector.cpp b/src/vector.cpp index 1e4f5a1..51d8e2e 100644 --- a/src/vector.cpp +++ b/src/vector.cpp @@ -52,6 +52,13 @@ Vec3d Vec3d::operator+(const Vec3d &vec) const { ); } +Vec3d& Vec3d::operator+=(const Vec3d &vec) { + m_x += vec.m_x; + m_y += vec.m_y; + m_z += vec.m_z; + return *this; +} + Vec3d Vec3d::operator-(const Vec3d &vec) const { return Vec3d( m_x - vec.m_x, @@ -76,3 +83,24 @@ Vec3d Vec3d::operator*(double op) const { m_z * op ); } + +Vec3d Vec3d::operator*(const Vec3d &vec) const { + return Vec3d( + m_x * vec.m_x, + m_y * vec.m_y, + m_z * vec.m_z + ); +} + +Vec3d Vec3d::operator/(double op) const { + return Vec3d( + m_x / op, + m_y / op, + m_z / op + ); +} + +std::ostream& operator<<(std::ostream &out, const Vec3d &v){ + out << "[ " << v.m_x << ", " << v.m_y << ", " << v.m_z << " ]"; + return out; +} diff --git a/src/vector.hpp b/src/vector.hpp index 05de25c..20e8210 100644 --- a/src/vector.hpp +++ b/src/vector.hpp @@ -1,6 +1,8 @@ #ifndef VECTOR_H #define VECTOR_H +#include + class Vec3d { public: Vec3d(); @@ -16,9 +18,14 @@ class Vec3d { // Operators Vec3d operator+(const Vec3d &vec) const; + Vec3d& operator+=(const Vec3d &vec); Vec3d operator-(const Vec3d &vec) const; Vec3d operator-() const; Vec3d operator*(double) const; + Vec3d operator*(const Vec3d &vec) const; + Vec3d operator/(double) const; + + friend std::ostream& operator<<(std::ostream& os, const Vec3d &v); double m_x, m_y, m_z; }; -- cgit v1.2.3