Merge remote-tracking branch 'base/master'

This commit is contained in:
arvid schröder 2022-12-06 14:28:05 +01:00
commit c5cfe34861
11 changed files with 326 additions and 99 deletions

View file

@ -59,6 +59,9 @@ add_executable(tracey_ex4 ex4.cpp)
target_link_libraries(tracey_ex4 tracey) target_link_libraries(tracey_ex4 tracey)
add_executable(tracey_ex5 ex5.cpp)
target_link_libraries(tracey_ex5 tracey)

54
ex5.cpp Normal file
View file

@ -0,0 +1,54 @@
#include <iostream>
#include <string>
#include "camera/perspectivecamera.h"
#include "scene/fastscene.h"
#include "renderer/kdtreerenderer.h"
#include "renderer/simplerenderer.h"
#include "shader/cooktorranceshader.h"
#include "light/ambientlight.h"
#include "light/pointlight.h"
#include "light/spotlight.h"
#include "primitive/objmodel.h"
int main() {
FastScene scene;
scene.setEnvironmentMap(std::make_shared<Texture>("data/space.png"));
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(90.0f);
camera.setPosition(Vector3d(0.0f, -2.5f, 10.0f));
camera.setForwardDirection(Vector3d(0.0f, 0.6f, -1.0f));
camera.setUpDirection(Vector3d(0.0f, 1.0f, 0.0f));
// Add shaders
auto redCook = std::make_shared<CookTorranceShader>(Color(1.0f, 0.0f, 0.0f), Color(1.0f, 1.0f, 1.0f), 1.0f, 0.3f);
auto goldCook = std::make_shared<CookTorranceShader>(Color(0.83f, 0.69f, 0.22f), Color(1.0f, 1.0f, 0.0f), 1.2f, 0.2f);
scene.addObj("data/teapot_stadium.obj", Vector3d(1.0f, 1.0f, 1.0f) * 40.0f, Vector3d(-0.25f, -0.5f, -2.5f), goldCook);
scene.addObj("data/stadium.obj", Vector3d(1.0f, 1.0f, 1.0f) * 70.0f, Vector3d(-0.5f, -1.0f, -3.0f), redCook);
// Add lights
scene.add(std::make_shared<AmbientLight>(0.1f));
scene.add(std::make_shared<PointLight>(Vector3d(-0.25f, 20.0f, -3.0f), 60.0f));
scene.add(
std::make_shared<SpotLight>(Vector3d(-0.25f, 7.0f, -4.0f), Vector3d(0.0f, -1.0f, 0.0f), 10.0f, 30.0f, 25.0f));
// build the tree
scene.buildTree();
// Render the scene
SimpleRenderer renderer;
renderer.renderImage(scene, camera, 1920, 1080).save("result.png");
KDTreeRenderer kd_renderer;
kd_renderer.renderKDTree(scene, camera, 1920, 1080).save("result_kd.png");
return 0;
}

View file

@ -112,12 +112,13 @@ bool Triangle::intersect(Ray &ray) const {
return false; return false;
// Calculate the normal // Calculate the normal
// IMPLEMENT smooth triangles, if available if (length(this->normal[0]) * length(this->normal[1]) * length(this->normal[2]) > EPSILON)
if (this->normal[0] == Vector3d{} || this->normal[1] == Vector3d{} || this->normal[2] == Vector3d{}) { ray.normal = normalized(u * this->normal[1] + v * this->normal[2] + (1 - u - v) * this->normal[0]);
else
ray.normal = normalized(crossProduct(edge1, edge2)); ray.normal = normalized(crossProduct(edge1, edge2));
} else { // calculate the tangent and bitangent vectors as well
ray.normal = u * this->normal[1] + v * this->normal[2] + (1 - u - v) * this->normal[0]; ray.tangent = normalized(u * this->tangent[1] + v * this->tangent[2] + (1 - u - v) * this->tangent[0]);
} ray.bitangent = normalized(u * this->bitangent[1] + v * this->bitangent[2] + (1 - u - v) * this->bitangent[0]);
// Calculate the surface position // Calculate the surface position

View file

@ -0,0 +1,18 @@
#include "camera/camera.h"
#include "renderer/kdtreerenderer.h"
#include "scene/fastscene.h"
#include <iostream>
Texture KDTreeRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height);
std::cout << "KD Tree renderer will only output KD Tree visualization" << std::endl;
return image;
}
Texture KDTreeRenderer::renderKDTree(FastScene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height);
// IMPLEMENT ME
return image;
}

