Merge remote-tracking branch 'base/master'
This commit is contained in:
commit
21d82ec5e2
11 changed files with 413 additions and 211 deletions
|
@ -62,6 +62,12 @@ target_link_libraries(tracey_ex4 tracey)
|
|||
add_executable(tracey_ex5 ex5.cpp)
|
||||
target_link_libraries(tracey_ex5 tracey)
|
||||
|
||||
add_executable(tracey_ex6 ex6.cpp)
|
||||
target_link_libraries(tracey_ex6 tracey)
|
||||
if("${CMAKE_CURRENT_LIST_DIR}/renderer/superrenderer.cpp" IN_LIST renderer_src)
|
||||
add_definitions(-DSUPERRENDERER_FOUND)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
#include "ray.h"
|
||||
|
||||
std::atomic_int Ray::rayCount = 0;
|
||||
std::atomic<int> Ray::rayCount(0);
|
||||
|
|
|
@ -42,7 +42,7 @@ private:
|
|||
int remainingBounces = ICG_RAY_BOUNCES;
|
||||
#endif
|
||||
|
||||
static std::atomic_int rayCount;
|
||||
static std::atomic<int> rayCount;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -55,7 +55,7 @@ Color Texture::color(float u, float v, bool interpolate) const {
|
|||
if (!interpolate) {
|
||||
color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height())));
|
||||
} else {
|
||||
// We will use bilinear filtering in the future, but for now we do a simple color look-up
|
||||
// IMPLEMENT bilinear interpolation
|
||||
color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height())));
|
||||
}
|
||||
return color;
|
||||
|
|
89
ex6.cpp
Normal file
89
ex6.cpp
Normal file
|
@ -0,0 +1,89 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "camera/perspectivecamera.h"
|
||||
|
||||
#include "scene/fastscene.h"
|
||||
|
||||
#include "primitive/sphere.h"
|
||||
|
||||
#include "renderer/simplerenderer.h"
|
||||
#ifdef SUPERRENDERER_FOUND
|
||||
#include "renderer/superrenderer.h"
|
||||
#endif
|
||||
|
||||
#include "shader/materialshader.h"
|
||||
|
||||
#include "light/ambientlight.h"
|
||||
#include "light/pointlight.h"
|
||||
#include "light/spotlight.h"
|
||||
|
||||
int main() {
|
||||
FastScene scene;
|
||||
scene.setEnvironmentMap(std::make_shared<Texture>("data/TychoSkymapII.t5_04096x02048.png"));
|
||||
|
||||
// Set up the camera
|
||||
PerspectiveCamera camera;
|
||||
camera.setPosition(Vector3d(0.0f, -0.2f, 0.0f));
|
||||
camera.setForwardDirection(normalized(Vector3d(-1.0f, -0.15f, 0.6f)));
|
||||
camera.setUpDirection(normalized(Vector3d(0.0f, -1.0f, 0.0f)));
|
||||
camera.setFovAngle(90.0f);
|
||||
|
||||
// Add the earth
|
||||
auto earthDiffuse = std::make_shared<Texture>("data/earth_diffuse.ppm");
|
||||
auto earthSpecular = std::make_shared<Texture>("data/earth_specular.ppm");
|
||||
auto earthNormal = std::make_shared<Texture>("data/earth_normal.ppm");
|
||||
auto earthClouds = std::make_shared<Texture>("data/earth_clouds.ppm");
|
||||
auto earthCloudShader = std::make_shared<MaterialShader>();
|
||||
earthCloudShader->setDiffuseMap(earthClouds);
|
||||
earthCloudShader->setAlphaMap(earthClouds);
|
||||
earthCloudShader->setDiffuseCoefficient(0.9f);
|
||||
earthCloudShader->setSpecularCoefficient(0.1f);
|
||||
auto earthShader = std::make_shared<MaterialShader>();
|
||||
earthShader->setDiffuseMap(earthDiffuse);
|
||||
earthShader->setDiffuseCoefficient(0.5f);
|
||||
earthShader->setSpecularMap(earthSpecular);
|
||||
earthShader->setSpecularCoefficient(0.5f);
|
||||
earthShader->setShininessExponent(15.0f);
|
||||
earthShader->setNormalMap(earthNormal);
|
||||
scene.add(std::make_shared<Sphere>(Vector3d(-50.0f, 0.0f, 60.0f), 20.0f, earthShader));
|
||||
scene.add(std::make_shared<Sphere>(Vector3d(-50.0f, 0.0f, 60.0f), 20.05f, earthCloudShader));
|
||||
|
||||
// Add the spaceship
|
||||
auto spaceshipDiffuse = std::make_shared<Texture>("data/space_frigate_6_diffuse.ppm");
|
||||
auto spaceshipSpecular = std::make_shared<Texture>("data/space_frigate_6_specular.ppm");
|
||||
auto spaceshipNormal = std::make_shared<Texture>("data/space_frigate_6_normal.ppm");
|
||||
auto spaceshipReflection = std::make_shared<Texture>("data/space_frigate_6_specular.ppm");
|
||||
auto spaceshipShader = std::make_shared<MaterialShader>();
|
||||
spaceshipShader->setDiffuseMap(spaceshipDiffuse);
|
||||
spaceshipShader->setDiffuseCoefficient(0.75f);
|
||||
spaceshipShader->setSpecularMap(spaceshipSpecular);
|
||||
spaceshipShader->setNormalMap(spaceshipNormal);
|
||||
spaceshipShader->setNormalCoefficient(0.8f);
|
||||
spaceshipShader->setSpecularCoefficient(0.25f);
|
||||
spaceshipShader->setShininessExponent(30.0f);
|
||||
spaceshipShader->setReflectionMap(spaceshipReflection);
|
||||
spaceshipShader->setReflectance(0.75f);
|
||||
|
||||
scene.addObj("data/space_frigate_6.obj", Vector3d(-1.0f, 1.0f, 1.0f) / 20.0f, Vector3d(-1.3f, -0.3f, 0.7f),
|
||||
spaceshipShader);
|
||||
|
||||
// Add lights
|
||||
scene.add(std::make_shared<PointLight>(Vector3d(0.0f, 0.0f, 30.0f), 1000.0f));
|
||||
scene.add(std::make_shared<AmbientLight>(0.3f));
|
||||
|
||||
// build the tree
|
||||
scene.buildTree();
|
||||
|
||||
// Render the scene
|
||||
SimpleRenderer renderer;
|
||||
renderer.renderImage(scene, camera, 1024, 768).save("result.png");
|
||||
|
||||
#ifdef SUPERRENDERER_FOUND
|
||||
SuperRenderer sr;
|
||||
sr.setSuperSamplingFactor(4);
|
||||
sr.renderImage(scene, camera, 1024, 768).save("result_super.png");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -10,13 +10,34 @@ Texture KDTreeRenderer::renderImage(Scene const &scene, Camera const &camera, in
|
|||
}
|
||||
|
||||
Texture KDTreeRenderer::renderKDTree(FastScene const &scene, Camera const &camera, int width, int height) {
|
||||
const Color RED = {1, 0, 0};
|
||||
const Color GREEN = {0, 1, 0};
|
||||
Texture image(width, height);
|
||||
float const aspectRatio = static_cast<float>(height) / width;
|
||||
|
||||
// IMPLEMENT ME
|
||||
std::vector<std::vector<int>> intersections;
|
||||
intersections.reserve(width);
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
Ray ray = camera.createRay((static_cast<float>(x) / width * 2 - 1),
|
||||
-(static_cast<float>(y) / height * 2 - 1) * aspectRatio);
|
||||
|
||||
image.setPixelAt(x, y, Color(float(scene.countNodeIntersections(ray)), 0.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
// map color to green -> red map
|
||||
float maxVal = -INFINITY;
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
maxVal = std::max(maxVal, image.getPixelAt(x, y).r);
|
||||
}
|
||||
}
|
||||
|
||||
Color r(1, 0, 0);
|
||||
Color g(0, 1, 0);
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
float factor = image.getPixelAt(x, y).r / maxVal;
|
||||
image.setPixelAt(x, y, (1.0f - factor) * g + factor * r);
|
||||
}
|
||||
}
|
||||
|
||||
int max = 0;
|
||||
float const aspectRatio = static_cast<float>(height) / static_cast<float>(width);
|
||||
|
|
|
@ -2,10 +2,26 @@
|
|||
#include "renderer/simplerenderer.h"
|
||||
#include "scene/scene.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <thread>
|
||||
|
||||
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;
|
||||
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);
|
||||
image->setPixelAt(x, y, clamped(scene->traceRay(ray)));
|
||||
|
||||
// Super hacky progress bar!
|
||||
if (++*k % stepSize == 0) {
|
||||
std::cout << "=" << std::flush;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
|
||||
Texture image(width, height);
|
||||
|
||||
|
@ -23,29 +39,24 @@ Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, in
|
|||
for (int i = 0; i < width / stepSize - 3 - 5; ++i)
|
||||
std::cout << " ";
|
||||
std::cout << "100% |" << std::endl << "|";
|
||||
int k = 0;
|
||||
std::atomic<int> k(0);
|
||||
|
||||
// Start timer
|
||||
start = std::chrono::steady_clock::now();
|
||||
|
||||
float const aspectRatio = static_cast<float>(height) / static_cast<float>(width);
|
||||
// Spawn a thread for every logical processor -1, calling the renderThread function
|
||||
int const nThreads = std::thread::hardware_concurrency();
|
||||
std::vector<std::thread> threads;
|
||||
threads.reserve(width);
|
||||
for (int x = 0; x < image.width(); ++x) {
|
||||
threads.emplace_back([&image, aspectRatio](auto camera, auto scene, int x) {
|
||||
for (int y = 0; y < image.height(); ++y) {
|
||||
Ray ray = camera->createRay((static_cast<float>(x) / image.width() * 2.0f - 1),
|
||||
-(static_cast<float>(y) / image.height() * 2 - 1) * aspectRatio);
|
||||
image.setPixelAt(x, y, clamped(scene->traceRay(ray)));
|
||||
}
|
||||
}, &camera, &scene, x);
|
||||
}
|
||||
for (auto &thread: threads) {
|
||||
thread.join();
|
||||
// Super hacky progress bar!
|
||||
if (++k % stepSize == 0) {
|
||||
std::cout << "=" << std::flush;
|
||||
for (int t = 0; t < nThreads - 1; ++t) {
|
||||
threads.emplace_back(renderThread, &scene, &camera, &image, width, nThreads, t, height, 1, 0, &k, stepSize);
|
||||
}
|
||||
|
||||
// Call the renderThread function yourself
|
||||
renderThread(&scene, &camera, &image, 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
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
#ifndef SIMPLERENDERER_H
|
||||
#define SIMPLERENDERER_H
|
||||
|
||||
#include <atomic>
|
||||
#include "renderer/renderer.h"
|
||||
|
||||
class SimpleRenderer : public Renderer {
|
||||
static void 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);
|
||||
|
||||
public:
|
||||
// Constructor / Destructor
|
||||
|
|
|
@ -6,224 +6,218 @@
|
|||
#include <numeric>
|
||||
|
||||
int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const {
|
||||
// IMPLEMENT ME
|
||||
// If this is a leaf node, we return 0
|
||||
if (isLeaf()) {
|
||||
return 0;
|
||||
}
|
||||
float t_split;
|
||||
if (ray.direction[this->dimension] == 0) {
|
||||
t_split = INFINITY;
|
||||
} else {
|
||||
t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
}
|
||||
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
|
||||
int farChild = nearChild == 0 ? 1 : 0;
|
||||
// Determine the order in which we intersect the child nodes
|
||||
float const d = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
int front = ray.direction[this->dimension] < 0 ? 1 : 0;
|
||||
int back = 1 - front;
|
||||
|
||||
if (t_split < 0 || t_split > t1) {
|
||||
return 0 + this->child[nearChild]->countNodeIntersections(ray, t0, t1);
|
||||
} else if (t_split < t0) {
|
||||
return 0 + this->child[farChild]->countNodeIntersections(ray, t0, t1);
|
||||
if (d <= t0 || d < 0) {
|
||||
// t0..t1 is totally behind d, only go through the back node.
|
||||
return this->child[back]->countNodeIntersections(ray, t0, t1);
|
||||
} else if (d >= t1) {
|
||||
// t0..t1 is totally in front of d, only go to front node.
|
||||
return this->child[front]->countNodeIntersections(ray, t0, t1);
|
||||
} else {
|
||||
return 1 + this->child[nearChild]->countNodeIntersections(ray, t0, t_split) +
|
||||
this->child[farChild]->countNodeIntersections(ray, t_split, t1);
|
||||
// Traverse *both* children. Front node first, back node last.
|
||||
// Be sure to get even triangles which are coincident with the splitting plane
|
||||
return 1 + this->child[front]->countNodeIntersections(ray, t0, d + SPLT_EPS) + this->child[back]->countNodeIntersections(ray, d - SPLT_EPS, t1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::findIntersection(Ray &ray, float t0, float t1) const {
|
||||
|
||||
// IMPLEMENT ME
|
||||
// If this is a leaf node, we intersect with all the primitives...
|
||||
if (isLeaf()) {
|
||||
bool found = false;
|
||||
for (const auto &prim: this->primitives) {
|
||||
found |= prim->intersect(ray);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
// ... otherwise we continue through the branches
|
||||
bool hit = false;
|
||||
for (const auto &primitive : this->primitives)
|
||||
hit |= primitive->intersect(ray);
|
||||
return hit;
|
||||
} else { // ... otherwise we continue through the branches
|
||||
|
||||
// Determine the order in which we intersect the child nodes
|
||||
// Traverse the necessary children
|
||||
float t_split;
|
||||
if (ray.direction[this->dimension] == 0) {
|
||||
t_split = INFINITY;
|
||||
float const d = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
int front = ray.direction[this->dimension] < 0 ? 1 : 0;
|
||||
int back = 1 - front;
|
||||
|
||||
if (d <= t0 || d < 0) {
|
||||
// t0..t1 is totally behind d, only go through the back node.
|
||||
return this->child[back]->findIntersection(ray, t0, t1);
|
||||
} else if (d >= t1) {
|
||||
// t0..t1 is totally in front of d, only go to front node.
|
||||
return this->child[front]->findIntersection(ray, t0, t1);
|
||||
} else {
|
||||
t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
// Traverse *both* children. Front node first and then back node, if no hit in front of splitting plane.
|
||||
// for whatever reason, this doesn't work in the fireplace scene?!
|
||||
return ((this->child[front]->findIntersection(ray, t0 - SPLT_EPS, d + SPLT_EPS) && ray.length <= d + SPLT_EPS) || this->child[back]->findIntersection(ray, d - SPLT_EPS, t1 + SPLT_EPS));
|
||||
// return (this->child[front]->findIntersection(ray, t0 - SPLT_EPS, d + SPLT_EPS) | this->child[back]->findIntersection(ray, d - SPLT_EPS, t1 + SPLT_EPS));
|
||||
}
|
||||
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
|
||||
int farChild = nearChild == 0 ? 1 : 0;
|
||||
if (t_split < 0 || t_split > t1) {
|
||||
return this->child[nearChild]->findIntersection(ray, t0, t1);
|
||||
} else if (t_split < t0) {
|
||||
return this->child[farChild]->findIntersection(ray, t0, t1);
|
||||
} else {
|
||||
this->child[nearChild]->findIntersection(ray, t0, t_split);
|
||||
if (ray.length < t_split) {
|
||||
return true;
|
||||
}
|
||||
return this->child[farChild]->findIntersection(ray, t_split, t1);
|
||||
}
|
||||
}
|
||||
|
||||
bool Node::findOcclusion(Ray &ray, float t0, float t1) const {
|
||||
// If this is a leaf node, we intersect with all the primitives...
|
||||
if (isLeaf()) {
|
||||
for (const auto &prim: this->primitives) {
|
||||
if (prim->intersect(ray)) {
|
||||
float const rayLength = ray.length;
|
||||
for (const auto &primitive : this->primitives)
|
||||
// since this is correct, but terribly slow:...
|
||||
// if (!primitive->shader()->isTransparent() && primitive->intersect(ray))
|
||||
// return true;
|
||||
// we do it this way:
|
||||
if (primitive->intersect(ray)) {
|
||||
if (!primitive->shader()->isTransparent())
|
||||
return true;
|
||||
}
|
||||
else
|
||||
ray.length = rayLength;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
float t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
|
||||
int farChild = nearChild == 0 ? 1 : 0;
|
||||
if (t_split < 0 || t_split > t1) {
|
||||
return this->child[nearChild]->findIntersection(ray, t0, t1);
|
||||
} else if (t_split < t0) {
|
||||
return this->child[farChild]->findIntersection(ray, t0, t1);
|
||||
} else { // ... otherwise we continue through the branches
|
||||
|
||||
// Determine the order in which we intersect the child nodes
|
||||
float const d = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
|
||||
int front = ray.direction[this->dimension] < 0 ? 1 : 0;
|
||||
int back = 1 - front;
|
||||
|
||||
if (d <= t0 || d < 0) {
|
||||
// t0..t1 is totally behind d, only go through the back node.
|
||||
return this->child[back]->findOcclusion(ray, t0, t1);
|
||||
} else if (d >= t1) {
|
||||
// t0..t1 is totally in front of d, only go to front node.
|
||||
return this->child[front]->findOcclusion(ray, t0, t1);
|
||||
} else {
|
||||
this->child[nearChild]->findIntersection(ray, t0, t_split);
|
||||
if (ray.length < t_split) {
|
||||
return true;
|
||||
// Traverse *both* children. Front node first and then back node, if no hit in front of splitting plane.
|
||||
// for whatever reason, this doesn't work in the fireplace scene?!
|
||||
return ((this->child[front]->findOcclusion(ray, t0, d + SPLT_EPS) && ray.length <= d + SPLT_EPS) || this->child[back]->findOcclusion(ray, d - SPLT_EPS, t1));
|
||||
// return (this->child[front]->findOcclusion(ray, t0, d + SPLT_EPS) | this->child[back]->findOcclusion(ray, d - SPLT_EPS, t1));
|
||||
}
|
||||
return this->child[farChild]->findIntersection(ray, t_split, t1);
|
||||
}
|
||||
}
|
||||
|
||||
int FastScene::countNodeIntersections(const Ray &ray) const {
|
||||
return root->countNodeIntersections(ray, 0, INFINITY);
|
||||
// Make sure the tree is set up
|
||||
if (!this->root)
|
||||
return false;
|
||||
|
||||
// Bounding box intersection
|
||||
Vector3d const min = componentQuotient(this->absoluteMinimum - ray.origin, ray.direction);
|
||||
Vector3d const max = componentQuotient(this->absoluteMaximum - ray.origin, ray.direction);
|
||||
float const tMin = std::max(std::max(std::min(min.x, max.x), std::min(min.y, max.y)), std::min(min.z, max.z));
|
||||
float const tMax = std::min(std::min(std::max(min.x, max.x), std::max(min.y, max.y)), std::max(min.z, max.z));
|
||||
|
||||
// Traverse the tree recursively
|
||||
if (0 <= tMax && tMin <= tMax)
|
||||
return this->root->countNodeIntersections(ray, tMin, tMax);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FastScene::findIntersection(Ray &ray) const {
|
||||
Vector3d distMin = (this->absoluteMinimum - ray.origin) / ray.direction;
|
||||
Vector3d distMax = (this->absoluteMaximum - ray.origin) / ray.direction;
|
||||
float t0 = std::min(distMin.x, std::min(distMin.y, distMin.z));
|
||||
float t1 = std::max(distMax.x, std::max(distMax.y, distMax.z));
|
||||
if (t0 > t1) std::swap(t0, t1);
|
||||
return root->findIntersection(ray, t0, t1);
|
||||
// Make sure the tree is set up
|
||||
if (!this->root)
|
||||
return false;
|
||||
|
||||
// Bounding box intersection
|
||||
Vector3d const min = componentQuotient(this->absoluteMinimum - ray.origin, ray.direction);
|
||||
Vector3d const max = componentQuotient(this->absoluteMaximum - ray.origin, ray.direction);
|
||||
float const tMin = std::max(std::max(std::min(min.x, max.x), std::min(min.y, max.y)), std::min(min.z, max.z));
|
||||
float const tMax = std::min(std::min(std::max(min.x, max.x), std::max(min.y, max.y)), std::max(min.z, max.z));
|
||||
|
||||
// Traverse the tree recursively
|
||||
if (0 <= tMax && tMin <= tMax)
|
||||
return this->root->findIntersection(ray, tMin, tMax);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FastScene::findOcclusion(Ray &ray) const {
|
||||
return root->findOcclusion(ray, 0, ray.length);
|
||||
// Make sure the tree is set up
|
||||
if (!this->root)
|
||||
return false;
|
||||
|
||||
// Bounding box intersection
|
||||
Vector3d const min = componentQuotient(this->absoluteMinimum - ray.origin, ray.direction);
|
||||
Vector3d const max = componentQuotient(this->absoluteMaximum - ray.origin, ray.direction);
|
||||
float const tMin = std::max(std::max(std::min(min.x, max.x), std::min(min.y, max.y)), std::min(min.z, max.z));
|
||||
float const tMax = std::min(std::min(std::max(min.x, max.x), std::max(min.y, max.y)), std::max(min.z, max.z));
|
||||
|
||||
// Traverse the tree recursively
|
||||
if (0 <= tMax && tMin <= tMax)
|
||||
return this->root->findOcclusion(ray, tMin, tMax);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) {
|
||||
// IMPLEMENT ME
|
||||
// Set the new depth and number of primitives
|
||||
this->maximumDepth = maximumDepth;
|
||||
this->minimumNumberOfPrimitives = minimumNumberOfPrimitives;
|
||||
|
||||
// Determine the bounding box of the kD-Tree
|
||||
this->absoluteMinimum = Vector3d(+INFINITY, +INFINITY, +INFINITY);
|
||||
this->absoluteMaximum = Vector3d(-INFINITY, -INFINITY, -INFINITY);
|
||||
for (const auto &primitive : this->primitives()) {
|
||||
for (int d = 0; d < 3; ++d) {
|
||||
this->absoluteMinimum[d] = std::min(this->absoluteMinimum[d], primitive->minimumBounds(d));
|
||||
this->absoluteMaximum[d] = std::max(this->absoluteMaximum[d], primitive->maximumBounds(d));
|
||||
}
|
||||
}
|
||||
|
||||
this->absoluteMaximum = {
|
||||
std::max_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->maximumBounds(0) < prim2->maximumBounds(0);
|
||||
})->get()->maximumBounds(0),
|
||||
std::max_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->maximumBounds(1) < prim2->maximumBounds(1);
|
||||
})->get()->maximumBounds(1),
|
||||
std::max_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->maximumBounds(2) < prim2->maximumBounds(2);
|
||||
})->get()->maximumBounds(2)
|
||||
};
|
||||
this->absoluteMinimum = {
|
||||
std::min_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->minimumBounds(0) < prim2->minimumBounds(0);
|
||||
})->get()->minimumBounds(0),
|
||||
std::min_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->minimumBounds(1) < prim2->minimumBounds(1);
|
||||
})->get()->minimumBounds(1),
|
||||
std::min_element(this->primitives().begin(),
|
||||
this->primitives().end(),
|
||||
[](const auto &prim1, const auto &prim2) {
|
||||
return prim1->minimumBounds(2) < prim2->minimumBounds(2);
|
||||
})->get()->minimumBounds(2)
|
||||
};
|
||||
this->root = build(this->absoluteMinimum, this->absoluteMaximum, this->primitives(), 0);
|
||||
// Recursively build the kD-Tree
|
||||
root = this->build(this->absoluteMinimum, this->absoluteMaximum, this->primitives(), 0);
|
||||
std::cout << "(FastScene): " << this->primitives().size() << " primitives organized into tree" << std::endl;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d const &maximumBounds,
|
||||
const std::vector<std::shared_ptr<Primitive>> &primitives, int depth) {
|
||||
|
||||
std::unique_ptr<Node> node = std::make_unique<Node>();
|
||||
// IMPLEMENT ME
|
||||
std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d const &maximumBounds, const std::vector<std::shared_ptr<Primitive>> &primitives, int depth) {
|
||||
// Determine the diameter of the bounding box
|
||||
float diameter_x = maximumBounds.x - minimumBounds.x;
|
||||
float diameter_y = maximumBounds.y - minimumBounds.y;
|
||||
float diameter_z = maximumBounds.z - minimumBounds.z;
|
||||
Vector3d const diameter = maximumBounds - minimumBounds;
|
||||
|
||||
// Test whether we have reached a leaf node...
|
||||
if (++depth == maximumDepth) {
|
||||
node->primitives = primitives;
|
||||
return node;
|
||||
int minimumDimension = ((diameter.x < diameter.y) ? ((diameter.x < diameter.z) ? 0 : 2) : ((diameter.y < diameter.z) ? 1 : 2));
|
||||
if (depth >= this->maximumDepth || (int)primitives.size() <= this->minimumNumberOfPrimitives || (diameter[minimumDimension]) <= EPSILON) {
|
||||
auto leafNode = std::make_unique<Node>();
|
||||
leafNode->primitives = primitives;
|
||||
return leafNode;
|
||||
}
|
||||
|
||||
// ... otherwise create a new inner node by splitting through the widest
|
||||
// dimension
|
||||
if (diameter_x >= diameter_y) {
|
||||
if (diameter_x >= diameter_z) {
|
||||
node->dimension = 0;
|
||||
} else {
|
||||
node->dimension = 2;
|
||||
}
|
||||
} else {
|
||||
if (diameter_y > diameter_z) {
|
||||
node->dimension = 1;
|
||||
} else {
|
||||
node->dimension = 2;
|
||||
}
|
||||
}
|
||||
// ... otherwise create a new inner node by splitting through the widest dimension
|
||||
auto node = std::make_unique<Node>();
|
||||
node->dimension = ((diameter.x > diameter.y) ? ((diameter.x > diameter.z) ? 0 : 2) : ((diameter.y > diameter.z) ? 1 : 2));
|
||||
|
||||
// Determine the split position
|
||||
// Note: Use the median of the minimum bounds of the primitives
|
||||
std::vector<std::shared_ptr<Primitive>> copy(primitives.size());
|
||||
std::partial_sort_copy(primitives.begin(), primitives.end(), copy.begin(), copy.end(),
|
||||
[&node](const auto &prim1, const auto &prim2) {
|
||||
return prim1->minimumBounds(node->dimension) <
|
||||
prim2->minimumBounds(node->dimension);
|
||||
});
|
||||
node->split = copy.at(copy.size() / 2)->minimumBounds(node->dimension);
|
||||
|
||||
std::vector<float> minimumValues;
|
||||
for (const auto &primitive : primitives)
|
||||
minimumValues.push_back(primitive->minimumBounds(node->dimension));
|
||||
std::sort(minimumValues.begin(), minimumValues.end());
|
||||
node->split = minimumValues[minimumValues.size() / 2];
|
||||
|
||||
// Divide primitives into the left and right lists
|
||||
// Remember: A primitive can be in both lists!
|
||||
// Also remember: You split exactly at the minimum of a primitive,
|
||||
// make sure *that* primitive does *not* appear in both lists!
|
||||
|
||||
std::vector<std::shared_ptr<Primitive>> left{};
|
||||
std::vector<std::shared_ptr<Primitive>> right{};
|
||||
for (auto &prim: primitives) {
|
||||
if (prim->minimumBounds(node->dimension) > node->split) {
|
||||
right.push_back(prim);
|
||||
} else {
|
||||
left.push_back(prim);
|
||||
if (prim->maximumBounds(node->dimension) > node->split) {
|
||||
right.push_back(prim);
|
||||
}
|
||||
}
|
||||
std::vector<std::shared_ptr<Primitive>> leftPrimitives, rightPrimitives;
|
||||
for (const auto &primitive : primitives) {
|
||||
if (primitive->minimumBounds(node->dimension) < node->split)
|
||||
leftPrimitives.push_back(primitive);
|
||||
if (primitive->maximumBounds(node->dimension) >= node->split)
|
||||
rightPrimitives.push_back(primitive);
|
||||
}
|
||||
|
||||
// Print out the number of primitives in the left and right child node
|
||||
std::cout << "Nodes: " << left.size() << " / " << right.size() << "\n";
|
||||
// std::cout << "(FastScene): Split " << leftPrimitives.size() << " | " << rightPrimitives.size() << std::endl;
|
||||
|
||||
// Set the left and right split vectors
|
||||
Vector3d newMinBounds = {node->dimension == 0 ? node->split : minimumBounds[0],
|
||||
node->dimension == 1 ? node->split : minimumBounds[1],
|
||||
node->dimension == 2 ? node->split : minimumBounds[2]};
|
||||
Vector3d newMaxBounds = {node->dimension == 0 ? node->split : maximumBounds[0],
|
||||
node->dimension == 1 ? node->split : maximumBounds[1],
|
||||
node->dimension == 2 ? node->split : maximumBounds[2]};
|
||||
node->child[0] = build(minimumBounds, newMaxBounds, left, depth);
|
||||
node->child[1] = build(newMinBounds, maximumBounds, right, depth);
|
||||
Vector3d minimumSplit = minimumBounds;
|
||||
Vector3d maximumSplit = maximumBounds;
|
||||
minimumSplit[node->dimension] = node->split;
|
||||
maximumSplit[node->dimension] = node->split;
|
||||
|
||||
// Recursively build the tree
|
||||
depth += 1;
|
||||
node->child[0] = this->build(minimumBounds, maximumSplit, leftPrimitives, depth);
|
||||
node->child[1] = this->build(minimumSplit, maximumBounds, rightPrimitives, depth);
|
||||
return node;
|
||||
}
|
||||
|
|
28
shader/materialshader.cpp
Normal file
28
shader/materialshader.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "light/light.h"
|
||||
#include "scene/scene.h"
|
||||
#include "shader/materialshader.h"
|
||||
#include <cmath>
|
||||
|
||||
Vector3d tangentToWorldSpace(const Vector3d &surfaceNormal, const Vector3d &surfaceTangent, const Vector3d &surfaceBitangent, const Vector3d &textureNormal) {
|
||||
return textureNormal.x * surfaceTangent + textureNormal.y * surfaceBitangent + textureNormal.z * surfaceNormal;
|
||||
}
|
||||
|
||||
MaterialShader::MaterialShader() : opacity(1.0f), normalCoefficient(1.0f), diffuseCoefficient(0.5f), reflectance(0.0f), specularCoefficient(0.5f), shininessExponent(8) {}
|
||||
|
||||
Color MaterialShader::shade(Scene const &scene, Ray const &ray) const {
|
||||
Color fragmentColor;
|
||||
|
||||
// IMPLEMENT ME
|
||||
|
||||
// (Normal Map) Calculate the new normal vector
|
||||
|
||||
// (Diffuse-/Specular Map) Accumulate the light over all light sources
|
||||
|
||||
// (Reflection Map) Calculate the reflectance, create a reflection ray
|
||||
|
||||
// (Alpha Map) Calculate the opacity, create a background ray
|
||||
|
||||
return fragmentColor;
|
||||
}
|
||||
|
||||
bool MaterialShader::isTransparent() const { return this->opacity < 1.0f || this->alphaMap; }
|
49
shader/materialshader.h
Normal file
49
shader/materialshader.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
#ifndef MATERIALSHADER_H
|
||||
#define MATERIALSHADER_H
|
||||
|
||||
#include "common/texture.h"
|
||||
#include "shader/shader.h"
|
||||
#include <memory>
|
||||
|
||||
class MaterialShader : public Shader {
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
MaterialShader();
|
||||
|
||||
// Set
|
||||
void setAlphaMap(std::shared_ptr<Texture> const &alphaMap) { this->alphaMap = alphaMap; }
|
||||
void setOpacity(float opacity) { this->opacity = opacity; }
|
||||
void setNormalMap(std::shared_ptr<Texture> const &normalMap) { this->normalMap = normalMap; }
|
||||
void setNormalCoefficient(float normalCoefficient) { this->normalCoefficient = normalCoefficient; }
|
||||
void setDiffuseMap(std::shared_ptr<Texture> const &diffuseMap) { this->diffuseMap = diffuseMap; }
|
||||
void setDiffuseCoefficient(float diffuseCoefficient) { this->diffuseCoefficient = diffuseCoefficient; }
|
||||
void setSpecularMap(std::shared_ptr<Texture> const &specularMap) { this->specularMap = specularMap; }
|
||||
void setSpecularCoefficient(float specularCoefficient) { this->specularCoefficient = specularCoefficient; }
|
||||
void setShininessExponent(float shininessExponent) { this->shininessExponent = shininessExponent; }
|
||||
void setReflectionMap(std::shared_ptr<Texture> const &reflectionMap) { this->reflectionMap = reflectionMap; }
|
||||
void setReflectance(float reflectance) { this->reflectance = reflectance; }
|
||||
|
||||
// Shader functions
|
||||
Color shade(Scene const &scene, Ray const &ray) const override;
|
||||
bool isTransparent() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Texture> alphaMap;
|
||||
float opacity;
|
||||
|
||||
std::shared_ptr<Texture> normalMap;
|
||||
float normalCoefficient;
|
||||
|
||||
std::shared_ptr<Texture> diffuseMap;
|
||||
float diffuseCoefficient;
|
||||
|
||||
std::shared_ptr<Texture> reflectionMap;
|
||||
float reflectance;
|
||||
|
||||
std::shared_ptr<Texture> specularMap;
|
||||
float specularCoefficient;
|
||||
float shininessExponent;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue