Field of View renderer finally working pretty good + some restructioring of code
This commit is contained in:
parent
f24011f642
commit
f5347bb07a
12 changed files with 209 additions and 122 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
32
ex4.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
124
renderer/depthoffieldrenderer.cpp
Normal file
124
renderer/depthoffieldrenderer.cpp
Normal 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;
|
||||
}
|
35
renderer/depthoffieldrenderer.h
Normal file
35
renderer/depthoffieldrenderer.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in a new issue