Field of View renderer finally working pretty good + some restructioring of code

This commit is contained in:
m.gaedke 2023-01-26 22:56:23 +01:00
parent f24011f642
commit f5347bb07a
12 changed files with 209 additions and 122 deletions

View file

@ -24,11 +24,13 @@ file(GLOB primitive_src "primitive/*.cpp")
file(GLOB renderer_src "renderer/*.cpp")
file(GLOB scene_src "scene/*.cpp")
file(GLOB shader_src "shader/*.cpp")
file(GLOB effect_src "effect/*.cpp")
file(GLOB post_processing_src "post_processing/*.cpp")
# The tracey library
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 post-processing/bloomshader.cpp post-processing/bloomshader.h shader/depthoffieldshader.cpp shader/depthoffieldshader.h)
${primitive_src} ${renderer_src} ${scene_src} ${shader_src} ${effect_src}
${post_processing_src})
if(NOT WIN32)
target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES})
endif()

View file

@ -12,6 +12,12 @@ public:
// Camera functions
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

View file

@ -13,11 +13,6 @@ public:
// Set
void setPosition(Vector3d const &position) { this->position = position; }
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));
}
void setUpDirection(Vector3d const &upDirection) {
@ -37,9 +32,9 @@ public:
Ray createRay(float x, float y) const override;
//Getter
Vector3d getPosition() const { return position; }
Vector3d getRightDirection() const { return rightDirection; }
Vector3d getUpDirection() const { return upDirection; }
Vector3d getPosition() const override { return position; }
Vector3d getRightDirection() const override{ return rightDirection; }
Vector3d getUpDirection() const override { return upDirection; }
protected:

32
ex4.cpp
View file

@ -16,7 +16,7 @@
#include "shader/mirrorshader.h"
#include "shader/phongshader.h"
#include "shader/cooktorranceshader.h"
#include "shader/depthoffieldshader.h"
#include "renderer/depthoffieldrenderer.h"
#include "light/ambientlight.h"
#include "light/pointlight.h"
@ -43,8 +43,8 @@ int main() {
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 dofShader = std::make_shared<DOFShader>(scene, Color(0.9f, 0.9f, 0.9f), 100, 0.1f, 10.0f, 50.0f, camera);
// DOF Shader
bool dofShader = true;
// Set up the walls
@ -55,20 +55,13 @@ int main() {
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, +5.0f, 0.0f), Vector3d(0.0f, -1.0f, 0.0f), white));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, -5.0f, 0.0f), Vector3d(0.0f, +1.0f, 0.0f), white));
scene.add(std::make_shared<InfinitePlane>(Vector3d(+5.0f, 0.0f, 0.0f), Vector3d(-1.0f, 0.0f, 0.0f), dofShader));
scene.add(std::make_shared<InfinitePlane>(Vector3d(+5.0f, 0.0f, 0.0f), Vector3d(-1.0f, 0.0f, 0.0f), blue));
scene.add(std::make_shared<InfinitePlane>(Vector3d(-5.0f, 0.0f, 0.0f), Vector3d(+1.0f, 0.0f, 0.0f), red));
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(3.0f, 0.0f, 0.0f), 1.0f, darkRed));
// Create a DOFShader instance with the desired aperture size, focal distance, and focal length
// Use the DOFShader instance as the shader for an object in the scene
auto sphere = std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.0f, dofShader);
// Add the sphere to the scene
scene.add(sphere);
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, -3.3f, -4.0f), 1.0f, mirror));
// Add the teapot
auto teapot = std::make_shared<ObjModel>(gold);
@ -83,8 +76,19 @@ int main() {
// Render the scene
SimpleRenderer renderer;
renderer.renderImage(scene, camera, 512, 512).save("result.png");
// SimpleRenderer renderer;
// 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;
}

View file

