Summary Files Commits Branches @commit @diff @stats
diff options
context:
space:
mode:
Commit2a3a4296e747a5b15786646a1ec5142ad23331c2 (patch)
Author
 Syndamia <kamen@syndamia.com>
2024-05-27 18:18:30 +0300
Committer
 Syndamia <kamen@syndamia.com>
2024-05-27 18:18:30 +0300
Files504bb49625e48c216fa2a7d28b5cbac4a2f6b31e
uni-projects-2a3a4296e747a5b15786646a1ec5142ad23331c2.tar
uni-projects-2a3a4296e747a5b15786646a1ec5142ad23331c2.tar.gz
uni-projects-2a3a4296e747a5b15786646a1ec5142ad23331c2.zip
Parent1b7010f0a1095591bbe59b29908a6524af239a32 (diff)
[raytracing] Added solutions to raytracing exercises
diff --git a/Year 3/Raytracing/0004-h9-Added-Wireframe-shader.patch b/Year 3/Raytracing/0004-h9-Added-Wireframe-shader.patch
new file mode 100644
index 0000000..8c0d3f7
--- /dev/null
+++ b/Year 3/Raytracing/0004-h9-Added-Wireframe-shader.patch
@@ -0,0 +1,649 @@
+From f9cb883ef657806c5de5d5268856f740c151ff69 Mon Sep 17 00:00:00 2001
+From: Syndamia <kamen@syndamia.com>
+Date: Sun, 26 May 2024 14:42:39 +0300
+Subject: [PATCH] [h9] Added Wireframe shader
+
+---
+ data/hw9/dragon.hexray | 86 ++++++++++++++++++++++
+ data/hw9/nonconvex.hexray | 119 ++++++++++++++++++++++++++++++
+ data/hw9/testmesh.hexray | 150 ++++++++++++++++++++++++++++++++++++++
+ data/hw9/wireframe.hexray | 72 ++++++++++++++++++
+ flake.lock | 27 +++++++
+ flake.nix | 26 +++++++
+ src/geometry.h | 4 +-
+ src/main.cpp | 2 +-
+ src/mesh.cpp | 2 +
+ src/scene.cpp | 1 +
+ src/shading.cpp | 8 ++
+ src/shading.h | 20 +++++
+ 12 files changed, 515 insertions(+), 2 deletions(-)
+ create mode 100644 data/hw9/dragon.hexray
+ create mode 100644 data/hw9/nonconvex.hexray
+ create mode 100644 data/hw9/testmesh.hexray
+ create mode 100644 data/hw9/wireframe.hexray
+ create mode 100644 flake.lock
+ create mode 100644 flake.nix
+
+diff --git a/data/hw9/dragon.hexray b/data/hw9/dragon.hexray
+new file mode 100644
+index 0000000..ba924da
+--- /dev/null
++++ b/data/hw9/dragon.hexray
+@@ -0,0 +1,86 @@
++//
++// A scene with a "heavy" mesh (the stanford dragon)
++//
++//
++
++// 1. First, some global settings
++GlobalSettings {
++ frameWidth 768
++ frameHeight 512
++ ambientLight (0.28, 0.30, 0.35)
++ lightPos (160, 80, 40)
++ lightIntensity 5000
++}
++
++// 2. A camera
++Camera camera {
++ pos (140, 23, 70)
++ yaw 66
++ pitch -10
++ roll 0.0
++ fov 90
++ aspectRatio 1.5
++}
++
++Plane floor {
++ y 0
++ limit 128
++}
++
++CheckerTexture checker {
++ color1 (0.5, 0.5, 0.25)
++ color2 (0.25, 0.25, 0.25)
++ scaling 8
++}
++
++Lambert floorDiffuse {
++ color (1, 1, 1)
++ texture checker
++}
++
++Reflection floorMirror {
++ reflColor (0.05, 0.03, 0.03)
++}
++
++Reflection floorGlossy {
++ glossiness 0.50
++ numSamples 25
++ reflColor (0.06, 0.06, 0.10)
++}
++
++Layered floorShader {
++ layer floorMirror (1, 1, 1)
++ layer floorGlossy (0.05, 0.05, 0.075)
++}
++
++// 3. A floor node, using a plane as a geometry, and a flat shader with a checker texture
++Node floorNode {
++ geometry floor
++ shader floorGlossy
++ translate (100, 0, 96)
++}
++
++Mesh dragon {
++ useKDTree true
++ file "dragon.obj"
++ useSAH off
++}
++
++Phong white {
++ color (0.7, 0.7, 0.7)
++ exponent 133
++}
++
++Node dragonNode {
++ geometry dragon
++ shader white
++ translate (104, 13.44, 93)
++ rotate (90, 90, 0)
++ scale (30, 30, 30)
++}
++
++
++// 5. The cubemap environment:
++CubemapEnvironment environment {
++ folder "../env/forest"
++}
+diff --git a/data/hw9/nonconvex.hexray b/data/hw9/nonconvex.hexray
+new file mode 100644
+index 0000000..bb609ac
+--- /dev/null
++++ b/data/hw9/nonconvex.hexray
+@@ -0,0 +1,119 @@
++//
++// Sample scene with non-convex geometries
++//
++
++GlobalSettings {
++ frameWidth 640
++ frameHeight 480
++ ambientLight (0, 0, 0)
++ lightPos (-30, 100, -250)
++ lightIntensity 50000
++
++}
++
++Camera camera {
++ pos (0, 50, -50)
++ //pos (0, 100, -100)
++ aspectRatio 1.33333
++ yaw 0
++ pitch -30
++ roll 0
++ fov 90
++}
++
++Plane floor {
++ y 2
++}
++
++CheckerTexture checker {
++ color1 (0, 0, 0)
++ color2 (0, 0.5, 1)
++ size 5
++}
++
++Lambert floor_shader {
++ texture checker
++}
++
++Node floor {
++ geometry floor
++ shader floor_shader
++}
++
++
++Mesh 1 {
++ file "fig1.obj"
++ faceted true
++}
++
++Lambert L1 {
++ color (0.6, 0.5, 0.5)
++}
++
++Node 1 {
++ geometry 1
++ shader L1
++ scale (10, 10, 10)
++}
++
++/*
++Mesh 2 {
++ file "fig2.obj"
++ faceted true
++}
++
++Lambert L2 {
++ color (0.5, 0.7, 0.5)
++}
++
++Node 2 {
++ geometry 2
++ shader L2
++ scale (10, 10, 10)
++}
++
++Mesh 3 {
++ file "fig3.obj"
++ faceted true
++}
++
++Lambert L3 {
++ color (0.5, 0.5, 0.7)
++}
++
++Node 3 {
++ geometry 3
++ shader L3
++ scale (10, 10, 10)
++}
++
++Mesh 4 {
++ file "fig4.obj"
++ faceted true
++}
++
++Lambert L4 {
++ color (0.7, 0.5, 0.5)
++}
++
++Node 4 {
++ geometry 4
++ shader L4
++ scale (10, 10, 10)
++}
++
++Mesh 5 {
++ file "fig5.obj"
++ faceted true
++}
++
++Lambert L5 {
++ color (0.5, 0.7, 0.7)
++}
++
++Node 5 {
++ geometry 5
++ shader L5
++ scale (10, 10, 10)
++}
++*/
+diff --git a/data/hw9/testmesh.hexray b/data/hw9/testmesh.hexray
+new file mode 100644
+index 0000000..3ef4847
+--- /dev/null
++++ b/data/hw9/testmesh.hexray
+@@ -0,0 +1,150 @@
++//
++// An example scene, which represents the same scenery as what the initializeScene()
++// function used to create around the tag 'homework6'
++// Namely:
++// 1) A shiny floor, made of a woody Lambert shader and a mirror layer on top
++// 2) A soccer ball
++// 3) A shiny ball using a glossy shader
++// 4) Three small phong-lit balls, using red, yellow and green colors
++
++// 1. First, some global settings
++GlobalSettings {
++ frameWidth 640
++ frameHeight 480
++ ambientLight (0.08, 0.08, 0.08)
++ lightPos (-750, 1200, -750)
++ lightIntensity 1200000
++}
++
++// 2. A camera
++Camera camera {
++ pos (90, 160, -320)
++ aspectRatio 1.33333
++ yaw 15
++ pitch -5
++ roll 0
++ fov 75
++// focalPlaneDist 300
++// fNumber 2.0
++// dof false
++// numSamples 100
++}
++
++// 3. The floor node:
++Plane floor {
++ y -0.01
++ limit 200
++}
++
++BitmapTexture wood_tex {
++ file "../texture/wood.bmp"
++ scaling 400
++}
++
++Lambert floor_diffuse {
++ texture wood_tex
++}
++
++Reflection mirror {
++}
++
++Fresnel mirror_fresnel {
++ ior 1.33
++}
++
++Layered floor_shader {
++ layer floor_diffuse (1, 1, 1)
++ layer mirror (1, 1, 1) mirror_fresnel
++}
++
++Node floor {
++ geometry floor
++ shader floor_shader
++}
++
++Reflection refl {
++}
++
++Refraction refr {
++ ior 1.6
++}
++
++Fresnel fresnel {
++ ior 1.6
++}
++
++Layered glass {
++ layer refr (1, 1, 1)
++ layer refl (1, 1, 1) fresnel
++}
++
++// 4. The mesh:
++Mesh mesh {
++// file "geom/3pyramid.obj"
++// file "geom/teapot_lowres.obj"
++// file "geom/heart.obj"
++ file "../geom/newwine.obj"
++// file "geom/truncated_cube.obj"
++// backfaceCulling false
++ faceted false
++}
++
++CheckerTexture checker {
++ color1 (0.70, 0.70, 0.70)
++ color2 (0.15, 0.15, 0.15)
++ scaling 100
++}
++
++Lambert checker_shader {
++ texture checker
++}
++
++BitmapTexture heart_lava {
++ file "../texture/zar-texture.bmp"
++}
++
++Lambert heart_shader {
++ texture heart_lava
++}
++
++Reflection reflection {
++}
++
++Refraction refraction {
++ ior 1.5
++}
++
++Fresnel fresnel {
++ ior 1.5
++}
++
++Layered glass {
++ layer refraction (1, 1, 1)
++ layer reflection (1, 1, 1) fresnel
++}
++
++CheckerTexture checker1 {
++ color1 (0.2, 0.2, 0.2)
++ color2 (0.8, 0.8, 0.8)
++ scaling 10
++}
++
++Lambert lambert {
++ color (0.7, 0.7, 0.7)
++ texture checker
++}
++
++Node soccer_ball {
++ geometry mesh
++ shader glass
++ scale (230, 230, 230)
++ rotate (135, 0, 0)
++ translate (0, 0, 0)
++// bump bumptex
++}
++
++
++// 7. And, finally, an environment
++CubemapEnvironment {
++ folder "../env/forest"
++}
+diff --git a/data/hw9/wireframe.hexray b/data/hw9/wireframe.hexray
+new file mode 100644
+index 0000000..0f0a54b
+--- /dev/null
++++ b/data/hw9/wireframe.hexray
+@@ -0,0 +1,72 @@
++//
++// A sample scene for the homeworks for Lecture 9
++//
++
++// 1. First, some global settings
++GlobalSettings {
++ frameWidth 640
++ frameHeight 480
++ ambientLight (0.28, 0.30, 0.35)
++ lightPos (160, 80, 33)
++ lightIntensity 10000
++}
++
++// 2. A camera
++Camera camera {
++ pos (120, 33, 40)
++ yaw 10
++ pitch -15
++ roll 0.0
++ fov 90
++ aspectRatio 1.333
++}
++
++Plane floor {
++ y 0
++ limit 128
++}
++
++CheckerTexture checker {
++ color1 (0.5, 0.5, 0.25)
++ color2 (0.25, 0.25, 0.25)
++ scaling 8
++}
++
++Lambert floorShader {
++ color (1, 1, 1)
++ texture checker
++}
++
++// 3. A floor node, using a plane as a geometry, and a flat shader with a checker texture
++Node floorNode {
++ geometry floor
++ shader floorShader
++ translate (100, 0, 96)
++}
++
++Mesh heart {
++ file "../geom/heart.obj"
++}
++
++Lambert white_flat {
++ color (0.7, 0.7, 0.7)
++}
++
++Wireframe wireframe {
++ wireSize 0.08
++ wireColor (0, 1, 0)
++ outColor (0, 0, 0)
++}
++
++Node heartNode {
++ geometry heart
++ shader wireframe
++ translate (100, 12.1, 80)
++ rotate (120, 0, 0)
++ scale (6, 6, 6)
++}
++
++// 5. The cubemap environment:
++CubemapEnvironment environment {
++ folder "../env/forest"
++}
+diff --git a/flake.lock b/flake.lock
+new file mode 100644
+index 0000000..25db693
+--- /dev/null
++++ b/flake.lock
+@@ -0,0 +1,27 @@
++{
++ "nodes": {
++ "nixpkgs": {
++ "locked": {
++ "lastModified": 1716061101,
++ "narHash": "sha256-H0eCta7ahEgloGIwE/ihkyGstOGu+kQwAiHvwVoXaA0=",
++ "owner": "nixos",
++ "repo": "nixpkgs",
++ "rev": "e7cc61784ddf51c81487637b3031a6dd2d6673a2",
++ "type": "github"
++ },
++ "original": {
++ "owner": "nixos",
++ "ref": "nixos-23.11",
++ "repo": "nixpkgs",
++ "type": "github"
++ }
++ },
++ "root": {
++ "inputs": {
++ "nixpkgs": "nixpkgs"
++ }
++ }
++ },
++ "root": "root",
++ "version": 7
++}
+diff --git a/flake.nix b/flake.nix
+new file mode 100644
+index 0000000..d0a99fd
+--- /dev/null
++++ b/flake.nix
+@@ -0,0 +1,26 @@
++# Този файл, заедно с flake.lock са си за моета система, да имам копие ако случайно изтрия нещо
++{
++ description = "hexray raytracer in FMI elective";
++
++ inputs = {
++ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
++ };
++
++ outputs = inputs:
++ let
++ pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
++ in {
++ devShells."x86_64-linux".default = pkgs.mkShell {
++ nativeBuildInputs = with pkgs; [
++ cmake
++ pkg-config
++ ];
++
++ buildInputs = with pkgs; [
++ SDL2
++ openexr_3
++ zlib
++ ];
++ };
++ };
++}
+diff --git a/src/geometry.h b/src/geometry.h
+index 4af6360..e7b4a8c 100644
+--- a/src/geometry.h
++++ b/src/geometry.h
+@@ -37,6 +37,8 @@ struct IntersectionInfo {
+ Vector dNdx, dNdy;
+ double u, v;
+ Geometry *geom;
++ double lambda2;
++ double lambda3;
+ };
+
+ /**
+@@ -133,4 +135,4 @@ public:
+ class CSGDiff: public CSGBase { // A - B
+ public:
+ virtual bool inside(bool inA, bool inB) override { return inA && !inB; }
+-};
+\ No newline at end of file
++};
+diff --git a/src/main.cpp b/src/main.cpp
+index 780804c..93a18ea 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -183,7 +183,7 @@ bool renderStatic()
+ return render(true);
+ }
+
+-const char* DEFAULT_SCENE = "data/heightfield.hexray";
++const char* DEFAULT_SCENE = "data/hw9/wireframe.hexray";
+
+ int main(int argc, char** argv)
+ {
+diff --git a/src/mesh.cpp b/src/mesh.cpp
+index 2f6da05..86a6b0b 100644
+--- a/src/mesh.cpp
++++ b/src/mesh.cpp
+@@ -160,8 +160,10 @@ bool Mesh::intersectTriangle(const Ray& ray, const Triangle& t, IntersectionInfo
+ double rDcr = 1/Dcr;
+ double lambda2 = det(H, AC, -ray.dir) * rDcr;
+ if (lambda2 < 0 || lambda2 > 1) return false;
++ info.lambda2 = lambda2;
+ double lambda3 = det(AB, H, -ray.dir) * rDcr;
+ if (lambda3 < 0 || lambda3 > 1) return false;
++ info.lambda3 = lambda3;
+ double lambda1 = 1 - (lambda2 + lambda3);
+ if (lambda1 < 0 || lambda1 > 1) return false;
+ double gamma = det(AB, AC, H) * rDcr;
+diff --git a/src/scene.cpp b/src/scene.cpp
+index 149320e..5ac4633 100644
+--- a/src/scene.cpp
++++ b/src/scene.cpp
+@@ -804,6 +804,7 @@ SceneElement* DefaultSceneParser::newSceneElement(const char* className)
+ if (!strcmp(className, "Heightfield")) return new Heightfield;
+ if (!strcmp(className, "BumpTexture")) return new BumpTexture;
+ if (!strcmp(className, "Bumps")) return new Bumps;
++ if (!strcmp(className, "Wireframe")) return new Wireframe;
+ if (!strcmp(className, "Const")) return new Const;
+
+ return NULL;
+diff --git a/src/shading.cpp b/src/shading.cpp
+index 25f3c80..f4d3f0a 100644
+--- a/src/shading.cpp
++++ b/src/shading.cpp
+@@ -270,3 +270,11 @@ void Bumps::modifyNormal(IntersectionInfo& data)
+ data.norm.normalize();
+ }
+ }
++
++Color Wireframe::computeColor(Ray ray, const IntersectionInfo& info) {
++ double lambda1 = 1.0 - info.lambda2 - info.lambda3;
++ if (lambda1 < wireSize || info.lambda2 < wireSize || info.lambda3 < wireSize) {
++ return wireColor;
++ }
++ return outColor;
++}
+diff --git a/src/shading.h b/src/shading.h
+index 95e26c5..5a77b71 100644
+--- a/src/shading.h
++++ b/src/shading.h
+@@ -199,3 +199,23 @@ public:
+ }
+
+ };
++
++class Wireframe : public Shader {
++public:
++ double wireSize = 0.08;
++ Color wireColor = Color(0, 1, 0);
++ Color outColor = Color(0, 0, 0);
++ Wireframe(double wireSize = 0.08,
++ Color wireColor = Color(0, 1, 0),
++ Color outColor = Color(0, 0, 0)) :
++ wireSize(wireSize), wireColor(wireColor), outColor(outColor)
++ {}
++ Color computeColor(Ray ray, const IntersectionInfo& info) override;
++
++ void fillProperties(ParsedBlock& pb) override
++ {
++ pb.getDoubleProp("wireSize", &wireSize);
++ pb.getColorProp("wireColor", &wireColor);
++ pb.getColorProp("outColor", &outColor);
++ }
++};
+--
+2.42.0
+
diff --git a/Year 3/Raytracing/0005-h10-Implemented-SpotLight.patch b/Year 3/Raytracing/0005-h10-Implemented-SpotLight.patch
new file mode 100644
index 0000000..f1fbfd6
--- /dev/null
+++ b/Year 3/Raytracing/0005-h10-Implemented-SpotLight.patch
@@ -0,0 +1,223 @@
+From 7b19946dab9bc0399214a36dc9067f56c4e0d3a8 Mon Sep 17 00:00:00 2001
+From: Syndamia <kamen@syndamia.com>
+Date: Mon, 27 May 2024 09:22:23 +0300
+Subject: [PATCH] [h10] Implemented SpotLight
+
+---
+ data/boxed.hexray | 37 ++++++++++++++++++-------------------
+ flake.lock | 27 +++++++++++++++++++++++++++
+ flake.nix | 26 ++++++++++++++++++++++++++
+ src/lights.cpp | 17 +++++++++++++++++
+ src/lights.h | 19 +++++++++++++++++++
+ src/main.cpp | 2 +-
+ src/scene.cpp | 1 +
+ src/vector.h | 4 ++++
+ 8 files changed, 113 insertions(+), 20 deletions(-)
+ create mode 100644 flake.lock
+ create mode 100644 flake.nix
+
+diff --git a/data/boxed.hexray b/data/boxed.hexray
+index e128db3..9ed1793 100644
+--- a/data/boxed.hexray
++++ b/data/boxed.hexray
+@@ -18,25 +18,24 @@ Camera camera {
+ //////////////////////// LIGHTS ////////////////////////////////////////////
+
+
+-RectLight l2 {
+- xSubd 4
+- ySubd 4
+- translate (50, 50, -20)
+- scale (8, 8, 8)
+- color (1, 1, 1)
+- power 4000
+- rotate (0, 45, 0)
+-}
+-
+-RectLight l_small {
+- translate (0, 28, 50)
+- rotate (0, -62, 0)
+- scale (10, 10, 10)
+- color (1, 1, 1)
+- power 4000
+- xSubd 4
+- ySubd 4
+-}
++SpotLight mylight {
++ color (1, 1, 1)
++ power 7000
++ pos (50, 50, -20)
++ dir (-0.727, -0.582, 0.364)
++ innerAngle 13.8
++ outerAngle 15.5
++}
++
++// RectLight l_small {
++// translate (0, 28, 50)
++// rotate (0, -62, 0)
++// scale (10, 10, 10)
++// color (1, 1, 1)
++// power 4000
++// xSubd 4
++// ySubd 4
++// }
+
+
+ //////////////////////// GEOMETRIES ////////////////////////////////////////
+diff --git a/flake.lock b/flake.lock
+new file mode 100644
+index 0000000..25db693
+--- /dev/null
++++ b/flake.lock
+@@ -0,0 +1,27 @@
++{
++ "nodes": {
++ "nixpkgs": {
++ "locked": {
++ "lastModified": 1716061101,
++ "narHash": "sha256-H0eCta7ahEgloGIwE/ihkyGstOGu+kQwAiHvwVoXaA0=",
++ "owner": "nixos",
++ "repo": "nixpkgs",
++ "rev": "e7cc61784ddf51c81487637b3031a6dd2d6673a2",
++ "type": "github"
++ },
++ "original": {
++ "owner": "nixos",
++ "ref": "nixos-23.11",
++ "repo": "nixpkgs",
++ "type": "github"
++ }
++ },
++ "root": {
++ "inputs": {
++ "nixpkgs": "nixpkgs"
++ }
++ }
++ },
++ "root": "root",
++ "version": 7
++}
+diff --git a/flake.nix b/flake.nix
+new file mode 100644
+index 0000000..d0a99fd
+--- /dev/null
++++ b/flake.nix
+@@ -0,0 +1,26 @@
++# Този файл, заедно с flake.lock са си за моета система, да имам копие ако случайно изтрия нещо
++{
++ description = "hexray raytracer in FMI elective";
++
++ inputs = {
++ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
++ };
++
++ outputs = inputs:
++ let
++ pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
++ in {
++ devShells."x86_64-linux".default = pkgs.mkShell {
++ nativeBuildInputs = with pkgs; [
++ cmake
++ pkg-config
++ ];
++
++ buildInputs = with pkgs; [
++ SDL2
++ openexr_3
++ zlib
++ ];
++ };
++ };
++}
+diff --git a/src/lights.cpp b/src/lights.cpp
+index d1b5a42..f1e6406 100644
+--- a/src/lights.cpp
++++ b/src/lights.cpp
+@@ -44,3 +44,20 @@ void RectLight::getNthSample(int sampleIdx, const Vector& shadePos, Vector& samp
+ color.makeZero();
+ }
+ }
++
++void SpotLight::getNthSample(int sampleIdx, const Vector& shadePos, Vector& samplePos, Color& color) {
++ samplePos = pos;
++
++ Vector shadeDir = shadePos - pos;
++ double degr = degreesBetween(shadeDir, dir);
++ if (degr < innerAngle) {
++ color = this->color;
++ }
++ else if (degr > outerAngle) {
++ color = Color(0, 0, 0);
++ }
++ else {
++ double alpha = (degr - innerAngle) / (outerAngle - innerAngle);
++ color = (1 - alpha) * this->color + alpha * Color(0, 0, 0);
++ }
++}
+diff --git a/src/lights.h b/src/lights.h
+index 548b53f..c76b9be 100644
+--- a/src/lights.h
++++ b/src/lights.h
+@@ -79,3 +79,22 @@ public:
+ void getNthSample(int sampleIdx, const Vector& shadePos, Vector& samplePos, Color& color) override;
+ };
+
++
++class SpotLight : public Light {
++ Vector pos;
++ Vector dir;
++ double innerAngle;
++ double outerAngle;
++public:
++ void fillProperties(ParsedBlock& pb) override
++ {
++ Light::fillProperties(pb);
++ pb.getVectorProp("pos", &pos);
++ pb.getVectorProp("dir", &dir);
++ pb.getDoubleProp("innerAngle", &innerAngle);
++ pb.getDoubleProp("outerAngle", &outerAngle);
++ }
++
++ int getNumSamples() const override { return 1; }
++ void getNthSample(int sampleIdx, const Vector& shadePos, Vector& samplePos, Color& color) override;
++};
+diff --git a/src/main.cpp b/src/main.cpp
+index d523f60..7a181cb 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -222,7 +222,7 @@ bool renderStatic()
+ return render(true);
+ }
+
+-const char* DEFAULT_SCENE = "data/meshes.hexray";
++const char* DEFAULT_SCENE = "data/boxed.hexray";
+
+ int main(int argc, char** argv)
+ {
+diff --git a/src/scene.cpp b/src/scene.cpp
+index f5fbdb0..82c8d98 100644
+--- a/src/scene.cpp
++++ b/src/scene.cpp
+@@ -799,6 +799,7 @@ SceneElement* DefaultSceneParser::newSceneElement(const char* className)
+ if (!strcmp(className, "Const")) return new Const;
+ if (!strcmp(className, "PointLight")) return new PointLight;
+ if (!strcmp(className, "RectLight")) return new RectLight;
++ if (!strcmp(className, "SpotLight")) return new SpotLight;
+
+ return NULL;
+ }
+diff --git a/src/vector.h b/src/vector.h
+index a21b432..cd140fb 100644
+--- a/src/vector.h
++++ b/src/vector.h
+@@ -199,3 +199,7 @@ inline std::pair<Vector, Vector> othonormedBasis(const Vector& v)
+ result.second = result.first ^ v;
+ return result;
+ }
++
++inline double degreesBetween(const Vector& a, const Vector& b) {
++ return acos(dot(normalize(a), normalize(b))) * 180 / M_PI;
++}
+--
+2.42.0
+
diff --git a/Year 3/Raytracing/0006-h12-Implemented-Russian-roulette-raytracing.patch b/Year 3/Raytracing/0006-h12-Implemented-Russian-roulette-raytracing.patch
new file mode 100644
index 0000000..32372da
--- /dev/null
+++ b/Year 3/Raytracing/0006-h12-Implemented-Russian-roulette-raytracing.patch
@@ -0,0 +1,161 @@
+From 6cf50cd269931e95f058e17c15dc210fc49fa216 Mon Sep 17 00:00:00 2001
+From: Syndamia <kamen@syndamia.com>
+Date: Mon, 27 May 2024 17:46:57 +0300
+Subject: [PATCH] [h12] Implemented Russian-roulette raytracing
+
+---
+ data/smallpt.hexray | 1 +
+ flake.lock | 27 +++++++++++++++++++++++++++
+ flake.nix | 26 ++++++++++++++++++++++++++
+ src/main.cpp | 14 ++++++++++++--
+ src/scene.cpp | 1 +
+ src/scene.h | 3 +++
+ 6 files changed, 70 insertions(+), 2 deletions(-)
+ create mode 100644 flake.lock
+ create mode 100644 flake.nix
+
+diff --git a/data/smallpt.hexray b/data/smallpt.hexray
+index dfdc6e5..cdb0cf9 100644
+--- a/data/smallpt.hexray
++++ b/data/smallpt.hexray
+@@ -11,6 +11,7 @@ GlobalSettings {
+ gi on
+ numPaths 40
+ prepassSamples 300
++ roulette on
+ }
+
+ Camera camera {
+diff --git a/flake.lock b/flake.lock
+new file mode 100644
+index 0000000..25db693
+--- /dev/null
++++ b/flake.lock
+@@ -0,0 +1,27 @@
++{
++ "nodes": {
++ "nixpkgs": {
++ "locked": {
++ "lastModified": 1716061101,
++ "narHash": "sha256-H0eCta7ahEgloGIwE/ihkyGstOGu+kQwAiHvwVoXaA0=",
++ "owner": "nixos",
++ "repo": "nixpkgs",
++ "rev": "e7cc61784ddf51c81487637b3031a6dd2d6673a2",
++ "type": "github"
++ },
++ "original": {
++ "owner": "nixos",
++ "ref": "nixos-23.11",
++ "repo": "nixpkgs",
++ "type": "github"
++ }
++ },
++ "root": {
++ "inputs": {
++ "nixpkgs": "nixpkgs"
++ }
++ }
++ },
++ "root": "root",
++ "version": 7
++}
+diff --git a/flake.nix b/flake.nix
+new file mode 100644
+index 0000000..d0a99fd
+--- /dev/null
++++ b/flake.nix
+@@ -0,0 +1,26 @@
++# Този файл, заедно с flake.lock са си за моета система, да имам копие ако случайно изтрия нещо
++{
++ description = "hexray raytracer in FMI elective";
++
++ inputs = {
++ nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11";
++ };
++
++ outputs = inputs:
++ let
++ pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux";
++ in {
++ devShells."x86_64-linux".default = pkgs.mkShell {
++ nativeBuildInputs = with pkgs; [
++ cmake
++ pkg-config
++ ];
++
++ buildInputs = with pkgs; [
++ SDL2
++ openexr_3
++ zlib
++ ];
++ };
++ };
++}
+diff --git a/src/main.cpp b/src/main.cpp
+index 5af773d..47c0028 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -115,6 +115,16 @@ Color pathtrace(Ray ray, Color pathMultiplier = Color(1, 1, 1))
+ TraceContext tc;
+ auto earlyResult = tc.raycast(ray);
+ if (earlyResult) return (*earlyResult) * pathMultiplier;
++
++ double rating = pathMultiplier.intensity();
++ double rouletteContribution = 1.0;
++ if (scene.settings.roulette && ray.depth < 4 && rating < 1) {
++ double kill = randDouble();
++ if (kill <= 1 - rating) {
++ return Color(0, 0, 0);
++ }
++ rouletteContribution = 1 / rating;
++ }
+ // Continue building the path:
+ // Option A: continue randomly
+ Ray newRay = ray;
+@@ -123,7 +133,7 @@ Color pathtrace(Ray ray, Color pathMultiplier = Color(1, 1, 1))
+ newRay.depth++;
+ tc.closestNode->shader->spawnRay(tc.closestIntersection, ray.dir, newRay, brdfColor, brdfPDF);
+ if (brdfPDF <= 0) return Color(0, 0, 0);
+- Color fromGI = pathtrace(newRay, pathMultiplier * brdfColor / brdfPDF);
++ Color fromGI = pathtrace(newRay, pathMultiplier * brdfColor / brdfPDF) * rouletteContribution;
+ //
+ // Option B: explicit light sampling
+ // scheme:
+@@ -160,7 +170,7 @@ Color pathtrace(Ray ray, Color pathMultiplier = Color(1, 1, 1))
+ float pChooseLight = 1.0f / scene.lights.size();
+ float pHitLight = 1.0f / solidAngle;
+ float pThisPath = pChooseLight * pHitLight;
+- return fromGI + fromLight * pathMultiplier / pThisPath;
++ return fromGI + fromLight * pathMultiplier / pThisPath * rouletteContribution;
+ }
+
+ bool visible(Vector A, Vector B)
+diff --git a/src/scene.cpp b/src/scene.cpp
+index c909d0c..69f9206 100644
+--- a/src/scene.cpp
++++ b/src/scene.cpp
+@@ -772,6 +772,7 @@ void GlobalSettings::fillProperties(ParsedBlock& pb)
+ pb.getIntProp("prepassSamples", &prepassSamples, 0);
+ pb.getBoolProp("gi", &gi);
+ pb.getIntProp("numPaths", &numPaths, 1);
++ pb.getBoolProp("roulette", &roulette);
+ }
+
+ SceneElement* DefaultSceneParser::newSceneElement(const char* className)
+diff --git a/src/scene.h b/src/scene.h
+index 250d561..327c828 100644
+--- a/src/scene.h
++++ b/src/scene.h
+@@ -259,6 +259,9 @@ struct GlobalSettings: public SceneElement {
+ bool gi = false;
+ int numPaths = 32;
+
++ // Russian roulette-related
++ bool roulette;
++
+ void fillProperties(ParsedBlock& pb);
+ ElementType getElementType() const { return ELEM_SETTINGS; }
+ };
+--
+2.42.0
+