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 --- app/draw.cpp | 11 ++++++- app/draw.hpp | 6 ++++ app/main.cpp | 9 +++--- app/rendercoord.cpp | 30 ++++++++++++------- src/object.cpp | 8 +++-- src/object.hpp | 17 +++++++++-- src/render.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++----------- src/render.hpp | 20 +++++++++++-- src/vector.cpp | 28 ++++++++++++++++++ src/vector.hpp | 7 +++++ 10 files changed, 179 insertions(+), 41 deletions(-) diff --git a/app/draw.cpp b/app/draw.cpp index 2f3a947..469b469 100644 --- a/app/draw.cpp +++ b/app/draw.cpp @@ -3,15 +3,20 @@ #include #include #include +#include #include #include -DrawWidget::DrawWidget(unsigned width, unsigned height) : QWidget() { +DrawWidget::DrawWidget(unsigned width, unsigned height) : QWidget(), m_timer(this) { m_width = width; m_height = height; m_drawbuffer = new QRgb[width * height]; m_img = QImage((uchar*)m_drawbuffer, width, height, QImage::Format_ARGB32); + + QObject::connect(&m_timer, &QTimer::timeout, this, &DrawWidget::redraw); + + m_timer.start(500); } void DrawWidget::paintEvent(QPaintEvent*) { @@ -19,6 +24,10 @@ void DrawWidget::paintEvent(QPaintEvent*) { painter.drawImage(0, 0, m_img); } +void DrawWidget::redraw() { + repaint(); +} + DrawWidget::~DrawWidget() { delete[] m_drawbuffer; } diff --git a/app/draw.hpp b/app/draw.hpp index 41d5d9c..f8e93a4 100644 --- a/app/draw.hpp +++ b/app/draw.hpp @@ -2,6 +2,7 @@ #define DRAW_H #include +#include #include class DrawWidget : public QWidget { @@ -15,9 +16,14 @@ class DrawWidget : public QWidget { unsigned m_width, m_height; ~DrawWidget(); + private slots: + void redraw(); + private: QImage m_img; unsigned char i; + + QTimer m_timer; }; #endif diff --git a/app/main.cpp b/app/main.cpp index e174d4d..48e26fe 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -18,19 +18,20 @@ int main(int argc, char *argv[]) Material blue(Color(0.3, 0.3, 1), 1); Material red(Color(1, 0.3, 0.3), 1); Material white(Color(1, 1, 1), 1); + Material em(Color(1, 1, 1), 0, 2); - scn.addShape(new Sphere(blue, Vec3d(2, 6, -1), 1)); + scn.addShape(new Sphere(red, Vec3d(2, 6, -1), 1)); scn.addShape(new Sphere(white, Vec3d(0, 4, -1), 1.3)); scn.addShape(new Sphere(white, Vec3d(-2, 5, -2), 1.3)); - //scn.addShape(new Sphere(Vec3d(0, 7, 0), 0.5)); + scn.addShape(new Sphere(blue, Vec3d(0, 7, 0), 0.5)); scn.addShape(new Plane(white, Vec3d(0, 0, 0), Vec3d(0, 1, 0))); - scn.addShape(new Plane(white, Vec3d(0, 10, 0), Vec3d(0, 1, 0))); + scn.addShape(new Plane(em, Vec3d(0, 10, 0), Vec3d(0, 1, 0))); scn.addShape(new Plane(white, Vec3d(0, 0, -5), Vec3d(0, 0, 1))); scn.addShape(new Plane(red, Vec3d(-5, 0, 0), Vec3d(1, 0, 0))); scn.addShape(new Plane(blue, Vec3d(5, 0, 0), Vec3d(1, 0, 0))); - Renderer render(scn, Vec3d(0, 5, 4), Vec3d(0, 5, 0), 500, 500); + Renderer render(scn, Vec3d(0, 5, 4), Vec3d(0, 5, 0), 500, 500, 5); MainWindow main(render); main.show(); diff --git a/app/rendercoord.cpp b/app/rendercoord.cpp index f9fc9f8..9c59acd 100644 --- a/app/rendercoord.cpp +++ b/app/rendercoord.cpp @@ -5,12 +5,13 @@ #include -uint32_t colorToUint32(Color &c) { - c.clamp(); +uint32_t colorToUint32(const Color &c) { + Color cnew = Color(c); + cnew.clamp(); return (0xFF << 24) + - (c.r() << 16) + - (c.g() << 8) + - c.b(); + (cnew.r() << 16) + + (cnew.g() << 8) + + cnew.b(); } // Run by main thread @@ -27,12 +28,19 @@ void RenderThread::run() { while (1) { // Wait for work m_work.acquire(); + m_render.m_sampler.seed(100); - for (unsigned x = 0; x < m_render.m_width; x++) { - for (unsigned y = 0; y < m_render.m_height; y++) { - auto c = m_render.render(m_render.m_width - x, m_render.m_height - y); - m_writebuffer[x + y * m_render.m_height] = - static_cast(colorToUint32(c)); + // Very expensive, but necesary to get live rendering + Color *sum = new Color[m_render.m_width * m_render.m_height]; + + for (unsigned sample = 1; sample < m_samples+1; sample++) { + for (unsigned x = 0; x < m_render.m_width; x++) { + for (unsigned y = 0; y < m_render.m_height; y++) { + auto index = x + y * m_render.m_height; + sum[index] += m_render.render(m_render.m_width - x, m_render.m_height - y, 1); + + m_writebuffer[index] = colorToUint32(sum[index] / sample); + } } } @@ -64,7 +72,7 @@ RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Render QObject::connect(&m_worker, &RenderThread::done, this, &RenderCoordinator::workerDone); - m_worker.render(target.m_drawbuffer, 1); + m_worker.render(target.m_drawbuffer, 100); } 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