22
renderer/kdtreerenderer.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef KDTREERENDERER_H
#define KDTREERENDERER_H
#include "renderer/renderer.h"
// Forward Declaration
class FastScene;
class KDTreeRenderer : public Renderer {
public:
// Constructor / Destructor
KDTreeRenderer() = default;
~KDTreeRenderer() override = default;
// Render functions
Texture renderImage(Scene const &scene, Camera const &camera, int width, int height) override;
Texture renderKDTree(FastScene const &scene, Camera const &camera, int width, int height);
};
#endif

73
scene/fastscene.cpp Normal file
View file

@ -0,0 +1,73 @@
#include "primitive/primitive.h"
#include "scene/fastscene.h"
#include "shader/shader.h"
#include <algorithm>
#include <iostream>
int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
return 0;
}
bool Node::findIntersection(Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
// If this is a leaf node, we intersect with all the primitives...
// ... otherwise we continue through the branches
// Determine the order in which we intersect the child nodes
// Traverse the necessary children
return false;
}
bool Node::findOcclusion(Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
return false;
}
int FastScene::countNodeIntersections(const Ray &ray) const {
// IMPLEMENT ME
return false;
}
bool FastScene::findIntersection(Ray &ray) const {
// IMPLEMENT ME
return false;
}
bool FastScene::findOcclusion(Ray &ray) const {
// IMPLEMENT ME
return false;
}
void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) {
// IMPLEMENT ME
// Set the new depth and number of primitives
// Determine the bounding box of the kD-Tree
// Recursively build the kD-Tree
}
std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d const &maximumBounds, const std::vector<std::shared_ptr<Primitive>> &primitives, int depth) {
// IMPLEMENT ME
// Determine the diameter of the bounding box
// Test whether we have reached a leaf node...
// ... otherwise create a new inner node by splitting through the widest
// dimension
// Determine the split position
// Note: Use the median of the minimum bounds of the primitives
// 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!
// Print out the number of primitives in the left and right child node
// Set the left and right split vectors
// Recursively build the tree
return nullptr;
}

46
scene/fastscene.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef FASTSCENE_H
#define FASTSCENE_H
#include "scene/scene.h"
struct Node {
// Constructor / Destructor
Node() : dimension(0), split(0) {}
// Traversal function
bool findIntersection(Ray &ray, float t0, float t1) const;
bool findOcclusion(Ray &ray, float t0, float t1) const;
int countNodeIntersections(const Ray &ray, float t0, float t1) const;
inline bool isLeaf() const { return (!this->primitives.empty() || (!this->child[0] && !this->child[1])); }
// Branch split
std::unique_ptr<Node> child[2];
int dimension;
float split;
// Leaf primitives
std::vector<std::shared_ptr<Primitive>> primitives;
};
class FastScene : public Scene {
public:
// Raytracing functions
bool findIntersection(Ray &ray) const override;
bool findOcclusion(Ray &ray) const override;
int countNodeIntersections(const Ray &ray) const;
// Setup functions
void buildTree(int maximumDepth = 10, int minimumNumberOfPrimitives = 2);
private:
std::unique_ptr<Node> build(Vector3d const &minimumBounds, Vector3d const &maximumBounds,
const std::vector<std::shared_ptr<Primitive>> &primitives, int depth);
std::unique_ptr<Node> root;
int maximumDepth;
int minimumNumberOfPrimitives;
Vector3d absoluteMinimum, absoluteMaximum;
};
#endif

View file

