Merge branch 'Bloomshader'

Adding DOFRenderer and postProcessing bloom effect to master
This commit is contained in:
m.gaedke 2023-01-26 23:00:20 +01:00
commit ce1fe4dd9a
12 changed files with 372 additions and 41 deletions

View file

@ -24,10 +24,13 @@ file(GLOB primitive_src "primitive/*.cpp")
file(GLOB renderer_src "renderer/*.cpp") file(GLOB renderer_src "renderer/*.cpp")
file(GLOB scene_src "scene/*.cpp") file(GLOB scene_src "scene/*.cpp")
file(GLOB shader_src "shader/*.cpp") file(GLOB shader_src "shader/*.cpp")
file(GLOB effect_src "effect/*.cpp")
file(GLOB post_processing_src "post_processing/*.cpp")
# The tracey library # The tracey library
add_library(tracey STATIC ${common_src} ${noise_src} ${camera_src} ${light_src} add_library(tracey STATIC ${common_src} ${noise_src} ${camera_src} ${light_src}
${primitive_src} ${renderer_src} ${scene_src} ${shader_src} light/ambientlight.cpp light/ambientlight.h light/spotlight.cpp light/spotlight.h) ${primitive_src} ${renderer_src} ${scene_src} ${shader_src} ${effect_src}
${post_processing_src})
if(NOT WIN32) if(NOT WIN32)
target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES}) target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES})
endif() endif()
@ -72,6 +75,10 @@ endif()
add_executable(fancy1 fancy1.cpp) add_executable(fancy1 fancy1.cpp)
target_link_libraries(fancy1 tracey) target_link_libraries(fancy1 tracey)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")

View file

@ -12,6 +12,12 @@ public:
// Camera functions // Camera functions
virtual Ray createRay(float x, float y) const = 0; virtual Ray createRay(float x, float y) const = 0;
// Setter methods
virtual Vector3d getPosition() const = 0;
virtual Vector3d getRightDirection() const = 0;
virtual Vector3d getUpDirection() const = 0;
}; };
#endif #endif

View file

