aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2020-07-26 12:56:27 +0200
committerJulian T <julian@jtle.dk>2020-07-26 12:56:27 +0200
commit893176a0b18a2281abe09def716ccc3db5583c3f (patch)
treea34da79b7dc0fcdbdd39e2a3f4000cc6a1c0a896
parent18960c4b88ce912e08b12182b835a7de75388b78 (diff)
Implemented object intersection and startet work on render gui
-rw-r--r--CMakeLists.txt26
-rw-r--r--app/draw.cpp24
-rw-r--r--app/draw.hpp21
-rw-r--r--app/main.cpp17
-rw-r--r--app/mainwindow.cpp8
-rw-r--r--app/mainwindow.hpp19
-rw-r--r--src/common.hpp6
-rw-r--r--src/main.cpp23
-rw-r--r--src/object.cpp89
-rw-r--r--src/object.hpp34
-rw-r--r--src/ray.cpp17
-rw-r--r--src/ray.hpp15
-rw-r--r--src/scene.cpp4
-rw-r--r--src/scene.hpp4
-rw-r--r--src/vector.cpp53
-rw-r--r--src/vector.hpp20
-rw-r--r--test/main.cpp2
-rw-r--r--test/object.cpp36
-rw-r--r--test/vector.cpp31
19 files changed, 404 insertions, 45 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8f895da..95e098b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,16 +2,30 @@ cmake_minimum_required(VERSION 3.10)
project(pathtracing)
+# cmake options
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+set(CMAKE_AUTOMOC ON)
+
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
+# Compiler options
+set(CMAKE_CXX_FLAGS "-Wall -Wextra")
+set(CMAKE_CXX_FLAGS_DEBUG "-g")
+set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+
+FILE(GLOB app_sources ${CMAKE_SOURCE_DIR}/app/*.cpp)
FILE(GLOB sources ${CMAKE_SOURCE_DIR}/src/*.cpp)
+FILE(GLOB test_sources ${CMAKE_SOURCE_DIR}/test/*.cpp)
-add_executable(pathtracing ${sources})
+add_executable(pathtracing ${sources} ${app_sources})
-set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake_modules" ${CMAKE_MODULE_PATH})
+# Catch2 unittests
+add_custom_target(test)
+add_executable(run_test EXCLUDE_FROM_ALL ${sources} ${test_sources})
+target_include_directories(run_test PUBLIC src)
+add_dependencies(test run_test)
-# Detect and add SFML
-find_package(SFML 2 REQUIRED system window graphics)
-include_directories(${SFML_INCLUDE_DIR})
-target_link_libraries(pathtracing ${SFML_LIBRARIES} ${SFML_DEPENDENCIES})
+# Qt
+find_package(Qt5 COMPONENTS Widgets REQUIRED)
+target_link_libraries(pathtracing Qt5::Widgets)
diff --git a/app/draw.cpp b/app/draw.cpp
new file mode 100644
index 0000000..2f3a947
--- /dev/null
+++ b/app/draw.cpp
@@ -0,0 +1,24 @@
+#include "draw.hpp"
+#include <qpainter.h>
+#include <qglobal.h>
+#include <qimage.h>
+#include <qrgb.h>
+#include <qwindowdefs.h>
+#include <iostream>
+
+DrawWidget::DrawWidget(unsigned width, unsigned height) : QWidget() {
+ m_width = width;
+ m_height = height;
+ m_drawbuffer = new QRgb[width * height];
+
+ m_img = QImage((uchar*)m_drawbuffer, width, height, QImage::Format_ARGB32);
+}
+
+void DrawWidget::paintEvent(QPaintEvent*) {
+ QPainter painter(this);
+ painter.drawImage(0, 0, m_img);
+}
+
+DrawWidget::~DrawWidget() {
+ delete[] m_drawbuffer;
+}
diff --git a/app/draw.hpp b/app/draw.hpp
new file mode 100644
index 0000000..a00de79
--- /dev/null
+++ b/app/draw.hpp
@@ -0,0 +1,21 @@
+#ifndef DRAW_H
+#define DRAW_H
+
+#include <qimage.h>
+#include <qwidget.h>
+
+class DrawWidget : public QWidget {
+ public:
+ DrawWidget(unsigned width, unsigned height);
+ void paintEvent(QPaintEvent*);
+
+ QRgb *m_drawbuffer;
+ unsigned m_width, m_height;
+
+ ~DrawWidget();
+ private:
+ QImage m_img;
+ unsigned char i;
+};
+
+#endif
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644
index 0000000..15262f2
--- /dev/null
+++ b/app/main.cpp
@@ -0,0 +1,17 @@
+#include <iostream>
+#include <qapplication.h>
+#include <qpushbutton.h>
+
+#include "mainwindow.hpp"
+
+using namespace std;
+
+int main(int argc, char *argv[])
+{
+ QApplication a(argc, argv);
+
+ MainWindow main;
+ main.show();
+
+ return a.exec();
+}
diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp
new file mode 100644
index 0000000..e5f9bfb
--- /dev/null
+++ b/app/mainwindow.cpp
@@ -0,0 +1,8 @@
+#include "mainwindow.hpp"
+
+MainWindow::MainWindow()
+ : drawer(500, 500) {
+
+ setCentralWidget(&drawer);
+
+}
diff --git a/app/mainwindow.hpp b/app/mainwindow.hpp
new file mode 100644
index 0000000..a6c1be3
--- /dev/null
+++ b/app/mainwindow.hpp
@@ -0,0 +1,19 @@
+#ifndef MAIN_H
+#define MAIN_H
+
+#include <QMainWindow>
+
+#include "draw.hpp"
+
+class MainWindow : public QMainWindow {
+ Q_OBJECT
+
+ public:
+ MainWindow();
+
+ private:
+ DrawWidget drawer;
+
+};
+
+#endif
diff --git a/src/common.hpp b/src/common.hpp
new file mode 100644
index 0000000..fd219bf
--- /dev/null
+++ b/src/common.hpp
@@ -0,0 +1,6 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#define ZERO_APPROX 1e-6
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
deleted file mode 100644
index 3c15429..0000000
--- a/src/main.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-#include <SFML/Graphics/CircleShape.hpp>
-#include <SFML/Graphics/RenderWindow.hpp>
-#include <SFML/Window/Event.hpp>
-#include <SFML/Window/VideoMode.hpp>
-#include <iostream>
-
-#include <SFML/Graphics.hpp>
-
-using namespace std;
-
-int main()
-{
- cout << "Hello World!" << endl;
-
- sf::RenderWindow window(sf::VideoMode(200, 200), "Yaah working");
- sf::CircleShape shape(100.f);
-
- window.clear();
- window.draw(shape);
- window.display();
-
- return 0;
-}
diff --git a/src/object.cpp b/src/object.cpp
new file mode 100644
index 0000000..e88ae13
--- /dev/null
+++ b/src/object.cpp
@@ -0,0 +1,89 @@
+#include "object.hpp"
+
+#include <math.h>
+#include "common.hpp"
+
+Sphere::Sphere(Vec3d center, double radius) {
+ m_center = center;
+ m_radius = radius;
+}
+
+Plane::Plane(Vec3d start, Vec3d norm) {
+ m_start = start;
+ m_norm = 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();
+ return res;
+}
+
+// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
+double Sphere::intersect(const Ray &ray, bool skip_dist) const {
+ // Calculate O - C used multiple places
+ auto oc = ray.m_start - m_center;
+ // Calculate components of quadratic formula
+ // a = 1 when ray.direction is a unit vector
+ auto a = 1;
+ auto b = 2 * ray.m_direction.dot(oc);
+ auto c = oc.dot(oc) - m_radius * m_radius;
+
+ // Solve quadratic function
+ auto discr = b * b - 4 * a * c;
+ if (discr < 0) {
+ // No solution
+ return -1;
+ }
+ if (skip_dist) {
+ // Do not calculate distance
+ return 1;
+ }
+
+ auto q = (b > 0) ?
+ -0.5 * (b + sqrt(discr)):
+ -0.5 * (b - sqrt(discr));
+ auto t1 = q; // Assuming a = 1
+ auto t0 = c / q;
+
+ // Find correct result
+ if (t0 <= ZERO_APPROX) {
+ t0 = t1;
+ }
+
+ if (t0 <= ZERO_APPROX) {
+ return -1;
+ }
+
+ return t0;
+}
+
+Vec3d Plane::norm_at(const Vec3d&, const Vec3d &indir) const {
+ auto scale = m_norm.dot(indir);
+ return scale > 0 ? -m_norm : m_norm;
+}
+
+// https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-plane-and-ray-disk-intersection
+// Requires that vectors are normalized
+// Skip dist is ignored as distance must be calculated
+double Plane::intersect(const Ray &ray, bool) const {
+ // If ray is parallel
+ auto nr = m_norm.dot(ray.m_direction);
+ if (abs(nr) < ZERO_APPROX) {
+ return -1;
+ }
+
+ // Calculate distance
+ auto dist = m_norm.dot(m_start - ray.m_start) / nr;
+ if (dist < 0) {
+ return -1;
+ }
+
+ return dist;
+}
diff --git a/src/object.hpp b/src/object.hpp
index 56c6968..3194dac 100644
--- a/src/object.hpp
+++ b/src/object.hpp
@@ -3,17 +3,41 @@
#include <memory>
#include "vector.hpp"
+#include "ray.hpp"
class Material {
- Vec3d color;
-
- double defuse;
- double emissive;
};
class Object {
public:
- std::shared_ptr<Material> m;
+ 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 {
+ public:
+ Sphere(Vec3d center, double radius);
+ Vec3d norm_at(const Vec3d &point, const Vec3d &indir) const;
+ double intersect(const Ray &ray, bool skip_dist) const;
+
+ private:
+ Vec3d m_center;
+ double m_radius;
+};
+
+class Plane : Object {
+ public:
+ Plane(Vec3d start, Vec3d norm);
+ Vec3d norm_at(const Vec3d &point, const Vec3d &indir) const;
+ double intersect(const Ray &ray, bool skip_dist) const;
+
+ private:
+ Vec3d m_start;
+ Vec3d m_norm;
};
#endif
diff --git a/src/ray.cpp b/src/ray.cpp
new file mode 100644
index 0000000..7bc6201
--- /dev/null
+++ b/src/ray.cpp
@@ -0,0 +1,17 @@
+#include "ray.hpp"
+
+Ray::Ray(Vec3d start, Vec3d direction, bool normalize) {
+ m_start = start;
+ m_direction = direction;
+
+ if (normalize) {
+ m_direction.normalize();
+ }
+}
+
+Ray::Ray(Vec3d a, Vec3d b) {
+ m_start = a;
+ m_direction = b - a;
+
+ m_direction.normalize();
+}
diff --git a/src/ray.hpp b/src/ray.hpp
new file mode 100644
index 0000000..6341d44
--- /dev/null
+++ b/src/ray.hpp
@@ -0,0 +1,15 @@
+#ifndef RAY_H
+#define RAY_H
+
+#include "vector.hpp"
+
+class Ray {
+ public:
+ Ray(Vec3d start, Vec3d direction, bool normalize);
+ Ray(Vec3d a, Vec3d b);
+
+ Vec3d m_start;
+ Vec3d m_direction;
+};
+
+#endif
diff --git a/src/scene.cpp b/src/scene.cpp
index d65dca7..d866fe4 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::addObject(Object &obj) {
+ objs.push_back(&obj);
}
diff --git a/src/scene.hpp b/src/scene.hpp
index 6e3f33e..a06ccb4 100644
--- a/src/scene.hpp
+++ b/src/scene.hpp
@@ -6,9 +6,9 @@
class Scene {
public:
- void addObject(Object obj);
+ void addObject(Object &obj);
- std::vector<Object> objs;
+ std::vector<Object*> objs;
};
diff --git a/src/vector.cpp b/src/vector.cpp
new file mode 100644
index 0000000..070f956
--- /dev/null
+++ b/src/vector.cpp
@@ -0,0 +1,53 @@
+#include "vector.hpp"
+
+#include <math.h>
+
+Vec3d::Vec3d() {
+ set(0, 0, 0);
+}
+
+Vec3d::Vec3d(double x, double y, double z) {
+ set(x, y, z);
+}
+
+void Vec3d::set(double x, double y, double z) {
+ m_x = x;
+ m_y = y;
+ m_z = z;
+}
+
+void Vec3d::normalize() {
+ auto len = length();
+ if (len == 0) {
+ throw "Normalizing zero vector";
+ }
+
+ m_x /= len;
+ m_y /= len;
+ m_z /= len;
+}
+
+double Vec3d::length() const {
+ return sqrt(m_x * m_x + m_y * m_y + m_z * m_z);
+}
+
+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::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 {
+ return Vec3d(
+ -m_x,
+ -m_y,
+ -m_z
+ );
+}
+
diff --git a/src/vector.hpp b/src/vector.hpp
index 6d15f34..76eb883 100644
--- a/src/vector.hpp
+++ b/src/vector.hpp
@@ -2,17 +2,23 @@
#define VECTOR_H
class Vec3d {
- Vec3d();
- Vec3d(double x, double y, double z);
+ public:
+ Vec3d();
+ Vec3d(double x, double y, double z);
- void set(double x, double y, double z);
- void normalize();
+ void set(double x, double y, double z);
+ void normalize();
- double length();
+ double length() const;
+ double dot(const Vec3d &vec) const;
- Vec3d cross(const Vec3d &vec);
+ Vec3d cross(const Vec3d &vec) const;
- // Operators
+ // Operators
+ Vec3d operator-(const Vec3d &vec) const;
+ Vec3d operator-() const;
+
+ double m_x, m_y, m_z;
};
#endif
diff --git a/test/main.cpp b/test/main.cpp
new file mode 100644
index 0000000..4ed06df
--- /dev/null
+++ b/test/main.cpp
@@ -0,0 +1,2 @@
+#define CATCH_CONFIG_MAIN
+#include <catch2/catch.hpp>
diff --git a/test/object.cpp b/test/object.cpp
new file mode 100644
index 0000000..bbce055
--- /dev/null
+++ b/test/object.cpp
@@ -0,0 +1,36 @@
+#include <object.hpp>
+#include <ray.hpp>
+#include <common.hpp>
+#include <vector.hpp>
+
+#include <catch2/catch.hpp>
+#include <math.h>
+
+TEST_CASE("Sphere normal at", "[sphere]") {
+ auto sph = Sphere(Vec3d(2, 3, 4), 2);
+
+ auto norm = sph.norm_at(Vec3d(2, 3, 2), Vec3d());
+ REQUIRE(norm.m_x == 0);
+ REQUIRE(norm.m_y == 0);
+ REQUIRE(norm.m_z == -1);
+}
+
+TEST_CASE("Sphere intersect", "[sphere]") {
+ auto sph = Sphere(Vec3d(2, 3, 4), 2);
+ auto ray = Ray(Vec3d(1, 0, 0), Vec3d(0, 1, 1.5), true);
+
+ auto dist = sph.intersect(ray, false);
+ REQUIRE(abs(dist - 3.28) < 0.01);
+}
+
+TEST_CASE("Plane intersect", "[plane]") {
+ auto pln = Plane(Vec3d(3, 4, 2), Vec3d(-6, -3, -2));
+ auto ray = Ray(Vec3d(0, 0, 0), Vec3d(-2, -1, 5));
+
+ auto dist = pln.intersect(ray, false);
+ REQUIRE(dist == -1);
+
+ ray = Ray(Vec3d(-2, -2, 0), Vec3d(-2, -1, 5));
+ dist = pln.intersect(ray, false);
+ REQUIRE(abs(dist - 20.4) < 0.1);
+}
diff --git a/test/vector.cpp b/test/vector.cpp
new file mode 100644
index 0000000..61648c6
--- /dev/null
+++ b/test/vector.cpp
@@ -0,0 +1,31 @@
+#include <vector.hpp>
+#include <common.hpp>
+#include <catch2/catch.hpp>
+
+TEST_CASE( "Vector length", "[vector]" ) {
+ auto vec = Vec3d(2, 4, 4);
+ REQUIRE(vec.length() == 6);
+ vec.set(0, 0, 0);
+ REQUIRE(vec.length() == 0);
+ vec.set(0, 3.5, 0);
+ REQUIRE(vec.length() == 3.5);
+}
+
+TEST_CASE("Vector_normal", "[vector]") {
+ auto vec = Vec3d(4, 5, 4545);
+ REQUIRE(vec.length() != 1.0);
+ vec.normalize();
+ REQUIRE(vec.length() - 1.0 < ZERO_APPROX);
+ vec.set(0, 0, 0);
+ REQUIRE_THROWS(vec.normalize());
+}
+
+TEST_CASE("Vector dot", "[vector]") {
+ auto a = Vec3d(4, 5, 6);
+ auto b = Vec3d(1, 2, 3);
+ REQUIRE(a.dot(b) == 32);
+ a.set(0, 0, 0);
+ REQUIRE(a.dot(b) == 0);
+ a.set(0, 5, 0);
+ REQUIRE(a.dot(b) == 10);
+}