@ -1,11 +1,11 @@
#include <iostream>
#include "bloomshader.h"
#include "bloom.h"
Bloomshader::Bloomshader(CImg<float> image) : image(image) {}
Bloom::Bloom(CImg<float> image) : image(image) {}
CImg<float> Bloomshader::bloom(float threshold, int kernelSize, float sigma, float intensity) {
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");
@ -28,13 +28,13 @@ CImg<float> Bloomshader::bloom(float threshold, int kernelSize, float sigma, flo
return image;
}
void Bloomshader::gaussianBlur(int kernelSize, float sigma) {
void Bloom::gaussianBlur(int kernelSize, float sigma) {
CImg<float> kernel = computeGaussianKernel(kernelSize, sigma);
image = convolution(image, kernel);
}
// Function to compute Gaussian kernel
CImg<float> Bloomshader::computeGaussianKernel(int kernelSize, float sigma) {
CImg<float> Bloom::computeGaussianKernel(int kernelSize, float sigma) {
// Create kernel
CImg<float> kernel(kernelSize, kernelSize, 1, 1);
@ -56,7 +56,7 @@ CImg<float> Bloomshader::computeGaussianKernel(int kernelSize, float sigma) {
}
// Function to perform convolution
CImg<float> Bloomshader::convolution(CImg<float> &img, CImg<float> &kernel) {
CImg<float> Bloom::convolution(CImg<float> &img, CImg<float> &kernel) {
int kernelSize = kernel.width();
int imgRows = img.height();
int imgCols = img.width();
@ -83,6 +83,6 @@ CImg<float> Bloomshader::convolution(CImg<float> &img, CImg<float> &kernel) {
return result;
}
void Bloomshader::scaleBrightness(float scale) {
void Bloom::scaleBrightness(float scale) {
image *= scale;
}

View file

@ -1,14 +1,14 @@
#ifndef CG1_TRACER_BLOOMSHADER_H
#define CG1_TRACER_BLOOMSHADER_H
#ifndef CG1_TRACER_BLOOM_H
#define CG1_TRACER_BLOOM_H
#include "common/texture.h"
#include "common/vector3d.h"
class Bloomshader {
class Bloom {
public:
Bloomshader(CImg<float> image);
Bloom(CImg<float> image);
CImg<float> bloom(float threshold, int kernelSize, float sigma, float intensity);
private:
@ -24,4 +24,4 @@ private:
};
#endif //CG1_TRACER_BLOOMSHADER_H
#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,7 +5,7 @@
#include <thread>
#include <chrono>
#include <iomanip>
#include "post-processing/bloomshader.h"
#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) {
float const aspectRatio = static_cast<float>(height) / width;
@ -79,7 +79,7 @@ Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, in
image.save("original.png");
Bloomshader bloomEffect = Bloomshader(image.getImage());
Bloom bloomEffect = Bloom(image.getImage());
image.setTexture(bloomEffect.bloom(0.55f, 5, 10.0f, 0.06f));
return image;

View file

@ -3,11 +3,12 @@
#include <atomic>
#include "renderer/renderer.h"
#include <renderer/depthoffieldrenderer.h>
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 const stepSize);
const int stepSize);
public:
// Constructor / Destructor

View file

@ -1,47 +0,0 @@
#include <iostream>
#include "depthoffieldshader.h"
DOFShader::DOFShader(SimpleScene& _scene, Color const &_diffuseColor, int _numSamples, float _aperture, float _focalDistance, float _focalLength, PerspectiveCamera& _camera) : scene(_scene), diffuseColor(_diffuseColor), numSamples(_numSamples), aperture(_aperture),
focalDistance(_focalDistance), focalLength(_focalLength), camera(_camera) {}
std::random_device DOFShader::rd;
std::mt19937 DOFShader::gen(DOFShader::rd());
Color DOFShader::shade(Scene const &scene, Ray const &ray) const {
Color color;
// Accumulate the light over all light sources
for (const auto &light : scene.lights()) {
Light::Illumination const illum = light->illuminate(scene, ray);
Color const diffuse = this->diffuseColor * std::max(dotProduct(-illum.direction, ray.normal), 0.0f);
// Add the sample color to the final color
color += diffuse * sample(ray, illum);
}
return color;
}
Color DOFShader::sample(Ray ray, Light::Illumination const &illum) const {
std::uniform_real_distribution<float> dis(-1.0, 1.0);
// calculate the point of focus
Vector3d focusPoint = ray.origin + ray.direction * focalDistance;
// create a random point on the aperture
Vector3d rnd = Vector3d(dis(gen), dis(gen), 0);
Vector3d offset = aperture * (rnd.x * camera.getRightDirection() + rnd.y * camera.getUpDirection());
// create the new ray with the offset point
Vector3d dofRayStart = camera.getPosition() + offset;
Vector3d dofRayDir = normalized(focusPoint - (camera.getPosition() + offset));
Ray dofRay = Ray(dofRayStart, dofRayDir);
// trace the new ray and sample the color
Color tracedColor = scene.traceRay(dofRay);
return tracedColor;
}

View file

@ -1,33 +0,0 @@
#ifndef DEPTHOFFIELDSHADER_H
#define DEPTHOFFIELDSHADER_H
#include "shader/shader.h"
#include "light/light.h"
#include "camera/perspectivecamera.h"
#include <random>
#include "scene/simplescene.h"
class DOFShader : public Shader {
public:
// Constructor / Desctructor
DOFShader(SimpleScene& _scene, Color const &_diffuseColor, int _numSamples, float _aperture, float _focalDistance, float _focalLength, PerspectiveCamera& _camera);
virtual ~DOFShader() = default;
// Shader functions
virtual Color shade(Scene const &scene, Ray const &ray) const;
private:
float aperture, focalDistance, focalLength;
int numSamples;
PerspectiveCamera& camera;
SimpleScene& scene;
Color diffuseColor;
static std::random_device rd;
static std::mt19937 gen;
Color sample(Ray ray, Light::Illumination const &illum) const;
};
#endif