@ -13,11 +13,6 @@ public:
// Set // Set
void setPosition(Vector3d const &position) { this->position = position; } void setPosition(Vector3d const &position) { this->position = position; }
void setForwardDirection(Vector3d const &forwardDirection) { void setForwardDirection(Vector3d const &forwardDirection) {
// IMPLEMENT ME
// Set up a left-handed coordinate system,
// in which the camera looks along the positive z-Axis
// Set up a left-handed coordinate system,
// in which the camera looks along the positive z-Axis
std::tie(this->forwardDirection, this->upDirection, this->rightDirection) = orthoNormalized(forwardDirection, this->upDirection, crossProduct(this->upDirection, forwardDirection)); std::tie(this->forwardDirection, this->upDirection, this->rightDirection) = orthoNormalized(forwardDirection, this->upDirection, crossProduct(this->upDirection, forwardDirection));
} }
void setUpDirection(Vector3d const &upDirection) { void setUpDirection(Vector3d const &upDirection) {
@ -36,6 +31,12 @@ public:
// Camera functions // Camera functions
Ray createRay(float x, float y) const override; Ray createRay(float x, float y) const override;
//Getter
Vector3d getPosition() const override { return position; }
Vector3d getRightDirection() const override{ return rightDirection; }
Vector3d getUpDirection() const override { return upDirection; }
protected: protected:
Vector3d position; Vector3d position;
Vector3d forwardDirection; Vector3d forwardDirection;

View file

@ -3,6 +3,7 @@
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <utility>
Texture::Texture(int width, int height) { this->resize(width, height); } Texture::Texture(int width, int height) { this->resize(width, height); }
@ -83,3 +84,12 @@ Color Texture::color(float u, float v, bool interpolate) const {
Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const { Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const {
return color(surfacePosition.u, surfacePosition.v, interpolate); return color(surfacePosition.u, surfacePosition.v, interpolate);
} }
CImg<float> Texture::getImage() {
return image_;
}
void Texture::setTexture(CImg<float> image){
image_ = std::move(image);
}

View file

@ -9,30 +9,34 @@ using namespace cimg_library;
class Texture { class Texture {
public: public:
// Constructor // Constructor
Texture(int width, int height); Texture(int width, int height);
Texture(char const *fileName); Texture(char const *fileName);
// Image functions // Image functions
inline void resize(int width, int height) { this->image_.resize(width, height, 1, 3); } inline void resize(int width, int height) { this->image_.resize(width, height, 1, 3); }
bool load(char const *fileName); bool load(char const *fileName);
bool save(char const *fileName) const; bool save(char const *fileName) const;
// Get // Get
inline bool isNull() const { return this->image_.is_empty(); } inline bool isNull() const { return this->image_.is_empty(); }
inline int width() const { return this->image_.width(); } inline int width() const { return this->image_.width(); }
inline int height() const { return this->image_.height(); } inline int height() const { return this->image_.height(); }
Color getPixelAt(int x, int y) const; Color getPixelAt(int x, int y) const;
// Set // Set
void setPixelAt(int x, int y, Color const &color); void setPixelAt(int x, int y, Color const &color);
void setTexture(CImg<float> image);
// Color functions
Color color(float u, float v, bool interpolate = true) const; // Color functions
Color color(Vector2d const &surfacePosition, bool interpolate = true) const; Color color(float u, float v, bool interpolate = true) const;
Color color(Vector2d const &surfacePosition, bool interpolate = true) const;
CImg<float> getImage();
private: private:
CImg<float> image_; CImg<float> image_;
}; };
#endif #endif

46
ex4.cpp
View file

@ -16,6 +16,7 @@
#include "shader/mirrorshader.h" #include "shader/mirrorshader.h"
#include "shader/phongshader.h" #include "shader/phongshader.h"
#include "shader/cooktorranceshader.h" #include "shader/cooktorranceshader.h"
#include "renderer/depthoffieldrenderer.h"
#include "light/ambientlight.h" #include "light/ambientlight.h"
#include "light/pointlight.h" #include "light/pointlight.h"
@ -25,6 +26,13 @@ int main() {
// Let's create a simple scene... // Let's create a simple scene...
SimpleScene scene; SimpleScene scene;
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Add shaders // Add shaders
auto mirror = std::make_shared<MirrorShader>(); auto mirror = std::make_shared<MirrorShader>();
auto white = std::make_shared<LambertShader>(Color(0.9f, 0.9f, 0.9f)); auto white = std::make_shared<LambertShader>(Color(0.9f, 0.9f, 0.9f));
@ -32,11 +40,16 @@ int main() {
auto blue = std::make_shared<LambertShader>(Color(0.2f, 0.3f, 1.0f)); auto blue = std::make_shared<LambertShader>(Color(0.2f, 0.3f, 1.0f));
auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f); auto orange = std::make_shared<PhongShader>(Color(1.0f, 0.64f, 0.0f), 1.0f, Color(1.0f, 1.0f, 1.0f), 1.0f, 25.0f);
auto gold= std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f); auto gold= std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f);
auto blueMetallic = std::make_shared<BrdfShader>("data/blue-metallic-paint.binary", Color(7.0f, 7.0f, 7.0f)); auto blueMetallic = std::make_shared<BrdfShader>("../data/blue-metallic-paint.binary", Color(7.0f, 7.0f, 7.0f));
auto darkRed = std::make_shared<BrdfShader>("data/dark-red-paint.binary", Color(7.0f, 7.0f, 7.0f)); auto darkRed = std::make_shared<BrdfShader>("../data/dark-red-paint.binary", Color(7.0f, 7.0f, 7.0f));
// DOF Shader
bool dofShader = true;
// Set up the walls // Set up the walls
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), mirror)); scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), mirror));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), mirror)); scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), mirror));
@ -48,27 +61,34 @@ int main() {
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.0f, blueMetallic)); scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.0f, blueMetallic));
scene.add(std::make_shared<Sphere>(Vector3d(0.0f, 2.0f, 0.0f), 1.0f, orange)); scene.add(std::make_shared<Sphere>(Vector3d(0.0f, 2.0f, 0.0f), 1.0f, orange));
scene.add(std::make_shared<Sphere>(Vector3d(3.0f, 0.0f, 0.0f), 1.0f, darkRed)); scene.add(std::make_shared<Sphere>(Vector3d(3.0f, 0.0f, 0.0f), 1.0f, darkRed));
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, -3.3f, -4.0f), 1.0f, mirror));
// Add the teapot // Add the teapot
auto teapot = std::make_shared<ObjModel>(gold); auto teapot = std::make_shared<ObjModel>(gold);
teapot->loadObj("data/teapot.obj", Vector3d(3.0f, 3.0f, 3.0f), Vector3d(0.0f, -5.0f, 0.0f)); teapot->loadObj("../data/teapot.obj", Vector3d(3.0f, 3.0f, 3.0f), Vector3d(0.0f, -5.0f, 0.0f));
scene.add(teapot); scene.add(teapot);
// Add ambient light // Add ambient light
scene.add(std::make_shared<AmbientLight>(0.15f)); scene.add(std::make_shared<AmbientLight>(0.15f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, -4.0f), 15.0f)); //scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, -4.0f), 15.0f));
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 4.0f, 4.0f), 15.0f)); scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 2.5f, -4.0f), 7.0f));
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, 0.0f, -10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.0f, 1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Render the scene // Render the scene
SimpleRenderer renderer; // SimpleRenderer renderer;
renderer.renderImage(scene, camera, 512, 512).save("result.png"); // renderer.renderImage(scene, camera, 1024, 1024).save("result.png");
// initialize renderer: aperture = lens thickness, secondaryRayCount = how many rays per pixel are created
// focalLength = the area which is in focus
DOFRenderer renderer(0.2, 100, 10.0f);
// Use DOFRenderer to raytrace
Texture image = renderer.renderImage(scene, camera, 1024, 1024);
// save image
image.save("result.png");
return 0; return 0;
} }