@ -8,90 +8,42 @@ BrdfShader::BrdfShader(char const *fileName, Color const &scale)
: scale(scale), brdf(std::make_unique<BRDFRead>(fileName)) {} : scale(scale), brdf(std::make_unique<BRDFRead>(fileName)) {}
Color BrdfShader::shade(Scene const &scene, Ray const &ray) const { Color BrdfShader::shade(Scene const &scene, Ray const &ray) const {
// Calculate theta and phi
float thetaIn = std::acos(dotProduct(-ray.normal, ray.direction));
float phiIn = 0.0f;
// Derive local coordinate system
Vector3d const x = crossProduct(-ray.direction, ray.normal);
Vector3d const y = crossProduct(ray.normal, x);
// Accumulate the light over all light sources
Color illuminationColor; Color illuminationColor;
/* for (const auto &light : scene.lights()) {
* Arvids Code Light::Illumination illum;
static auto rebase = [](Vector3d const &axis_x, illum = light->illuminate(scene, ray);
Vector3d const &axis_y,
Vector3d const &axis_z,
Vector3d const &vec) {
auto det = axis_x.x * axis_y.y * axis_z.z +
axis_x.y * axis_y.z * axis_z.x +
axis_x.z * axis_y.x * axis_z.y -
axis_x.x * axis_y.z * axis_z.y -
axis_x.y * axis_y.x * axis_z.z -
axis_x.z * axis_y.y * axis_z.x;
// Calculate resulting vector by using inverse matrix of new base vectors
return Vector3d{
((axis_y.y * axis_z.z - axis_y.z * axis_z.y) * vec.x +
(axis_y.z * axis_z.x - axis_y.x * axis_z.z) * vec.y +
(axis_y.x * axis_z.y - axis_z.x * axis_y.y) * vec.z) / det,
((axis_x.z * axis_z.y - axis_x.y * axis_z.z) * vec.x +
(axis_x.x * axis_z.z - axis_x.z * axis_z.x) * vec.y +
(axis_z.x * axis_x.y - axis_x.x * axis_z.y) * vec.z) / det,
((axis_x.y * axis_y.z - axis_x.z * axis_y.y) * vec.x +
(axis_y.x * axis_x.z - axis_x.x * axis_y.z) * vec.y +
(axis_x.x * axis_y.y - axis_y.x * axis_x.y) * vec.z) / det
};
};
for (auto &light: scene.lights()) { // Diffuse term
float const cosine = dotProduct(-illum.direction, ray.normal);
if (cosine > 0) {
Color color;
auto illum = light->illuminate(scene, ray); // Avoid numeric instability
if (dotProduct(ray.normal, illum.direction) <= EPSILON) { if (cosine < 1) {
continue; float const thetaOut = std::acos(cosine);
// Project outgoing vector into local coordinate system
Vector3d const c = crossProduct(-illum.direction, ray.normal);
float const phiOut = std::atan2(dotProduct(c, y), dotProduct(c, x));
color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, thetaOut, phiOut));
} else {
color = Color(brdf->lookupBrdfValues(thetaIn, phiIn, 0, 0));
} }
auto axis = orthoNormalized(ray.normal, ray.direction, illum.direction); // Calculate colors
auto axis_y = std::get<0>(axis); Color const diffuseColor = scale * color * cosine;
auto axis_x = std::get<1>(axis); illuminationColor += diffuseColor * illum.color;
auto axis_z = std::get<2>(axis);
auto N = normalized(rebase(axis_x, axis_y, axis_z, ray.normal));
auto D = normalized(rebase(axis_x, axis_y, axis_z, -ray.direction));
auto L = normalized(rebase(axis_x, axis_y, axis_z, -illum.direction));
D = axis_y * D.y + axis_z * D.z;
L = axis_y * L.y + axis_z * L.z;
illuminationColor += brdf->lookupBrdfValues(std::acos(dotProduct(ray.normal, -ray.direction)),
0.0f,
std::acos(dotProduct(illum.direction, ray.normal)),
std::acos(dotProduct(D, L)));
} }
*/
// IMPLEMENT ME
for(auto& light : scene.lights()){
Light::Illumination illum = light->illuminate(scene, ray);
Vector3d invertedDirection = -ray.direction;
Vector3d invertedIllum = -illum.direction;
// the dot-product cant be negative otherwise the light ray would come from the inside
if(dotProduct(invertedIllum, ray.normal) < 0)
continue;
// Calculate coordinate System
Vector3d axisX = crossProduct(invertedDirection, ray.normal);
Vector3d axisY = crossProduct(axisX, ray.normal);
// Project ray.direction and illum.direction into plane
Vector2d projectedIllum = Vector2d(dotProduct(axisX, invertedIllum), dotProduct(axisY, invertedIllum));
Vector2d projectedDirection = Vector2d(dotProduct(axisX, invertedDirection), dotProduct(axisY, invertedDirection));
// get Z coordinate for theta angles
double coordinateZDirection = dotProduct(ray.normal, invertedDirection);
double coordinateZIllum = dotProduct(ray.normal, invertedIllum);
// calculate theta1 and 2
double theta1Correct = std::atan2(length(projectedDirection), coordinateZDirection);
double theta2Correct = std::atan2(length(projectedIllum), coordinateZIllum);
// calculate phi
double phi = std::atan2(projectedIllum.u, projectedIllum.v);
illuminationColor += brdf->lookupBrdfValues(theta1Correct, 0.0, theta2Correct, phi);
} }
return illuminationColor; return illuminationColor;
} }

View file

