aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2020-08-13 20:06:29 +0200
committerJulian T <julian@jtle.dk>2020-08-13 20:06:29 +0200
commit5b0b916c561f602723b9ae80f5462a7939b652a1 (patch)
tree6ee419f0dd1649b2c329585551f06a555a631db8
parent690b72664ca8d471f5c117f6ed87aeae2de0a208 (diff)
Pathtracing working with defuse and emissive lighting
-rw-r--r--app/draw.cpp11
-rw-r--r--app/draw.hpp6
-rw-r--r--app/main.cpp9
-rw-r--r--app/rendercoord.cpp30
-rw-r--r--src/object.cpp8
-rw-r--r--src/object.hpp17
-rw-r--r--src/render.cpp84
-rw-r--r--src/render.hpp20
-rw-r--r--src/vector.cpp28
-rw-r--r--src/vector.hpp7
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 <qglobal.h>
#include <qimage.h>
#include <qrgb.h>
+#include <qtimer.h>
#include <qwindowdefs.h>
#include <iostream>
-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 <qimage.h>
+#include <qtimer.h>
#include <qwidget.h>
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 <render.hpp>
-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<QRgb>(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 <cstdlib>
#include <math.h>
#include <iostream>
#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 <iostream>
+
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;
};