From 65d26692ae8736f67f3951f088b26086eeb6b829 Mon Sep 17 00:00:00 2001 From: Julian T Date: Thu, 13 Aug 2020 21:17:30 +0200 Subject: Added nicer ui which can save --- app/draw.cpp | 6 +----- app/draw.hpp | 4 +--- app/mainwindow.cpp | 32 +++++++++++++++++++++++++++++++- app/mainwindow.hpp | 10 ++++++++++ app/rendercoord.cpp | 45 ++++++++++++++++++++++++++++++++++++++++----- app/rendercoord.hpp | 16 +++++++++++++++- src/render.cpp | 17 ++++++++--------- src/render.hpp | 17 +++++++++++++---- 8 files changed, 119 insertions(+), 28 deletions(-) diff --git a/app/draw.cpp b/app/draw.cpp index 469b469..b3187be 100644 --- a/app/draw.cpp +++ b/app/draw.cpp @@ -7,16 +7,12 @@ #include #include -DrawWidget::DrawWidget(unsigned width, unsigned height) : QWidget(), m_timer(this) { +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); - - QObject::connect(&m_timer, &QTimer::timeout, this, &DrawWidget::redraw); - - m_timer.start(500); } void DrawWidget::paintEvent(QPaintEvent*) { diff --git a/app/draw.hpp b/app/draw.hpp index f8e93a4..cee5734 100644 --- a/app/draw.hpp +++ b/app/draw.hpp @@ -13,6 +13,7 @@ class DrawWidget : public QWidget { void paintEvent(QPaintEvent*); QRgb *m_drawbuffer; + QImage m_img; unsigned m_width, m_height; ~DrawWidget(); @@ -20,10 +21,7 @@ class DrawWidget : public QWidget { void redraw(); private: - QImage m_img; unsigned char i; - - QTimer m_timer; }; #endif diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index 3077168..a365f38 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -1,12 +1,42 @@ #include "mainwindow.hpp" +#include +#include +#include +#include +#include +#include MainWindow::MainWindow(Renderer r) : m_drawer(500, 500), - m_render(this, m_drawer, r) + runstatus("Not running", this), + m_render(this, m_drawer, r, &runstatus) { setCentralWidget(&m_drawer); + auto saveAct = new QAction(tr("&Save as"), this); + saveAct->setStatusTip(tr("Save the rendered image")); + connect(saveAct, &QAction::triggered, this, &MainWindow::saveimage); + fileMenu = menuBar()->addMenu(tr("&File")); + fileMenu->addAction(saveAct); + helpMenu = menuBar()->addMenu(tr("&Help")); + helpMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt); + + statusBar()->addWidget(&runstatus); +} + +void MainWindow::saveimage() { + + QGuiApplication::setOverrideCursor(Qt::WaitCursor); + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save image"), "", tr("PNG image (*.png);;All Files (*)")); + if (fileName.isEmpty()) { + return; + } + + if (!m_drawer.m_img.save(fileName)) { + QMessageBox::information(this, tr("Unable to save file"), ""); + } } diff --git a/app/mainwindow.hpp b/app/mainwindow.hpp index 7e356a0..b2566f4 100644 --- a/app/mainwindow.hpp +++ b/app/mainwindow.hpp @@ -2,9 +2,13 @@ #define MAIN_H #include +#include +#include +#include #include "draw.hpp" #include "rendercoord.hpp" +#include #include class MainWindow : public QMainWindow { @@ -13,9 +17,15 @@ class MainWindow : public QMainWindow { public: MainWindow(Renderer r); + private slots: + void saveimage(); private: DrawWidget m_drawer; + QLabel runstatus; RenderCoordinator m_render; + + QMenu *fileMenu; + QMenu *helpMenu; }; #endif diff --git a/app/rendercoord.cpp b/app/rendercoord.cpp index 9c59acd..a5a3da5 100644 --- a/app/rendercoord.cpp +++ b/app/rendercoord.cpp @@ -4,6 +4,7 @@ #include #include +#include uint32_t colorToUint32(const Color &c) { Color cnew = Color(c); @@ -28,11 +29,12 @@ void RenderThread::run() { while (1) { // Wait for work m_work.acquire(); - m_render.m_sampler.seed(100); // Very expensive, but necesary to get live rendering Color *sum = new Color[m_render.m_width * m_render.m_height]; + m_current_samples = 0; + 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++) { @@ -42,6 +44,8 @@ void RenderThread::run() { m_writebuffer[index] = colorToUint32(sum[index] / sample); } } + + m_current_samples = sample; } // Signal done @@ -58,15 +62,25 @@ int RenderThread::render(QRgb *buffer, unsigned samples) { m_writebuffer = buffer; m_samples = samples; m_work.release(); + return 0; } -RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r) +// Running on main thread +unsigned RenderThread::current_samples() { + // No sync should not be a problem here. + return m_current_samples; +} + +RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r, QLabel *status) : QObject(parent), m_target(target), m_renderer(r), - m_worker(m_renderer, this) + m_worker(m_renderer, this), + m_timer(this) { + m_status = status; + m_worker.start(); QObject::connect(&m_worker, &RenderThread::done, @@ -74,10 +88,31 @@ RenderCoordinator::RenderCoordinator(QObject *parent, DrawWidget &target, Render m_worker.render(target.m_drawbuffer, 100); + m_state = running; + updateUi(); + + QObject::connect(&m_timer, &QTimer::timeout, this, &RenderCoordinator::updateUi); + + m_timer.start(500); + } void RenderCoordinator::workerDone(unsigned workerid) { - std::cout << workerid << " done!" << std::endl; - m_target.repaint(); + m_state = stopped; + m_timer.stop(); + updateUi(); } + +void RenderCoordinator::updateUi() { + m_target.repaint(); + + if (!m_status) { + return; + } + + std::ostringstream status; + status << states[m_state] << " " << m_worker.current_samples() << " samples"; + + m_status->setText(QString::fromStdString(status.str())); +} diff --git a/app/rendercoord.hpp b/app/rendercoord.hpp index 6aa8698..113b4dc 100644 --- a/app/rendercoord.hpp +++ b/app/rendercoord.hpp @@ -2,6 +2,7 @@ #define RENDER_THREAD_H #include "draw.hpp" +#include #include #include @@ -19,6 +20,7 @@ class RenderThread : public QThread { // Returns 0 if successful or 1 if busy int render(QRgb *buffer, unsigned samples); + unsigned current_samples(); signals: void done(unsigned workerid); @@ -30,6 +32,7 @@ class RenderThread : public QThread { QRgb *m_writebuffer; unsigned m_samples; + unsigned m_current_samples; Renderer m_render; @@ -38,23 +41,34 @@ class RenderThread : public QThread { unsigned m_id; }; +const std::string states[] = { "Stopped", "Running" }; +enum State { stopped, running }; + class RenderCoordinator : public QObject { Q_OBJECT public: - RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r); + RenderCoordinator(QObject *parent, DrawWidget &target, Renderer r, QLabel *status=nullptr); void setSamples(unsigned samples); void render(); public slots: void workerDone(unsigned workerid); + private slots: + void updateUi(); + private: DrawWidget &m_target; Renderer m_renderer; RenderThread m_worker; + QLabel *m_status; + QTimer m_timer; + + State m_state; + unsigned m_samples; }; diff --git a/src/render.cpp b/src/render.cpp index 0bbe5cb..c278797 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -13,28 +13,26 @@ const Vec3d up = Vec3d(0, 1, 0); -Sampler::Sampler() { - m_seed = 0; -} - -void Sampler::seed(unsigned seed) { +void Random::seed(unsigned seed) { for (unsigned i = 0; i < seed; i++) { rand_r(&m_seed); } } -double Sampler::random() { +double Random::operator()() { return (double)rand_r(&m_seed) / (double)RAND_MAX; } +Sampler::Sampler(Random &src) : m_src(src) { } + 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 theta = 2.0 * M_PI * m_src(); + auto phi = acos(2.0 * m_src() - 1.0); auto sinphi = sin(phi); @@ -48,6 +46,7 @@ Vec3d Sampler::sample(const Vec3d &norm) { } Renderer::Renderer(const Scene &scn, Vec3d eye, Vec3d target, unsigned width, unsigned height, unsigned maxhops) : + m_sampler(m_random), m_scn(scn) { m_eye = eye; @@ -88,11 +87,11 @@ Ray Renderer::findray(double x, double y) const { } Color Renderer::render(unsigned x, unsigned y, unsigned samples) { - auto r = findray(x, y); Color sum(0, 0, 0); for (unsigned i = 0; i < samples; i++) { + auto r = findray(x + m_random(), y + m_random()); sum += pathtrace_sample(r, 0); } diff --git a/src/render.hpp b/src/render.hpp index 1274bb5..7557fce 100644 --- a/src/render.hpp +++ b/src/render.hpp @@ -5,18 +5,25 @@ #include "ray.hpp" #include "scene.hpp" +class Random { + public: + void seed(unsigned seed); + double operator()(); + + private: + unsigned m_seed; +}; + // 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); + Sampler(Random &src); Vec3d sample(const Vec3d &norm); private: - double random(); - unsigned m_seed; + Random &m_src; }; class Renderer { @@ -40,6 +47,8 @@ class Renderer { const Scene &m_scn; + Random m_random; + // User options Vec3d m_eye, m_target; unsigned m_maxhops; -- cgit v1.2.3