@ -7,16 +7,51 @@ CookTorranceShader::CookTorranceShader(Color const &diffCol, Color const &ctCol,
ctColor(ctCol * ctCoeff), F0(IOR), ctColor(ctCol * ctCoeff), F0(IOR),
m(roughness) {} m(roughness) {}
float CookTorranceShader::D(float NdotH) const {
// Beckmann distribution
float const r2 = m * m;
float const NdotH2 = NdotH * NdotH;
return expf((NdotH2 - 1.0f) / (r2 * NdotH2)) / (4.0f * r2 * powf(NdotH, 4.0f));
}
float CookTorranceShader::F(float VdotH) const {
// Schlicks approximation
return F0 + (1.0f - F0) * powf(1.0f - VdotH, 5);
}
float CookTorranceShader::G(float NdotH, float NdotV, float VdotH, float NdotL) const { return std::min(1.0f, std::min(2.0f * NdotH * NdotV / VdotH, 2.0f * NdotH * NdotL / VdotH)); }
Color CookTorranceShader::shade(Scene const &scene, Ray const &ray) const { Color CookTorranceShader::shade(Scene const &scene, Ray const &ray) const {
Color fragmentColor; Color fragmentColor;
// IMPLEMENT ME if (m >= 0.0f) {
for (auto &light: scene.lights()) { // Accumulate the light over all light sources
auto illum = light->illuminate(scene, ray); for (const auto &light : scene.lights()) {
auto N = ray.normal; Light::Illumination illum;
auto V = ray.direction; illum = light->illuminate(scene, ray);
auto L = illum.direction;
auto H = normalized(V + L); float const NdotL = std::max(0.0f, dotProduct(-illum.direction, ray.normal));
if (NdotL <= 0.0f)
continue;
// Diffuse term
Color const diffuse = this->diffuseColor / float(PI);
fragmentColor += diffuse * NdotL * illum.color;
// Cook-Torrance term
// half angle vector
Vector3d const H = normalized(-illum.direction - ray.direction);
float const NdotH = std::max(0.0f, dotProduct(ray.normal, H));
float const NdotV = std::max(0.0f, dotProduct(ray.normal, -ray.direction));
float const VdotH = std::max(0.0f, dotProduct(-ray.direction, H));
if (NdotV * NdotL > EPSILON) {
Color const specular = this->ctColor * (F(VdotH) * D(NdotH) * G(NdotH, NdotV, VdotH, NdotL)) / (float(PI) * NdotV * NdotL);
fragmentColor += specular * NdotL * illum.color;
}
}
}
auto NV = dotProduct(N, V); auto NV = dotProduct(N, V);
auto NL = dotProduct(N, L); auto NL = dotProduct(N, L);

View file

@ -7,11 +7,13 @@ LambertShader::LambertShader(Color const &diffuseColor) : diffuseColor(diffuseCo
Color LambertShader::shade(Scene const &scene, Ray const &ray) const { Color LambertShader::shade(Scene const &scene, Ray const &ray) const {
Color fragmentColor; Color fragmentColor;
// IMPLEMENT ME // Accumulate the light over all light sources
for (auto& light : scene.lights()) { for (const auto &light : scene.lights()) {
auto illum = light->illuminate(scene, ray); Light::Illumination const illum = light->illuminate(scene, ray);
auto lightAngle = dotProduct(ray.normal, normalized(illum.direction)); // Diffuse term
fragmentColor += illum.color * std::abs(lightAngle); Color const diffuse = this->diffuseColor * std::max(dotProduct(-illum.direction, ray.normal), 0.0f);
fragmentColor += diffuse * illum.color;
} }
return fragmentColor * diffuseColor;
return fragmentColor;
} }

View file

@ -10,6 +10,27 @@ PhongShader::PhongShader(Color const &diffuseColor, float diffuseCoefficient, Co
Color PhongShader::shade(Scene const &scene, Ray const &ray) const { Color PhongShader::shade(Scene const &scene, Ray const &ray) const {
Color fragmentColor; Color fragmentColor;
// Calculate the reflection vector
Vector3d const reflection = ray.direction - 2 * dotProduct(ray.normal, ray.direction) * ray.normal;
// Accumulate the light over all light sources
for (const auto &light : scene.lights()) {
Light::Illumination illum;
illum = light->illuminate(scene, ray);
// Diffuse term
Color const diffuse =
this->diffuseCoefficient * this->diffuseColor * std::max(dotProduct(-illum.direction, ray.normal), 0.0f);
fragmentColor += diffuse * illum.color;
// Specular term
float const cosine = dotProduct(-illum.direction, reflection);
if (cosine > 0) {
Color const specular = this->specularCoefficient * this->specularColor // highlight
* powf(cosine, this->shininessExponent); // shininess factor
fragmentColor += specular * illum.color;
}
}
for (auto &light: scene.lights()) { for (auto &light: scene.lights()) {
auto illum = light->illuminate(scene, ray); auto illum = light->illuminate(scene, ray);