first commit

This commit is contained in:
Maximilian Giller 2022-11-05 22:08:16 +01:00
commit bbfdda7796
41 changed files with 80434 additions and 0 deletions

19
.gitignore vendored Normal file
View file

@ -0,0 +1,19 @@
# Project ide-Folders
.vs
.vscode
.idea
build/
/data/*
!/data/README.txt
/result*
# Latex stuff
*.aux
*.fdb_latexmk
*.fls
*.log
*.out
*.pdf
*.synctex.gz
*.json

74
BUILD_INSTRUCTIONS.md Normal file
View file

@ -0,0 +1,74 @@
# Build-Anleitung Linux (und hoffentlich auch Mac):
## Sicherstellen, dass alle Abhängigkeiten installiert sind
Das Projekt davon ab, dass folgende Programme vorhanden und auffindbar sind:
```
cmake
make (ninja, ...)
g++ (clang, ...)
ImageMagick
```
## Repository auschecken (ab hier alles im Terminal):
`git clone gogs@FORK_OF_THIS_REPO`
oder
`git -c http.sslVerify=false https://FORK_OF_THIS_REPO`
## Build Verzeichnis anlegen
`cd FOLDER`
`mkdir build`
`cd build`
## CMake und make ausführen
`cmake .. && make`
für schnelleres Bauen ist `make -j4` zu empfehlen
## Die gebaute Anwendung ausführen
`./tracey_ex1` oder `./tracey_exN`
# Build-Anleitung Windows
## Sicherstellen, dass alle Abhängigkeiten installiert sind
Das Projekt davon ab, dass folgende Programme vorhanden und auffindbar sind:
```
cmake
make (ninja, ...)
c++ Kompiler
ImageMagick
```
Der wahrscheinlich meistverbreitete C++ Kompiler für Windows ist der MSVC, der mit Visual Studio mitgeliefert wird.
Studenten der TU können diesen kostenlos in Enterprise-Variante von https://doku.rz.tu-bs.de/doku.php?id=software:azure_dev_tools beziehen.
Eine Community-Version ist auch verfügbar: https://visualstudio.microsoft.com/de/vs/
## Repository auschecken (im Terminal):
`git clone gogs@FORK_OF_THIS_REPO`
oder
`git -c http.sslVerify=false https://FORK_OF_THIS_REPO`
## CMake-GUI ausführen
![CMake-Gui](images/cmake-gui.png)
Auf _Configure_ klicken und sicherstellen, dass die entsprechende Version von Visual Studio verwendet wird:
![Generator](images/cmake-gui-generator.png)
Auf _Finish_ und auf _Generate_ (evtl. 2 mal) klicken.
Mit _Open Project_ sollte die entsprechende Solution in VS geöffnet werden (alternativ kann die _.sln_ aus dem Build-Ordner mit VS geöffnet werden).
## In Visual Studio bauen
Das _Startup Project_ auf die entsprechende Aufgabe setzen:
![Visual-Studio](images/vs.png)
Mit _F5_ das Projekt bauen und gleichzeitig mit angehängtem Debugger starten. Nach erfolgreicher Beendigung sollte sich im `build`-Ordner die `result.png` befinden.

39
CMakeLists.txt Normal file
View file

@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.5)
project(CG1_Tracer LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT WIN32)
find_package(Threads REQUIRED)
find_package(X11 REQUIRED)
endif()
# This directory
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if(APPLE)
include_directories(AFTER "/opt/X11/include")
endif()
# which source files to use
file(GLOB common_src "common/*.cpp")
file(GLOB camera_src "camera/*.cpp")
file(GLOB light_src "light/*.cpp")
file(GLOB primitive_src "primitive/*.cpp")
file(GLOB renderer_src "renderer/*.cpp")
file(GLOB scene_src "scene/*.cpp")
file(GLOB shader_src "shader/*.cpp")
# The tracey library
add_library(tracey STATIC ${common_src} ${camera_src} ${light_src}
${primitive_src} ${renderer_src} ${scene_src} ${shader_src})
if(NOT WIN32)
target_link_libraries(tracey ${CMAKE_THREAD_LIBS_INIT} ${X11_LIBRARIES})
endif()
# Executables
add_executable(tracey_ex1 ex1.cpp)
target_link_libraries(tracey_ex1 tracey)

35
README.md Normal file
View file

@ -0,0 +1,35 @@
# Git Grundlagen / Workflow
Git Spickzettel: https://files.morr.cc/git-spickzettel.png
Guter Schnellstart mit Begriffen und Konzepten ist Kapitel 2 des _pro Git_ Buches: https://git-scm.com/book/de/v2/Git-Grundlagen-Ein-Git-Repository-anlegen
# Musterlösung integrieren (ohne die ganze History wegzuwerfen)
Angepasst von https://medium.com/@topspinj/how-to-git-rebase-into-a-forked-repo-c9f05e821c8a
Das Musterlösungs-Repository als remote mit Namen _upstream_ (oder ähnlich) hinzufügen:
`git remote add upstream gogs@git.cg.cs.tu-bs.de:CG1/WS2021.git`
Alle Branches von _upstream_ und deren Änderungen herunterladen
`git fetch upstream`
Die Änderungen hineinmergen und Konflikte so auflösen, dass die Musterlösung bevorzugt wird
`git merge -X theirs upstream/master`
bitte beachten: Dies löst nur _Konflikte_ so auf, dass die Änderungen der Musterlösung bevorzugt werden. Wenn ihr andere Dateien hinzugefügt habt, werden diese immer noch nach dem Merge vorhanden sein.
Ebenso verhält es sich, wenn ihr Interfaces von Funktionen geändert oder Änderungen vorgenommen habt, die nicht mit der _upstream_-Version in Konflikt stehen (z.B wenn ihr in Aufgabe 03 etwas an der Schnittpunktberechnung aus Aufgabe 01 ändert, das nicht in Aufgabe 03 angepasst wurde, wird die Änderung nach dem Merge immer noch da sein).
Deswegen kann so ein Merge immer noch in nicht-kompilierbaren Code resultieren oder Bugs einführen.
Daher ist es _immer_ eine gute Idee, sich noch einmal einen Diff zwischen der aktuellen Version und der Musterlösung zu Gemüte zu führen:
`git diff master upstream/master`
hier kann man dann noch ggf. selbst Hand anlegen und die Änderungen dann wieder auf das eigene Repository schieben:
`git commit -am "Merge solution"`
`git push origin master`
evtl. muss noch `--force` angegeben werden

17
camera/camera.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef CAMERA_H
#define CAMERA_H
#include "common/ray.h"
class Camera {
public:
// Constructor / Destructor
Camera() = default;
virtual ~Camera() = default;
// Camera functions
virtual Ray createRay(float x, float y) const = 0;
};
#endif

View file

@ -0,0 +1,10 @@
#include "camera/perspectivecamera.h"
PerspectiveCamera::PerspectiveCamera() : forwardDirection(0, 0, 1), upDirection(0, 1, 0), fovAngle(70) {}
Ray PerspectiveCamera::createRay(float x, float y) const {
// IMPLEMENT ME!
// Set up a left-handed coordinate system,
// in which the camera looks along the positive z-Axis
return Ray();
}

View file

@ -0,0 +1,29 @@
#ifndef PERSPECTIVECAMERA_H
#define PERSPECTIVECAMERA_H
#include "camera/camera.h"
class PerspectiveCamera : public Camera {
public:
// Constructor / Destructor
PerspectiveCamera();
~PerspectiveCamera() override = default;
// Set
void setPosition(Vector3d const &position) { this->position = position; }
void setForwardDirection(Vector3d const &forwardDirection) { this->forwardDirection = normalized(forwardDirection); }
void setUpDirection(Vector3d const &upDirection) { this->upDirection = normalized(upDirection); }
void setFovAngle(float fovAngle) { this->fovAngle = fovAngle; }
// Camera functions
Ray createRay(float x, float y) const override;
protected:
Vector3d position;
Vector3d forwardDirection;
Vector3d upDirection;
float fovAngle;
};
#endif

79034
common/CImg.h Normal file

File diff suppressed because it is too large Load diff

106
common/color.cpp Normal file
View file

@ -0,0 +1,106 @@
#include "common/color.h"
#include <algorithm>
#include <cassert>
// Access operators ////////////////////////////////////////////////////////////
float &Color::operator[](int channel) {
assert(0 <= channel && channel < 3);
switch (channel) {
case Channel::R:
return this->r;
case Channel::G:
return this->g;
case Channel::B:
return this->b;
default: // This must never happen
return this->r;
}
}
float const &Color::operator[](int channel) const {
assert(0 <= channel && channel < 3);
switch (channel) {
case Channel::R:
return this->r;
case Channel::G:
return this->g;
case Channel::B:
return this->b;
default: // This must never happen
return this->r;
}
}
// Comparison operators ////////////////////////////////////////////////////////
bool operator==(Color const &left, Color const &right) {
return (left.r == right.r && left.g == right.g && left.b == right.b);
}
bool operator!=(Color const &left, Color const &right) { return !(left == right); }
// Arithmetic operators ////////////////////////////////////////////////////////
Color operator+(Color const &left, Color const &right) {
return Color(left.r + right.r, left.g + right.g, left.b + right.b);
}
Color operator-(Color const &right) { return Color(-right.r, -right.g, -right.b); }
Color operator-(Color const &left, Color const &right) {
return Color(left.r - right.r, left.g - right.g, left.b - right.b);
}
Color operator*(Color const &left, float right) { return Color(left.r * right, left.g * right, left.b * right); }
Color operator*(float left, Color const &right) { return Color(left * right.r, left * right.g, left * right.b); }
Color operator*(Color const &left, Color const &right) {
return Color(left.r * right.r, left.g * right.g, left.b * right.b);
}
Color operator/(Color const &left, float right) { return Color(left.r / right, left.g / right, left.b / right); }
Color operator/(Color const &left, Color const &right) {
return Color(left.r / right.r, left.g / right.g, left.b / right.b);
}
// Assignment operators ////////////////////////////////////////////////////////
Color &operator+=(Color &left, Color const &right) {
left.r += right.r;
left.g += right.g;
left.b += right.b;
return left;
}
Color &operator-=(Color &left, Color const &right) {
left.r -= right.r;
left.g -= right.g;
left.b -= right.b;
return left;
}
Color &operator*=(Color &left, float right) {
left.r *= right;
left.g *= right;
left.b *= right;
return left;
}
Color &operator/=(Color &left, float right) {
left.r /= right;
left.g /= right;
left.b /= right;
return left;
}
// Useful functions ////////////////////////////////////////////////////////////
Color clamped(Color const &c) {
return Color(std::max(0.0f, std::min(c.r, 1.0f)), std::max(0.0f, std::min(c.g, 1.0f)),
std::max(0.0f, std::min(c.b, 1.0f)));
}
void clamp(Color *c) { *c = clamped(*c); }

45
common/color.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef COLOR_H
#define COLOR_H
struct Color {
// Components
float r, g, b;
// Enum for readability
enum Channel { R, G, B };
// Constructor
Color() : r(0), g(0), b(0) {}
Color(float r, float g, float b) : r(r), g(g), b(b) {}
// Access operators
float &operator[](int channel);
float const &operator[](int channel) const;
};
// Comparison operators
bool operator==(Color const &left, Color const &right);
bool operator!=(Color const &left, Color const &right);
// Arithmetic operators
Color operator+(Color const &left, Color const &right);
Color operator-(Color const &right);
Color operator-(Color const &left, Color const &right);
Color operator*(Color const &left, float right);
Color operator*(float left, Color const &right);
Color operator*(Color const &left, Color const &right);
Color operator/(Color const &left, float right);
Color operator/(Color const &left, Color const &right);
// Assignment operators
Color &operator+=(Color &left, Color const &right);
Color &operator-=(Color &left, Color const &right);
Color &operator*=(Color &left, float right);
Color &operator/=(Color &left, float right);
// Useful functions
Color clamped(Color const &c);
void clamp(Color *c);
#endif

18
common/common.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef COMMON_H
#define COMMON_H
#include <cmath>
#ifndef EPSILON
#define EPSILON 1E-4f
#endif
#ifndef INFINITY
#define INFINITY HUGE_VAL
#endif
#ifndef PI
#define PI 3.1415926535897932384f
#endif
#endif

40
common/ray.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef RAY_H
#define RAY_H
#include "common/common.h"
#include "common/vector2d.h"
#include "common/vector3d.h"
#include <atomic>
// Forward declaration
class Primitive;
struct Ray {
friend class Scene;
// Components
Vector3d origin; // o
Vector3d direction; // d
float length = INFINITY; // t
Primitive const *primitive = nullptr;
Vector3d normal;
Vector2d surface;
Vector3d tangent;
Vector3d bitangent;
// Constructor
Ray(Vector3d const &origin = Vector3d(0, 0, 0), Vector3d const &direction = Vector3d(0, 0, 1))
: origin(origin), direction(normalized(direction)) {
}
private:
#ifndef ICG_RAY_BOUNCES
int remainingBounces = 4;
#else
int remainingBounces = ICG_RAY_BOUNCES;
#endif
};
#endif

66
common/texture.cpp Normal file
View file

@ -0,0 +1,66 @@
#include "common/texture.h"
#include <cmath>
#include <fstream>
#include <iostream>
#include <string>
Texture::Texture(int width, int height) { this->resize(width, height); }
Texture::Texture(char const *fileName) { this->load(fileName); }
bool Texture::load(char const *fileName) {
CImg<unsigned char> img_char(fileName);
if (img_char.is_empty()) {
std::cerr << "(Image): Could not open file " << fileName << std::endl;
return false;
}
this->image_ = CImg<float>(img_char.width(), img_char.height(), img_char.depth(), 3, 0);
for (int c = 0; c < 3; c++)
cimg_forXYZ(img_char, x, y, z) {
this->image_(x, y, z, c) = img_char(x, y, z, std::min(c, img_char.spectrum() - 1)) / 255.0f;
}
return true;
}
bool Texture::save(char const *fileName) const {
CImg<unsigned char> img_char;
img_char = 255 * this->image_;
img_char.save(fileName);
return true;
}
Color Texture::getPixelAt(int x, int y) const {
if ((x %= this->width()) < 0)
x += this->width();
if ((y %= this->height()) < 0)
y += this->height();
return Color(this->image_.atXY(x, y, 0), this->image_.atXY(x, y, 1), this->image_.atXY(x, y, 2));
}
void Texture::setPixelAt(int x, int y, Color const &color) {
if ((x %= this->width()) < 0)
x += this->width();
if ((y %= this->height()) < 0)
y += this->height();
this->image_(x, y, 0) = color.r;
this->image_(x, y, 1) = color.g;
this->image_(x, y, 2) = color.b;
}
Color Texture::color(float u, float v, bool interpolate) const {
Color color;
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
color = this->getPixelAt(int(roundf(u * this->width())), int(roundf(v * this->height())));
}
return color;
}
Color Texture::color(Vector2d const &surfacePosition, bool interpolate) const {
return color(surfacePosition.u, surfacePosition.v, interpolate);
}

38
common/texture.h Normal file
View file

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

93
common/vector2d.cpp Normal file
View file

@ -0,0 +1,93 @@
#include "common/vector2d.h"
#include <cassert>
#include <cmath>
// Access operators ////////////////////////////////////////////////////////////
float &Vector2d::operator[](int dimension) {
assert(0 <= dimension && dimension < 2);
switch (dimension) {
case Dimension::U:
return this->u;
case Dimension::V:
return this->v;
default: // This must never happen
return this->u;
}
}
float const &Vector2d::operator[](int dimension) const {
assert(0 <= dimension && dimension < 2);
switch (dimension) {
case Dimension::U:
return this->u;
case Dimension::V:
return this->v;
default: // This must never happen
return this->u;
}
}
// Comparison operators ////////////////////////////////////////////////////////
bool operator==(Vector2d const &left, Vector2d const &right) { return (left.u == right.u && left.v == right.v); }
bool operator!=(Vector2d const &left, Vector2d const &right) { return !(left == right); }
// Arithmetic operators ////////////////////////////////////////////////////////
Vector2d operator+(Vector2d const &left, Vector2d const &right) { return Vector2d(left.u + right.u, left.v + right.v); }
Vector2d operator-(Vector2d const &right) { return Vector2d(-right.u, -right.v); }
Vector2d operator-(Vector2d const &left, Vector2d const &right) { return Vector2d(left.u - right.u, left.v - right.v); }
Vector2d operator*(Vector2d const &left, float right) { return Vector2d(left.u * right, left.v * right); }
Vector2d operator*(float left, Vector2d const &right) { return Vector2d(left * right.u, left * right.v); }
Vector2d operator/(Vector2d const &left, float right) { return Vector2d(left.u / right, left.v / right); }
// Assignment operators ////////////////////////////////////////////////////////
Vector2d &operator+=(Vector2d &left, Vector2d const &right) {
left.u += right.u;
left.v += right.v;
return left;
}
Vector2d &operator-=(Vector2d &left, Vector2d const &right) {
left.u -= right.u;
left.v -= right.v;
return left;
}
Vector2d &operator*=(Vector2d &left, float right) {
left.u *= right;
left.v *= right;
return left;
}
Vector2d &operator/=(Vector2d &left, float right) {
left.u /= right;
left.v /= right;
return left;
}
// Useful functions ////////////////////////////////////////////////////////////
Vector2d componentProduct(const Vector2d &left, const Vector2d &right) {
return Vector2d(left.u * right.u, left.v * right.v);
}
Vector2d componentQuotient(const Vector2d &left, const Vector2d &right) {
return Vector2d(left.u / right.u, left.v / right.v);
}
float dotProduct(Vector2d const &left, Vector2d const &right) { return left.u * right.u + left.v * right.v; }
float length(Vector2d const &c) { return std::sqrt(dotProduct(c, c)); }
Vector2d normalized(Vector2d const &v) { return v / length(v); }
void normalize(Vector2d *v) { *v = normalized(*v); }

46
common/vector2d.h Normal file
View file

@ -0,0 +1,46 @@
#ifndef VECTOR2D_H
#define VECTOR2D_H
struct Vector2d {
// Components
float u, v;
// Enum for readability
enum Dimension { U, V };
// Constructor
Vector2d() : u(0), v(0) {}
Vector2d(float u, float v) : u(u), v(v) {}
// Access operators
float &operator[](int dimension);
float const &operator[](int dimension) const;
};
// Comparison operators
bool operator==(Vector2d const &left, Vector2d const &right);
bool operator!=(Vector2d const &left, Vector2d const &right);
// Arithmetic operators
Vector2d operator+(Vector2d const &left, Vector2d const &right);
Vector2d operator-(Vector2d const &right);
Vector2d operator-(Vector2d const &left, Vector2d const &right);
Vector2d operator*(Vector2d const &left, float right);
Vector2d operator*(float left, Vector2d const &right);
Vector2d operator/(Vector2d const &left, float right);
// Assignment operators
Vector2d &operator+=(Vector2d &left, Vector2d const &right);
Vector2d &operator-=(Vector2d &left, Vector2d const &right);
Vector2d &operator*=(Vector2d &left, float right);
Vector2d &operator/=(Vector2d &left, float right);
// Useful functions
Vector2d componentProduct(Vector2d const &left, Vector2d const &right);
Vector2d componentQuotient(Vector2d const &left, Vector2d const &right);
float dotProduct(Vector2d const &left, Vector2d const &right);
float length(Vector2d const &c);
Vector2d normalized(Vector2d const &v);
void normalize(Vector2d *v);
#endif

120
common/vector3d.cpp Normal file
View file

@ -0,0 +1,120 @@
#include "common/vector3d.h"
#include <cassert>
#include <cmath>
// Access operators ////////////////////////////////////////////////////////////
float &Vector3d::operator[](int const dimension) {
assert(0 <= dimension && dimension < 3);
switch (dimension) {
case Dimension::X:
return this->x;
case Dimension::Y:
return this->y;
case Dimension::Z:
return this->z;
default: // This must never happen
return this->x;
}
}
float const &Vector3d::operator[](int const dimension) const {
assert(0 <= dimension && dimension < 3);
switch (dimension) {
case Dimension::X:
return this->x;
case Dimension::Y:
return this->y;
case Dimension::Z:
return this->z;
default: // This must never happen
return this->x;
}
}
// Comparison operators ////////////////////////////////////////////////////////
bool operator==(Vector3d const &left, Vector3d const &right) {
return (left.x == right.x && left.y == right.y && left.z == right.z);
}
bool operator!=(Vector3d const &left, Vector3d const &right) { return !(left == right); }
// Arithmetic operators ////////////////////////////////////////////////////////
Vector3d operator+(Vector3d const &left, Vector3d const &right) {
return Vector3d(left.x + right.x, left.y + right.y, left.z + right.z);
}
Vector3d operator-(Vector3d const &right) { return Vector3d(-right.x, -right.y, -right.z); }
Vector3d operator-(Vector3d const &left, Vector3d const &right) {
return Vector3d(left.x - right.x, left.y - right.y, left.z - right.z);
}
Vector3d operator*(Vector3d const &left, float right) {
return Vector3d(left.x * right, left.y * right, left.z * right);
}
Vector3d operator*(float left, Vector3d const &right) {
return Vector3d(left * right.x, left * right.y, left * right.z);
}
Vector3d operator/(Vector3d const &left, float right) {
return Vector3d(left.x / right, left.y / right, left.z / right);
}
// Assignment operators ////////////////////////////////////////////////////////
Vector3d &operator+=(Vector3d &left, Vector3d const &right) {
left.x += right.x;
left.y += right.y;
left.z += right.z;
return left;
}
Vector3d &operator-=(Vector3d &left, Vector3d const &right) {
left.x -= right.x;
left.y -= right.y;
left.z -= right.z;
return left;
}
Vector3d &operator*=(Vector3d &left, float right) {
left.x *= right;
left.y *= right;
left.z *= right;
return left;
}
Vector3d &operator/=(Vector3d &left, float right) {
left.x /= right;
left.y /= right;
left.z /= right;
return left;
}
// Useful functions ////////////////////////////////////////////////////////////
Vector3d componentProduct(const Vector3d &left, const Vector3d &right) {
return Vector3d(left.x * right.x, left.y * right.y, left.z * right.z);
}
Vector3d componentQuotient(const Vector3d &left, const Vector3d &right) {
return Vector3d(left.x / right.x, left.y / right.y, left.z / right.z);
}
Vector3d crossProduct(Vector3d const &left, Vector3d const &right) {
return Vector3d(left.y * right.z - left.z * right.y, left.z * right.x - left.x * right.z,
left.x * right.y - left.y * right.x);
}
float dotProduct(Vector3d const &left, Vector3d const &right) {
return left.x * right.x + left.y * right.y + left.z * right.z;
}
float length(Vector3d const &v) { return std::sqrt(dotProduct(v, v)); }
Vector3d normalized(Vector3d const &v) { return v / length(v); }
void normalize(Vector3d *v) { *v = normalized(*v); }

47
common/vector3d.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef VECTOR3D_H
#define VECTOR3D_H
struct Vector3d {
// Components
float x, y, z;
// Enum for readability
enum Dimension { X, Y, Z };
// Constructor
Vector3d() : x(0), y(0), z(0) {}
Vector3d(float x, float y, float z) : x(x), y(y), z(z) {}
// Access operators
float &operator[](int dimension);
float const &operator[](int dimension) const;
};
// Comparison operators
bool operator==(Vector3d const &left, Vector3d const &right);
bool operator!=(Vector3d const &left, Vector3d const &right);
// Arithmetic operators
Vector3d operator+(Vector3d const &left, Vector3d const &right);
Vector3d operator-(Vector3d const &right);
Vector3d operator-(Vector3d const &left, Vector3d const &right);
Vector3d operator*(Vector3d const &left, float right);
Vector3d operator*(float left, Vector3d const &right);
Vector3d operator/(Vector3d const &left, float right);
// Assignment operators
Vector3d &operator+=(Vector3d &left, Vector3d const &right);
Vector3d &operator-=(Vector3d &left, Vector3d const &right);
Vector3d &operator*=(Vector3d &left, float right);
Vector3d &operator/=(Vector3d &left, float right);
// Useful functions
Vector3d componentProduct(Vector3d const &left, Vector3d const &right);
Vector3d componentQuotient(Vector3d const &left, Vector3d const &right);
Vector3d crossProduct(Vector3d const &left, Vector3d const &right);
float dotProduct(Vector3d const &left, Vector3d const &right);
float length(Vector3d const &v);
Vector3d normalized(Vector3d const &v);
void normalize(Vector3d *v);
#endif

2
data/README.txt Normal file
View file

@ -0,0 +1,2 @@
Download the data folder from the lecture page under https://graphics.tu-bs.de/teaching
BRDFs courtesy of https://www.merl.com/brdf/

52
ex1.cpp Normal file
View file

@ -0,0 +1,52 @@
#include "camera/perspectivecamera.h"
#include "renderer/simplerenderer.h"
#include "scene/simplescene.h"
#include "primitive/infiniteplane.h"
#include "primitive/sphere.h"
#include "primitive/triangle.h"
#include "shader/flatshader.h"
int main() {
// Let's create a simple cornell box scene...
SimpleScene scene;
// Add shaders for the walls
auto red = std::make_shared<FlatShader>(Color(1.0f, 0.3f, 0.2f));
auto white = std::make_shared<FlatShader>(Color(1.0f, 1.0f, 1.0f));
auto blue = std::make_shared<FlatShader>(Color(0.2f, 0.3f, 1.0f));
// Add shaders for the objects
auto green = std::make_shared<FlatShader>(Color(0.0f, 1.0f, 0.0f));
auto purple = std::make_shared<FlatShader>(Color(0.5f, 0.2f, 1.0f));
auto orange = std::make_shared<FlatShader>(Color(1.0f, 0.5f, 0.0f));
// Set up the cornell box walls
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, +5.0f), Vector3d(0.0f, 0.0f, -1.0f), purple));
scene.add(std::make_shared<InfinitePlane>(Vector3d(0.0f, 0.0f, -5.0f), Vector3d(0.0f, 0.0f, +1.0f), purple));
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), blue));
scene.add(std::make_shared<InfinitePlane>(Vector3d(-5.0f, 0.0f, 0.0f), Vector3d(+1.0f, 0.0f, 0.0f), red));
// Add a sphere
scene.add(std::make_shared<Sphere>(Vector3d(-3.0f, 0.0f, 0.0f), 1.5f, green));
scene.add(std::make_shared<Triangle>(Vector3d(0.0f, -5.0f, -4.0f), Vector3d(0.0f, -3.0f, 0.0f),
Vector3d(5.0f, -2.0f, -3.0f), orange));
// Set up the camera
PerspectiveCamera camera;
camera.setFovAngle(70);
camera.setPosition(Vector3d(-2.5f, 2.5f, -10.0f));
camera.setForwardDirection(Vector3d(0.25f, -0.33f, 1.0f));
camera.setUpDirection(Vector3d(0.2f, 1.0f, 0.0f));
// Render the scene
SimpleRenderer renderer;
renderer.renderImage(scene, camera, 512, 512).save("result.png");
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
images/cmake-gui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
images/vs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

34
light/light.h Normal file
View file

@ -0,0 +1,34 @@
#ifndef LIGHT_H
#define LIGHT_H
#include "common/color.h"
#include "common/ray.h"
// Forward declarations
class Scene;
class Light {
public:
// Illumination object
struct Illumination {
Color color;
Vector3d direction;
};
// Constructor / Destructor
Light(float intensity, Color const &color = Color(1, 1, 1)) : color(color), intensity(intensity) {}
virtual ~Light() = default;
// Set
void setColor(Color const &color) { this->color = color; }
void setIntensity(float intensity) { this->intensity = intensity; }
// Light functions
virtual Illumination illuminate(Scene const &scene, Ray const &ray) const = 0;
protected:
Color color;
float intensity;
};
#endif

View file

@ -0,0 +1,45 @@
#include "primitive/infiniteplane.h"
#include <cmath>
// Constructor /////////////////////////////////////////////////////////////////
InfinitePlane::InfinitePlane(std::shared_ptr<Shader> const &shader) : Primitive(shader), normal(0, 1, 0) {}
InfinitePlane::InfinitePlane(Vector3d const &origin, Vector3d const &normal, std::shared_ptr<Shader> const &shader)
: Primitive(shader), origin(origin), normal(normal) {}
// Primitive functions /////////////////////////////////////////////////////////
bool InfinitePlane::intersect(Ray &ray) const {
// IMPLEMENT ME!
// Make sure the ray is not coming from the other side (backface culling).
// Note: We only use backface culling for InfinitePlanes, because we have
// some special features planned that rely on backfaces for other primitives.
// Determine whether the ray intersects the plane
// Test whether this is the foremost primitive in front of the camera
// (Optional for now) Set the normal
// Set the new length and the current primitive
return false;
}
// Bounding box ////////////////////////////////////////////////////////////////
float InfinitePlane::minimumBounds(int dimension) const {
if (this->normal[dimension] == 1.0f) // plane is orthogonal to the axis
return this->origin[dimension] - EPSILON;
else
return -INFINITY;
}
float InfinitePlane::maximumBounds(int dimension) const {
if (this->normal[dimension] == 1.0f) // plane is orthogonal to the axis
return this->origin[dimension] + EPSILON;
else
return +INFINITY;
}

28
primitive/infiniteplane.h Normal file
View file

@ -0,0 +1,28 @@
#ifndef INFINITEPLANE_H
#define INFINITEPLANE_H
#include "primitive/primitive.h"
class InfinitePlane : public Primitive {
public:
// Constructor
InfinitePlane(std::shared_ptr<Shader> const &shader);
InfinitePlane(Vector3d const &origin, Vector3d const &normal, std::shared_ptr<Shader> const &shader);
// Set
void setOrigin(Vector3d const &origin) { this->origin = origin; }
void setNormal(Vector3d const &normal) { this->normal = normalized(normal); }
// Primitive functions
bool intersect(Ray &ray) const override;
// Bounding box
float minimumBounds(int dimension) const override;
float maximumBounds(int dimension) const override;
protected:
Vector3d origin, normal;
};
#endif

30
primitive/primitive.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef PRIMITIVE_H
#define PRIMITIVE_H
#include "common/ray.h"
#include "shader/shader.h"
#include <algorithm>
#include <memory>
class Primitive {
public:
// Constructor / Destructor
Primitive(const std::shared_ptr<Shader> &shader) : shader_(shader) {}
virtual ~Primitive() = default;
// Get
std::shared_ptr<Shader> shader() const { return this->shader_; }
// Primitive functions
virtual bool intersect(Ray &ray) const = 0;
// Bounding box
virtual float minimumBounds(int dimension) const = 0;
virtual float maximumBounds(int dimension) const = 0;
private:
std::shared_ptr<Shader> shader_;
};
#endif

33
primitive/sphere.cpp Normal file
View file

@ -0,0 +1,33 @@
#include "common/ray.h"
#include "primitive/sphere.h"
// Constructor /////////////////////////////////////////////////////////////////
Sphere::Sphere(std::shared_ptr<Shader> const &shader) : Primitive(shader), radius(0.5f) {}
Sphere::Sphere(Vector3d const &center, float radius, std::shared_ptr<Shader> const &shader)
: Primitive(shader), center(center), radius(radius) {}
// Primitive functions /////////////////////////////////////////////////////////
bool Sphere::intersect(Ray &ray) const {
// IMPLEMENT ME!
// Determine whether the ray intersects the sphere
// Test whether this is the foremost primitive in front of the camera
// (Optional for now) Calculate the normal
// (Optional for now) Calculate the surface position
// Set the new length and the current primitive
return false;
}
// Bounding box ////////////////////////////////////////////////////////////////
float Sphere::minimumBounds(int dimension) const { return this->center[dimension] - this->radius; }
float Sphere::maximumBounds(int dimension) const { return this->center[dimension] + this->radius; }

29
primitive/sphere.h Normal file
View file

@ -0,0 +1,29 @@
#ifndef SPHERE_H
#define SPHERE_H
#include "primitive/primitive.h"
class Sphere : public Primitive {
public:
// Constructor
Sphere(std::shared_ptr<Shader> const &shader);
Sphere(Vector3d const &center, float radius, std::shared_ptr<Shader> const &shader);
// Set
void setCenter(Vector3d const &center) { this->center = center; }
void setRadius(float radius) { this->radius = radius; }
// Primitive functions
bool intersect(Ray &ray) const override;
// Bounding box
float minimumBounds(int dimension) const override;
float maximumBounds(int dimension) const override;
protected:
Vector3d center;
float radius;
};
#endif

47
primitive/triangle.cpp Normal file
View file

@ -0,0 +1,47 @@
#include "primitive/triangle.h"
#include <algorithm>
// Constructor /////////////////////////////////////////////////////////////////
Triangle::Triangle(std::shared_ptr<Shader> const &shader) : Primitive(shader) {}
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr<Shader> const &shader)
: Primitive(shader), vertex{a, b, c} {}
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb,
Vector3d const &nc, std::shared_ptr<Shader> const &shader)
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc} {}
Triangle::Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb,
Vector3d const &nc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc,
std::shared_ptr<Shader> const &shader)
: Primitive(shader), vertex{a, b, c}, normal{na, nb, nc}, surface{ta, tb, tc} {}
// Primitive functions /////////////////////////////////////////////////////////
bool Triangle::intersect(Ray &ray) const {
// IMPLEMENT ME!
// Determine whether the ray intersects the triangle
// Test whether this is the foremost primitive in front of the camera
// (Optional for now) Calculate the normal
// (Optional for now) Calculate the surface position
// Set the new length and the current primitive
return false;
}
// Bounding box ////////////////////////////////////////////////////////////////
float Triangle::minimumBounds(int dimension) const {
return std::min(this->vertex[0][dimension], std::min(this->vertex[1][dimension], this->vertex[2][dimension]));
}
float Triangle::maximumBounds(int dimension) const {
return std::max(this->vertex[0][dimension], std::max(this->vertex[1][dimension], this->vertex[2][dimension]));
}

41
primitive/triangle.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef TRIANGLE_H
#define TRIANGLE_H
#include "primitive/primitive.h"
class Triangle : public Primitive {
public:
// Constructor
Triangle(std::shared_ptr<Shader> const &shader);
Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, std::shared_ptr<Shader> const &shader);
Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb,
Vector3d const &nc, std::shared_ptr<Shader> const &shader);
Triangle(Vector3d const &a, Vector3d const &b, Vector3d const &c, Vector3d const &na, Vector3d const &nb,
Vector3d const &nc, Vector2d const &ta, Vector2d const &tb, Vector2d const &tc,
std::shared_ptr<Shader> const &shader);
// Set
void setVertex(int index, Vector3d const &vertex) { this->vertex[index] = vertex; }
void setNormal(int index, Vector3d const &normal) { this->normal[index] = normalized(normal); }
Vector3d getNormal(int index){return this->normal[index];}
void setTangent(int index, Vector3d const &tangent) { this->tangent[index] = normalized(tangent); }
void setBitangent(int index, Vector3d const &bitangent) { this->bitangent[index] = normalized(bitangent); }
void setSurface(int index, Vector2d const &surface) { this->surface[index] = surface; }
// Primitive functions
bool intersect(Ray &ray) const override;
// Bounding box
float minimumBounds(int dimension) const override;
float maximumBounds(int dimension) const override;
protected:
Vector3d vertex[3];
Vector3d normal[3];
Vector3d tangent[3];
Vector3d bitangent[3];
Vector2d surface[3];
};
#endif

21
renderer/renderer.h Normal file
View file

@ -0,0 +1,21 @@
#ifndef RENDERER_H
#define RENDERER_H
#include "common/texture.h"
// Forward declarations
class Camera;
class Scene;
class Renderer {
public:
// Constructor / Destructor
Renderer() = default;
virtual ~Renderer() = default;
// Render functions
virtual Texture renderImage(Scene const &scene, Camera const &camera, int width, int height) = 0;
};
#endif

View file

@ -0,0 +1,16 @@
#include "camera/camera.h"
#include "renderer/simplerenderer.h"
#include "scene/scene.h"
#include <iostream>
#include <chrono>
#include <iomanip>
Texture SimpleRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height);
// Calculate the aspect ration
// Create the image by casting one ray into the scene for each pixel
return image;
}

17
renderer/simplerenderer.h Normal file
View file

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

32
scene/scene.cpp Normal file
View file

@ -0,0 +1,32 @@
#include "light/light.h"
#include "primitive/triangle.h"
#include "scene/scene.h"
#include "shader/shader.h"
#include <array>
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
void Scene::add(const std::shared_ptr<Light> &light) { this->lights_.push_back(light); }
void Scene::add(const std::shared_ptr<Primitive> &primitive) {
assert(primitive->shader() != nullptr);
this->primitives_.push_back(primitive);
}
Color Scene::traceRay(Ray &ray) const {
if (this->findIntersection(ray) && ray.remainingBounces-- > 0) {
// If the ray has hit an object, call the shader ...
return ray.primitive->shader()->shade(*this, ray);
} else if (this->environmentMap) {
// ... otherwise look up the environment map ...
float const phi = std::acos(ray.direction.y);
float const rho = std::atan2(ray.direction.z, ray.direction.x) + float(PI);
return this->environmentMap->color(rho / (2.0f * float(PI)), phi / float(PI));
} else {
// ... if all else fails, just return the background color
return this->backgroundColor;
}
}

50
scene/scene.h Normal file
View file

@ -0,0 +1,50 @@
#ifndef SCENE_H
#define SCENE_H
#include "common/color.h"
#include "common/ray.h"
#include "common/texture.h"
#include "common/vector3d.h"
#include <algorithm>
#include <memory>
#include <vector>
// Forward declarations
class Light;
class Primitive;
class Shader;
class Scene {
public:
// Constructor / Destructor
Scene() = default;
virtual ~Scene() = default;
// Get
std::vector<std::shared_ptr<Light>> const &lights() const { return this->lights_; }
std::vector<std::shared_ptr<Primitive>> const &primitives() const { return this->primitives_; }
// Set
void setBackgroundColor(Color const &color) { this->backgroundColor = color; }
void setEnvironmentMap(std::shared_ptr<Texture> const &map) { this->environmentMap = map; }
// Setup functions
void add(const std::shared_ptr<Light> &light);
void add(const std::shared_ptr<Primitive> &primitive);
// Raytracing functions
Color traceRay(Ray &ray) const;
virtual bool findIntersection(Ray &ray) const = 0;
virtual bool findOcclusion(Ray &ray) const = 0;
protected:
Color backgroundColor;
std::shared_ptr<Texture> environmentMap;
private:
std::vector<std::shared_ptr<Light>> lights_;
std::vector<std::shared_ptr<Primitive>> primitives_;
};
#endif

17
scene/simplescene.cpp Normal file
View file

@ -0,0 +1,17 @@
#include "scene/simplescene.h"
#include "primitive/primitive.h"
#include "shader/shader.h"
bool SimpleScene::findIntersection(Ray &ray) const {
bool hit = false;
for (auto i : this->primitives())
hit |= i->intersect(ray);
return hit;
}
bool SimpleScene::findOcclusion(Ray &ray) const {
for (auto i : this->primitives())
if (i->intersect(ray) && !i->shader()->isTransparent())
return true;
return false;
}

14
scene/simplescene.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef SIMPLESCENE_H
#define SIMPLESCENE_H
#include "scene/scene.h"
class SimpleScene : public Scene {
public:
// Raytracing functions
bool findIntersection(Ray &ray) const override;
bool findOcclusion(Ray &ray) const override;
};
#endif

8
shader/flatshader.cpp Normal file
View file

@ -0,0 +1,8 @@
#include "shader/flatshader.h"
FlatShader::FlatShader(Color const &objectColor) : objectColor(objectColor) {}
Color FlatShader::shade(Scene const &scene, Ray const &ray) const {
(void)ray; // ray is unused in this case, but we do not want a warning.
return this->objectColor;
}

19
shader/flatshader.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef FLATSHADER_H
#define FLATSHADER_H
#include "shader/shader.h"
class FlatShader : public Shader {
public:
// Constructor
FlatShader(Color const &objectColor);
// Shader functions
virtual Color shade(Scene const &scene, Ray const &ray) const;
protected:
Color objectColor;
};
#endif

23
shader/shader.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef SHADER_H
#define SHADER_H
#include "common/color.h"
#include "common/ray.h"
// Forward declarations
class Scene;
class Shader {
public:
// Constructor / Desctructor
Shader() = default;
virtual ~Shader() = default;
// Get
virtual bool isTransparent() const { return false; }
// Shader functions
virtual Color shade(Scene const &scene, Ray const &ray) const = 0;
};
#endif