88
post_processing/bloom.cpp Normal file
View file

@ -0,0 +1,88 @@
#include <iostream>
#include "bloom.h"
Bloom::Bloom(CImg<float> image) : image(image) {}
CImg<float> Bloom::bloom(float threshold, int kernelSize, float sigma, float intensity) {
// Apply threshold to image
//CImg<float> brightPixels = image_.get_threshold(threshold);
//brightPixels.save("brightpixels.png");
// Apply gaussian blur to bright pixels
CImg<float> kernel = computeGaussianKernel(kernelSize, sigma);
CImg<float> blurred = convolution(image, kernel);
for(int i = 0; i < 3; i++){
kernel = computeGaussianKernel(kernelSize, sigma);
blurred = convolution(image, kernel);
blurred *= intensity;
}
// Add blurred image back to original image
cimg_forXYC(image, x, y, c) {
float value = image(x,y,0,c) + blurred(x,y,0,c);
image(x,y,0,c) = (value > 1.0f) ? 1.0f : value;
}
return image;
}
void Bloom::gaussianBlur(int kernelSize, float sigma) {
CImg<float> kernel = computeGaussianKernel(kernelSize, sigma);
image = convolution(image, kernel);
}
// Function to compute Gaussian kernel
CImg<float> Bloom::computeGaussianKernel(int kernelSize, float sigma) {
// Create kernel
CImg<float> kernel(kernelSize, kernelSize, 1, 1);
// Compute Gaussian kernel
float sum = 0.0f;
int i, j;
for (i = 0; i < kernelSize; i++) {
for (j = 0; j < kernelSize; j++) {
kernel(i, j) = exp(-0.5f * (pow((i - kernelSize / 2.f) / sigma, 2.f) +
pow((j - kernelSize / 2.f) / sigma, 2.f))) / (2 * M_PI * sigma * sigma);
sum += kernel(i, j);
}
}
// Normalize kernel
kernel /= sum;
return kernel;
}
// Function to perform convolution
CImg<float> Bloom::convolution(CImg<float> &img, CImg<float> &kernel) {
int kernelSize = kernel.width();
int imgRows = img.height();
int imgCols = img.width();
CImg<float> result(imgCols, imgRows, 1, 3);
float sum;
int i, j, m, n;
int kernelRadius = kernelSize / 2;
// Perform convolution
cimg_forXYC(img, i, j, c) {
sum = 0;
cimg_forY(kernel, m) {
cimg_forX(kernel, n) {
int x = i + n - kernelRadius;
int y = j + m - kernelRadius;
if(x >= 0 && x < imgCols && y >= 0 && y < imgRows){
sum += img(x, y, 0, c) * kernel(n, m);
}
}
}
result(i, j, 0, c) = sum;
}
return result;
}
void Bloom::scaleBrightness(float scale) {
image *= scale;
}

