Skip to content

implemented new render mode called progressive, for progressive image… #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 2.8.3)
cmake_minimum_required (VERSION 2.8.10)
project(nori)

add_subdirectory(ext ext_build)
Expand Down Expand Up @@ -31,6 +31,8 @@ include_directories(
${NANOGUI_EXTRA_INCS}
# Portable filesystem API
${FILESYSTEM_INCLUDE_DIR}
# STB Image Write
${STB_IMAGE_WRITE_INCLUDE_DIR}
)

# The following lines build the main executable. If you add a source
Expand Down Expand Up @@ -62,6 +64,7 @@ add_executable(nori
include/nori/transform.h
include/nori/vector.h
include/nori/warp.h
include/nori/rendermode.h

# Source code files
src/bitmap.cpp
Expand All @@ -86,6 +89,9 @@ add_executable(nori
src/microfacet.cpp
src/mirror.cpp
src/dielectric.cpp
src/blockwise.cpp
src/progressive.cpp
src/normals.cpp
)

add_definitions(${NANOGUI_EXTRA_DEFS})
Expand All @@ -104,4 +110,4 @@ add_executable(warptest
target_link_libraries(nori tbb_static pugixml IlmImf nanogui ${NANOGUI_EXTRA_LIBS})
target_link_libraries(warptest tbb_static nanogui ${NANOGUI_EXTRA_LIBS})

# vim: set et ts=2 sw=2 ft=cmake nospell:
# vim: set et ts=2 sw=2 ft=cmake nospell:
4 changes: 4 additions & 0 deletions include/nori/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ class BlockGenerator {

/// Return the total number of blocks
int getBlockCount() const { return m_blocksLeft; }

/// Set total number of blocks (for progressive rendering)
void setBlockCount(const Vector2i &size, int blockSize);

protected:
enum EDirection { ERight = 0, EDown, ELeft, EUp };

Expand Down
2 changes: 2 additions & 0 deletions include/nori/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class NoriObject {
ESampler,
ETest,
EReconstructionFilter,
ERenderMode,
EClassTypeCount
};

Expand Down Expand Up @@ -97,6 +98,7 @@ class NoriObject {
case ECamera: return "camera";
case EIntegrator: return "integrator";
case ESampler: return "sampler";
case ERenderMode: return "rendermode";
case ETest: return "test";
default: return "<unknown>";
}
Expand Down
51 changes: 51 additions & 0 deletions include/nori/rendermode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
This file is part of Nori, a simple educational ray tracer

Copyright (c) 2015 by Wenzel Jakob

Nori is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.

Nori is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Modified (c) 2018 by Christoph Kreisl idea from Sebastian Herholz
University of Tuebingen, Germany
*/

#include <nori/object.h>

NORI_NAMESPACE_BEGIN

/**
* \brief RenderMode handles different render modes
*
* This class serves as interface for new render modes
* In this example we implemented a progressive render mode for a faster visualization
* (default: blockwise, new: progressive)
*/
class RenderMode : public NoriObject {
public:

/// Release all memory
virtual ~RenderMode() { }

/// Main render process
virtual void render(Scene *scene, const std::string &filename) = 0;

/// Sub-Render process for one block
virtual void renderBlock(const Scene *scene, Sampler *sampler, ImageBlock &block) = 0;

EClassType getClassType() const { return ERenderMode; }

private:
/* nothing to-do here */
};

NORI_NAMESPACE_END
5 changes: 5 additions & 0 deletions include/nori/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#pragma once

#include <nori/accel.h>
#include <nori/rendermode.h>

NORI_NAMESPACE_BEGIN

Expand Down Expand Up @@ -55,6 +56,9 @@ class Scene : public NoriObject {
/// Return a pointer to the scene's sample generator
Sampler *getSampler() { return m_sampler; }

/// Return a pointer to the scene's render mode
RenderMode *getRenderMode() { return m_rendermode; }

/// Return a reference to an array containing all meshes
const std::vector<Mesh *> &getMeshes() const { return m_meshes; }

Expand Down Expand Up @@ -122,6 +126,7 @@ class Scene : public NoriObject {
Sampler *m_sampler = nullptr;
Camera *m_camera = nullptr;
Accel *m_accel = nullptr;
RenderMode *m_rendermode = nullptr;
};

NORI_NAMESPACE_END
13 changes: 13 additions & 0 deletions src/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,19 @@ BlockGenerator::BlockGenerator(const Vector2i &size, int blockSize)
m_numSteps = 1;
}

void BlockGenerator::setBlockCount(const Vector2i &size, int blockSize) {
m_size = size;
m_blockSize = blockSize;
m_numBlocks = Vector2i(
(int) std::ceil(size.x() / (float) blockSize),
(int) std::ceil(size.y() / (float) blockSize));
m_blocksLeft = m_numBlocks.x() * m_numBlocks.y();
m_direction = ERight;
m_block = Point2i(m_numBlocks / 2);
m_stepsLeft = 1;
m_numSteps = 1;
}

bool BlockGenerator::next(ImageBlock &block) {
tbb::mutex::scoped_lock lock(m_mutex);

Expand Down
168 changes: 168 additions & 0 deletions src/blockwise.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/*
This file is part of Nori, a simple educational ray tracer

Copyright (c) 2015 by Wenzel Jakob

Nori is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.

Nori is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

Modified (c) 2018 by Christoph Kreisl idea from Sebastian Herholz
University of Tuebingen, Germany
*/

#include <nori/parser.h>
#include <nori/scene.h>
#include <nori/camera.h>
#include <nori/block.h>
#include <nori/timer.h>
#include <nori/bitmap.h>
#include <nori/sampler.h>
#include <nori/integrator.h>
#include <nori/gui.h>
#include <tbb/parallel_for.h>
#include <tbb/blocked_range.h>
#include <filesystem/resolver.h>
#include <thread>

NORI_NAMESPACE_BEGIN

/// Blockwise render mode
class Blockwise : public RenderMode {
public:

Blockwise(const PropertyList &) { }

virtual ~Blockwise() { }

void render(Scene *scene, const std::string &filename) {
const Camera *camera = scene->getCamera();
Vector2i outputSize = camera->getOutputSize();
scene->getIntegrator()->preprocess(scene);

/* Create a block generator (i.e. a work scheduler) */
BlockGenerator blockGenerator(outputSize, NORI_BLOCK_SIZE);

/* Allocate memory for the entire output image and clear it */
ImageBlock result(outputSize, camera->getReconstructionFilter());
result.clear();

/* Create a window that visualizes the partially rendered result */
nanogui::init();
NoriScreen *screen = new NoriScreen(result);

/* Do the following in parallel and asynchronously */
std::thread render_thread([&] {
cout << "Rendering .. ";
cout.flush();
Timer timer;

tbb::blocked_range<int> range(0, blockGenerator.getBlockCount());

auto map = [&](const tbb::blocked_range<int> &range) {
/* Allocate memory for a small image block to be rendered
by the current thread */
ImageBlock block(Vector2i(NORI_BLOCK_SIZE),
camera->getReconstructionFilter());

/* Create a clone of the sampler for the current thread */
std::unique_ptr<Sampler> sampler(scene->getSampler()->clone());

for (int i=range.begin(); i<range.end(); ++i) {
/* Request an image block from the block generator */
blockGenerator.next(block);

/* Inform the sampler about the block to be rendered */
sampler->prepare(block);

/* Render all contained pixels */
renderBlock(scene, sampler.get(), block);

/* The image block has been processed. Now add it to
the "big" block that represents the entire image */
result.put(block);
}
};

/// Uncomment the following line for single threaded rendering
// map(range);

/// Default: parallel rendering
tbb::parallel_for(range, map);

cout << "done. (took " << timer.elapsedString() << ")" << endl;
});

/* Enter the application main loop */
nanogui::mainloop();

/* Shut down the user interface */
render_thread.join();

delete screen;
nanogui::shutdown();

/* Now turn the rendered image block into
a properly normalized bitmap */
std::unique_ptr<Bitmap> bitmap(result.toBitmap());

/* Determine the filename of the output bitmap */
std::string outputName = filename;
size_t lastdot = outputName.find_last_of(".");
if (lastdot != std::string::npos)
outputName.erase(lastdot, std::string::npos);
outputName += ".exr";

/* Save using the OpenEXR format */
bitmap->save(outputName);
}

void renderBlock(const Scene *scene, Sampler *sampler, ImageBlock &block) {
const Camera *camera = scene->getCamera();
const Integrator *integrator = scene->getIntegrator();

Point2i offset = block.getOffset();
Vector2i size = block.getSize();

/* Clear the block contents */
block.clear();

/* For each pixel and pixel sample sample */
for (int y=0; y<size.y(); ++y) {
for (int x=0; x<size.x(); ++x) {
for (uint32_t i=0; i<sampler->getSampleCount(); ++i) {
Point2f pixelSample = Point2f((float) (x + offset.x()), (float) (y + offset.y())) + sampler->next2D();
Point2f apertureSample = sampler->next2D();

/* Sample a ray from the camera */
Ray3f ray;
Color3f value = camera->sampleRay(ray, pixelSample, apertureSample);

/* Compute the incident radiance */
value *= integrator->Li(scene, sampler, ray);

/* Store in the image block */
block.put(pixelSample, value);
}
}
}
}

std::string toString() const {
return tfm::format("Blockwise[]");
}

private:

};

NORI_REGISTER_CLASS(Blockwise, "blockwise");
NORI_NAMESPACE_END
Loading