aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2020-08-06 19:21:49 +0200
committerJulian T <julian@jtle.dk>2020-08-06 19:22:37 +0200
commit4348cc9581bfea05359485c5d2d074132d0271da (patch)
tree0c6d92a90ac4cf9acd326f632dcdc962ddca013a
parent893176a0b18a2281abe09def716ccc3db5583c3f (diff)
Renders scenes with a single hardcoded light and green objects
-rw-r--r--CMakeLists.txt1
-rw-r--r--app/draw.hpp2
-rw-r--r--app/main.cpp19
-rw-r--r--app/mainwindow.cpp10
-rw-r--r--app/mainwindow.hpp8
-rw-r--r--app/rendercoord.cpp75
-rw-r--r--app/rendercoord.hpp61
-rw-r--r--src/object.cpp5
-rw-r--r--src/object.hpp8
-rw-r--r--src/render.cpp124
-rw-r--r--src/render.hpp50
-rw-r--r--src/scene.cpp4
-rw-r--r--src/scene.hpp4
-rw-r--r--src/vector.cpp27
-rw-r--r--src/vector.hpp2
15 files changed, 379 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95e098b..df9bd70 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ FILE(GLOB sources ${CMAKE_SOURCE_DIR}/src/*.cpp)
FILE(GLOB test_sources ${CMAKE_SOURCE_DIR}/test/*.cpp)
add_executable(pathtracing ${sources} ${app_sources})
+target_include_directories(pathtracing PUBLIC src)
# Catch2 unittests
add_custom_target(test)
diff --git a/app/draw.hpp b/app/draw.hpp
index a00de79..41d5d9c 100644
--- a/app/draw.hpp
+++ b/app/draw.hpp
@@ -5,6 +5,8 @@
#include <qwidget.h>
class DrawWidget : public QWidget {
+ Q_OBJECT
+
public:
DrawWidget(unsigned width, unsigned height);
void paintEvent(QPaintEvent*);
diff --git a/app/main.cpp b/app/main.cpp
index 15262f2..64e413a 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -3,14 +3,31 @@
#include <qpushbutton.h>
#include "mainwindow.hpp"
+#include "vector.hpp"
+#include <scene.hpp>
+#include <render.hpp>
+#include <object.hpp>
using namespace std;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
+ Scene scn;
+ scn.addShape(new Sphere(Vec3d(2, 6, -1), 1));
+ scn.addShape(new Sphere(Vec3d(0, 4, -1), 1.3));
+ scn.addShape(new Sphere(Vec3d(-2, 5, -2), 1.3));
+ //scn.addShape(new Sphere(Vec3d(0, 7, 0), 0.5));
- MainWindow main;
+ scn.addShape(new Plane(Vec3d(0, 0, 0), Vec3d(0, 1, 0)));
+ scn.addShape(new Plane(Vec3d(0, 10, 0), Vec3d(0, 1, 0)));
+ scn.addShape(new Plane(Vec3d(0, 0, -5), Vec3d(0, 0, 1)));
+ scn.addShape(new Plane(Vec3d(-5, 0, 0), Vec3d(1, 0, 0)));
+ scn.addShape(new Plane(Vec3d(5, 0, 0), Vec3d(1, 0, 0)));
+
+ Renderer render(scn, Vec3d(0, 5, 4), Vec3d(0, 5, 0), 500, 500);
+
+ MainWindow main(render);
main.show();
return a.exec();
diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp
index e5f9bfb..3077168 100644
--- a/app/mainwindow.cpp
+++ b/app/mainwindow.cpp
@@ -1,8 +1,12 @@
#include "mainwindow.hpp"
-MainWindow::MainWindow()
- : drawer(500, 500) {
+MainWindow::MainWindow(Renderer r)
+ : m_drawer(500, 500),
+ m_render(this, m_drawer, r)
+{
+
+ setCentralWidget(&m_drawer);
+
- setCentralWidget(&drawer);
}
diff --git a/app/mainwindow.hpp b/app/mainwindow.hpp
index a6c1be3..7e356a0 100644
--- a/app/mainwindow.hpp
+++ b/app/mainwindow.hpp
@@ -4,16 +4,18 @@
#include <QMainWindow>
#include "draw.hpp"
+#include "rendercoord.hpp"
+#include <render.hpp>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
- MainWindow();
+ MainWindow(Renderer r);
private:
- DrawWidget drawer;
-
+ DrawWidget m_drawer;
+ RenderCoordinator m_render;
};
#endif
diff --git a/app/rendercoord.cpp b/app/rendercoord.cpp
new file mode 100644
index 0000000..8db7c97
--- /dev/null
+++ b/app/rendercoord.cpp
@@ -0,0 +1,75 @@
+#include "rendercoord.hpp"
+#include <qobject.h>
+#include <iostream>
+#include <qrgb.h>
+
+#include <render.hpp>
+
+uint32_t colorToUint32(Color &c) {
+ c.clamp();
+ return (0xFF << 24) +
+ (c.r() << 16) +
+ (c.g() << 8) +
+ c.b();
+}
+
+// Run by main thread
+RenderThread::RenderThread(Renderer r, QObject *parent, unsigned id)
+ : QThread(parent),
+ m_lock(1),
+ m_render(r)
+{
+ m_id = id;
+}
+
+// Run on new thread
+void RenderThread::run() {
+ while (1) {
+ // Wait for work
+ m_work.acquire();
+
+ 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(x, y);
+ m_writebuffer[x + y * m_render.m_height] =
+ static_cast<QRgb>(colorToUint32(c));
+ }
+ }
+
+ // Signal done
+ m_lock.release();
+ emit done(m_id);
+ }
+}
+
+int RenderThread::render(QRgb *buffer, unsigned samples) {
+ // Check if already running
+ if (!m_lock.tryAcquire()) {
+ return 1;
+ }
+ m_writebuffer = buffer;
+ m_samples = samples;
+ m_work.release();
+ return 0;
+}
+
+RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r)
+ : QObject(parent),
+ m_target(target),
+ m_renderer(r),
+ m_worker(m_renderer, this)
+{
+ m_worker.start();
+
+ QObject::connect(&m_worker, &RenderThread::done,
+ this, &RenderCoordinator::workerDone);
+
+ m_worker.render(target.m_drawbuffer, 1);
+
+}
+
+void RenderCoordinator::workerDone(unsigned workerid) {
+ std::cout << workerid << " done!" << std::endl;
+ m_target.repaint();
+}
+
diff --git a/app/rendercoord.hpp b/app/rendercoord.hpp
new file mode 100644
index 0000000..6aa8698
--- /dev/null
+++ b/app/rendercoord.hpp
@@ -0,0 +1,61 @@
+#ifndef RENDER_THREAD_H
+#define RENDER_THREAD_H
+
+#include "draw.hpp"
+#include <render.hpp>
+
+#include <qobject.h>
+#include <qrgb.h>
+#include <qthread.h>
+#include <qsemaphore.h>
+#include <vector>
+// https://doc.qt.io/archives/qt-4.8/qt-threads-mandelbrot-example.html
+
+class RenderThread : public QThread {
+ Q_OBJECT
+
+ public:
+ RenderThread(Renderer r, QObject *parent = nullptr, unsigned id = 0);
+
+ // Returns 0 if successful or 1 if busy
+ int render(QRgb *buffer, unsigned samples);
+
+ signals:
+ void done(unsigned workerid);
+
+ protected:
+ void run();
+
+ QSemaphore m_lock;
+
+ QRgb *m_writebuffer;
+ unsigned m_samples;
+
+ Renderer m_render;
+
+ // Value in here means work is to be done
+ QSemaphore m_work;
+ unsigned m_id;
+};
+
+class RenderCoordinator : public QObject {
+ Q_OBJECT
+
+ public:
+ RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r);
+ void setSamples(unsigned samples);
+ void render();
+
+ public slots:
+ void workerDone(unsigned workerid);
+
+ private:
+ DrawWidget &m_target;
+
+ Renderer m_renderer;
+ RenderThread m_worker;
+
+ unsigned m_samples;
+};
+
+#endif
diff --git a/src/object.cpp b/src/object.cpp
index e88ae13..179785a 100644
--- a/src/object.cpp
+++ b/src/object.cpp
@@ -1,6 +1,7 @@
#include "object.hpp"
#include <math.h>
+#include <iostream>
#include "common.hpp"
Sphere::Sphere(Vec3d center, double radius) {
@@ -15,10 +16,6 @@ Plane::Plane(Vec3d start, Vec3d norm) {
m_norm.normalize();
}
-void Object::setMaterial(std::shared_ptr<Material> m) {
- m_mat = m;
-}
-
Vec3d Sphere::norm_at(const Vec3d &point, const Vec3d&) const {
auto res = point - m_center;
res.normalize();
diff --git a/src/object.hpp b/src/object.hpp
index 3194dac..ac5122f 100644
--- a/src/object.hpp
+++ b/src/object.hpp
@@ -8,17 +8,15 @@
class Material {
};
-class Object {
+class Shape {
public:
void setMaterial(std::shared_ptr<Material> m);
- std::shared_ptr<Material> m_mat;
-
virtual Vec3d norm_at(const Vec3d &point, const Vec3d &indir) const = 0;
virtual double intersect(const Ray &ray, bool skip_dist) const = 0;
};
-class Sphere : Object {
+class Sphere : public Shape {
public:
Sphere(Vec3d center, double radius);
Vec3d norm_at(const Vec3d &point, const Vec3d &indir) const;
@@ -29,7 +27,7 @@ class Sphere : Object {
double m_radius;
};
-class Plane : Object {
+class Plane : public Shape {
public:
Plane(Vec3d start, Vec3d norm);
Vec3d norm_at(const Vec3d &point, const Vec3d &indir) const;
diff --git a/src/render.cpp b/src/render.cpp
new file mode 100644
index 0000000..5601863
--- /dev/null
+++ b/src/render.cpp
@@ -0,0 +1,124 @@
+#include "render.hpp"
+#include "vector.hpp"
+#include "common.hpp"
+
+#include <math.h>
+#include <iostream>
+
+#define FOV 1.74533
+
+const Vec3d up = Vec3d(0, 1, 0);
+
+void Color::clamp() {
+ if (m_x > 1) { m_x = 1; }
+ if (m_y > 1) { m_y = 1; }
+ if (m_z > 1) { m_z = 1; }
+}
+
+Renderer::Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height) :
+ m_scn(scn)
+{
+ m_eye = eye;
+ m_target = target;
+ m_width = width;
+ m_height = height;
+
+ recalculate();
+}
+
+void Renderer::recalculate() {
+ auto tmp = m_target - m_eye;
+
+ // Orthogonal vector to E
+ auto b = up.cross(tmp);
+
+ b.normalize();
+ tmp.normalize();
+
+ auto v = tmp.cross(b);
+
+ // Calculate size of viewplane
+ double gx = tan( FOV / 2);
+ double gy = gx * ((double) m_height / m_width);
+
+ // Calculate scaling vectors
+ m_qx = b * ((2 * gx) / (m_width - 1));
+ m_qy = v * ((2 * gy) / (m_height - 1));
+
+ // Calculate starting point
+ m_blc = tmp - (b * gx) - (v * gy);
+}
+
+Ray Renderer::findray(double x, double y) const {
+ auto dir = m_blc + (m_qx * x) + (m_qy * y);
+ return Ray(m_eye, dir, true);
+}
+
+Color Renderer::render(unsigned x, unsigned y) {
+ auto r = findray(x, y);
+ return pathtrace_sample(r, 0);
+}
+
+Color Renderer::pathtrace_sample(const Ray &r, unsigned hop) {
+ double dist;
+ auto res = cast_ray(r, 0, &dist);
+
+ if (!res) {
+ return Color(0, 0, 0);
+ }
+
+ // Calculate endpoint
+ auto end = r.m_start + r.m_direction * dist;
+
+ auto norm = res->norm_at(end, r.m_direction);
+
+ // 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);
+ }
+
+ auto green = Color(0, 1, 0);
+ return Color(green * norm.dot(tolight));
+}
+
+const Shape* Renderer::cast_ray(const Ray &r, double chk_dist, double *dist_ret) {
+ const Shape *smallest = nullptr;
+ double dist = 0;
+
+ for (auto obj : m_scn.objs) {
+ if (!obj) {
+ continue;
+ }
+ auto d = obj->intersect(r, false);
+ if (d > ZERO_APPROX) {
+ if (chk_dist > 0 && d < chk_dist) {
+ dist = d; smallest = obj;
+ goto exit;
+ }
+ if (d < dist || smallest == nullptr) {
+ dist = d; smallest = obj;
+ }
+ }
+ }
+
+ if (chk_dist > 0) {
+ // If we reach this it means none of the
+ // object where within distance.
+ return nullptr;
+ }
+
+exit:
+
+ if (dist_ret) {
+ *dist_ret = dist;
+ }
+
+ return smallest;
+}
diff --git a/src/render.hpp b/src/render.hpp
new file mode 100644
index 0000000..3fd84b1
--- /dev/null
+++ b/src/render.hpp
@@ -0,0 +1,50 @@
+#ifndef RENDER_H
+#define RENDER_H
+
+#include "vector.hpp"
+#include "ray.hpp"
+#include "scene.hpp"
+
+class Color : public Vec3d {
+ public:
+ Color() {}
+ Color(const Vec3d &v) : Vec3d(v) {}
+ Color(double r, double g, double b) : Vec3d(r, g, b) {}
+
+ uint8_t r() { return m_x * 255; }
+ uint8_t g() { return m_y * 255; }
+ uint8_t b() { return m_z * 255; }
+
+ void clamp();
+
+};
+
+class Renderer {
+ public:
+ Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height);
+
+ Color render(unsigned x, unsigned y);
+
+ unsigned m_width, m_height;
+
+ private:
+ void recalculate();
+ Ray findray(double x, double y) const ;
+
+ Color pathtrace_sample(const Ray &r, unsigned hop);
+ // Will return first result less than chk_dist.
+ // This is ignored if chk_dist is 0.
+ // If dist is non-null the resulting distance is written here.
+ const Shape* cast_ray(const Ray &r, double chk_dist, double *dist);
+
+ const Scene &m_scn;
+
+ // User options
+ Vec3d m_eye, m_target;
+
+ // Calculated values
+ Vec3d m_qx, m_qy, m_blc;
+
+};
+
+#endif
diff --git a/src/scene.cpp b/src/scene.cpp
index d866fe4..a8c0695 100644
--- a/src/scene.cpp
+++ b/src/scene.cpp
@@ -1,5 +1,5 @@
#include "scene.hpp"
-void Scene::addObject(Object &obj) {
- objs.push_back(&obj);
+void Scene::addShape(const Shape *obj) {
+ objs.push_back(obj);
}
diff --git a/src/scene.hpp b/src/scene.hpp
index a06ccb4..f270409 100644
--- a/src/scene.hpp
+++ b/src/scene.hpp
@@ -6,9 +6,9 @@
class Scene {
public:
- void addObject(Object &obj);
+ void addShape(const Shape *obj);
- std::vector<Object*> objs;
+ std::vector<const Shape*> objs;
};
diff --git a/src/vector.cpp b/src/vector.cpp
index 070f956..1e4f5a1 100644
--- a/src/vector.cpp
+++ b/src/vector.cpp
@@ -1,6 +1,7 @@
#include "vector.hpp"
#include <math.h>
+#include <stdexcept>
Vec3d::Vec3d() {
set(0, 0, 0);
@@ -19,7 +20,7 @@ void Vec3d::set(double x, double y, double z) {
void Vec3d::normalize() {
auto len = length();
if (len == 0) {
- throw "Normalizing zero vector";
+ throw std::runtime_error("Normalizing zero vector");
}
m_x /= len;
@@ -35,6 +36,22 @@ double Vec3d::dot(const Vec3d &vec) const {
return m_x * vec.m_x + m_y * vec.m_y + m_z * vec.m_z;
}
+Vec3d Vec3d::cross(const Vec3d &vec) const {
+ return Vec3d(
+ m_y * vec.m_z - m_z * vec.m_y,
+ m_z * vec.m_x - m_x * vec.m_z,
+ m_x * vec.m_y - m_y * vec.m_x
+ );
+}
+
+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-(const Vec3d &vec) const {
return Vec3d(
m_x - vec.m_x,
@@ -51,3 +68,11 @@ Vec3d Vec3d::operator-() const {
);
}
+
+Vec3d Vec3d::operator*(double op) const {
+ return Vec3d(
+ m_x * op,
+ m_y * op,
+ m_z * op
+ );
+}
diff --git a/src/vector.hpp b/src/vector.hpp
index 76eb883..05de25c 100644
--- a/src/vector.hpp
+++ b/src/vector.hpp
@@ -15,8 +15,10 @@ class Vec3d {
Vec3d cross(const Vec3d &vec) const;
// Operators
+ Vec3d operator+(const Vec3d &vec) const;
Vec3d operator-(const Vec3d &vec) const;
Vec3d operator-() const;
+ Vec3d operator*(double) const;
double m_x, m_y, m_z;
};