27
post_processing/bloom.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef CG1_TRACER_BLOOM_H
#define CG1_TRACER_BLOOM_H
#include "common/texture.h"
#include "common/vector3d.h"
class Bloom {
public:
Bloom(CImg<float> image);
CImg<float> bloom(float threshold, int kernelSize, float sigma, float intensity);
private:
void scaleBrightness(float scale);
void gaussianBlur(int kernelSize, float sigma);
CImg<float> convolution(CImg<float> &img, CImg<float> &kernel);
CImg<float> computeGaussianKernel(int kernelSize, float sigma);
CImg<float> image;
};
#endif //CG1_TRACER_BLOOM_H

View file

@ -0,0 +1,124 @@
#include <iostream>
#include <thread>
#include <chrono>
#include "depthoffieldrenderer.h"
#include <iomanip>
#include "post_processing/bloom.h"
DOFRenderer::DOFRenderer(float _aperture, int _secondaryRayCount, float _focalLength) : aperture(_aperture),
numSamples(_secondaryRayCount), focalLength(_focalLength) {}
std::random_device DOFRenderer::rd;
std::mt19937 DOFRenderer::gen(DOFRenderer::rd());
Color DOFRenderer::sample(const Ray &ray, const Scene& scene, const Camera& camera) const {
std::uniform_real_distribution<float> dis(-1.0, 1.0);
// calculate the point of focus
Vector3d focusPoint = ray.origin + ray.direction * focalLength;
// Final color
Color finalColor;
// Calculate all secondary Rays
for (int i = 0; i < numSamples; i++) {
// create a random point on the aperture
Vector3d rnd = Vector3d(dis(gen), dis(gen), 0);
Vector3d apertureOffset = aperture * (rnd.x * camera.getRightDirection() + rnd.y * camera.getUpDirection());
// create the new ray with the offset point
Vector3d dofRayOrigin = ray.origin + apertureOffset;
Vector3d dofRayDirection = normalized(focusPoint - dofRayOrigin);
Ray dofRay(dofRayOrigin, dofRayDirection);
// get Color of the new Ray
finalColor += scene.traceRay(dofRay);
}
// trace the new ray and return the color
return finalColor /= float(numSamples);;
}
void DOFRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, const DOFRenderer *renderer, int width, int widthStep, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int const stepSize) {
float const aspectRatio = static_cast<float>(height) / width;
for (int y = heightOffset; y < height; y += heightStep) {
for (int x = widthOffset; x < width; x += widthStep) {
Ray ray = camera->createRay((static_cast<float>(x) / width * 2 - 1), -(static_cast<float>(y) / height * 2 - 1) * aspectRatio);
// Trace rays with DOF
image->setPixelAt(x, y, clamped(renderer->sample(ray, *scene, *camera)));
// Super hacky progress bar!
if (++*k % stepSize == 0) {
std::cout << "=" << std::flush;
}
}
}
}
Texture DOFRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height);
// Setup timer
std::chrono::steady_clock::time_point start, stop;
// Reset Ray counting
Ray::resetRayCount();
// Super-hacky progress bar!
std::cout << "(SimpleRenderer): Begin rendering..." << std::endl;
std::cout << "| 0%";
int const barSize = 50;
int const stepSize = (width * height) / barSize;
for (int i = 0; i < barSize - 3 - 5; ++i)
std::cout << " ";
std::cout << "100% |" << std::endl << "|";
std::atomic<int> k(0);
/* Start timer */ start = std::chrono::steady_clock::now();
// Spawn a thread for every logical processor -1, calling the renderThread function
int const nThreads = std::thread::hardware_concurrency();
std::vector<std::thread> threads;
for (int t = 0; t < nThreads - 1; ++t) {
threads.emplace_back(renderThread, &scene, &camera, &image, this, width, nThreads, t, height, 1, 0, &k, stepSize);
}
// Call the renderThread function yourself
renderThread(&scene, &camera, &image, this, width, nThreads, nThreads - 1, height, 1, 0, &k, stepSize);
// Rejoin the threads
for (int t = 0; t < nThreads - 1; ++t) {
threads[t].join();
}
// Stop timer
stop = std::chrono::steady_clock::now();
std::cout << "| Done!" << std::endl;
// Calculate the Time taken in seconds
double seconds = std::chrono::duration_cast<std::chrono::duration<double>>(stop - start).count();
std::cout << "Time: " << seconds << "s" << std::endl;
// Get the number of seconds per ray
int rays = Ray::getRayCount();
std::cout << "Paths: " << rays << std::endl;
std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl;
// Post-processing
// Bloom shader
image.save("original.png");
Bloom bloomEffect = Bloom(image.getImage());
image.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
return image;
}

View file

@ -0,0 +1,35 @@
#ifndef DEPTHOFFIELDSHADER_H
#define DEPTHOFFIELDSHADER_H
#include "camera/camera.h"
#include <random>
#include "renderer/renderer.h"
#include "scene/simplescene.h"
class DOFRenderer : public Renderer {
static void renderThread(const Scene *scene, const Camera *camera, Texture *image, const DOFRenderer *renderer, int width, int widthStep,
int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k,
const int stepSize);
public:
// Constructor
DOFRenderer(float _aperture, int _secondaryRayCount, float _focalLength);
~DOFRenderer() override = default;
//Render Functions
Texture renderImage(Scene const &scene, Camera const &camera, int width, int height) override;
// DOF sampler
Color sample(const Ray &ray, const Scene& scene, const Camera& camera) const;
private:
float aperture, focalLength;
int numSamples;
static std::random_device rd;
static std::mt19937 gen;
};
#endif

View file

@ -5,6 +5,7 @@
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <iomanip> #include <iomanip>
#include "post_processing/bloom.h"
void SimpleRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int const stepSize) { void SimpleRenderer::renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int const stepSize) {
float const aspectRatio = static_cast<float>(height) / width; float const aspectRatio = static_cast<float>(height) / width;
@ -73,6 +74,13 @@ Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, in
std::cout << "Paths: " << rays << std::endl; std::cout << "Paths: " << rays << std::endl;
std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl; std::cout << "Paths per second: " << std::fixed << std::setprecision(0) << rays / seconds << std::endl;
// Post-processing
// Bloom shader
image.save("original.png");
Bloom bloomEffect = Bloom(image.getImage());
image.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
return image; return image;
} }

View file

@ -3,11 +3,12 @@
#include <atomic> #include <atomic>
#include "renderer/renderer.h" #include "renderer/renderer.h"
#include <renderer/depthoffieldrenderer.h>
class SimpleRenderer : public Renderer { class SimpleRenderer : public Renderer {
static void renderThread(const Scene *scene, Camera const *camera, Texture *image, int width, int widthStep, static void renderThread(const Scene *scene, const Camera *camera, Texture *image, int width, int widthStep,
int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k, int widthOffset, int height, int heightStep, int heightOffset, std::atomic<int> *k,
int const stepSize); const int stepSize);
public: public:
// Constructor / Destructor // Constructor / Destructor