diff --git a/CMakeLists.txt b/CMakeLists.txt
index d3d976c..372156b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -74,6 +74,7 @@ endif()
include_directories(.)
#add_subdirectory(stream_compaction) # TODO: uncomment if using your stream compaction
add_subdirectory(src)
+add_subdirectory(tinyobjloader)
cuda_add_executable(${CMAKE_PROJECT_NAME}
"src/main.h"
@@ -83,6 +84,7 @@ cuda_add_executable(${CMAKE_PROJECT_NAME}
target_link_libraries(${CMAKE_PROJECT_NAME}
src
#stream_compaction # TODO: uncomment if using your stream compaction
+ tinyobj
${CORELIBS}
)
diff --git a/README.md b/README.md
index 110697c..941905c 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,138 @@ CUDA Path Tracer
**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3**
-* (TODO) YOUR NAME HERE
-* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
+* Ziad Ben Hadj-Alouane
+ * [LinkedIn](https://www.linkedin.com/in/ziadbha/), [personal website](https://www.seas.upenn.edu/~ziadb/)
+* Tested on: Windows 10, i7-8750H @ 2.20GHz, 16GB, GTX 1060
-### (TODO: Your README)
+# Project Goal
+
+
+
-*DO NOT* leave the README to the last minute! It is a crucial part of the
-project, and we will not be able to grade you without a good README.
+In this project, I implemented a Path Tracer using the CUDA parallel computing platform. Path Tracing is a computer graphics Monte Carlo method of rendering images of three-dimensional scenes such that the global illumination is faithful to reality.
+# Path Tracing Intro
+
+
+
+In simple terms, a path tracer fires rays from each pixel, which would bounce off in many directions depending on the objects in the scene. If the ray is fortunate enough, it would hit an illuminating surface (lightbulb, sun, etc...), and would cause the first object it hit to be illuminated, much like how light travels towards our eyes.
+
+## Issues
+Path tracing is computationally expensive since for each pixel, our rays might hit numerous geometries along the way. Checking intersections with each geometry is expensive, which is why we employ optimization techniques (culling, caching, stream compaction, material sorting) as well as use a parallel computing platform (CUDA) to take advantage of the GPUs cores.
+
+## Stream Compaction, Caching, & Sorting Optimizations
+### Stream Compaction
+[Stream Compaction](https://github.com/ziedbha/Project2-Stream-Compaction) helps by ommitting rays that aren't used in an iteration anymore. This happens when a ray hits nothing, or runs out of bounces (e.g hits a light source). This allows for a fewer blocks to launch, and less divergence in threads. The effects are great in different types of open scenes (tall, numerous materials, high-poly count) as shown below:
+
+
+However, the effects are less impressive when it comes to closed scenes, since stream compaction only comes in when rays hit a light source or natrually run out of permitted bounces. Overall, performance improves a lot with stream compaction.
+
+
+### Caching
+Since in the beginning of every iteration we fire the exact same ray from the exact same pixel, then we cache the intersection results so that we do not repeat them in subsequent iterations. Note that this feature does not work when anti-aliasing is enabled since anti-aliasing adds noise to the first rays, making them probabilistically unequal in subsequent iterations.
+
+### Material Sorting
+
+| Without Sorting | With Sorting |
+| ------------- | ----------- |
+| 
| 
|
+
+Before shading a ray, I performed an optimization that consisted in sorting the rays by the material type they hit. This allowed the CUDA warps (sets of threads) to diverge less in execution, saving more time. As the graphs above show, there are more branches in the unsorted case, and even more divergence as captured by the CUDA profiler.
+
+## 3D Model Importing (.obj)
+| Dedocahedron | Sword |
+| ------------- | ----------- |
+|  |  |
+
+To import 3D .obj files, I used TinyObjLoader which is a good parser for this specific file format. .obj files contain vertex information (positions, normals, texture coordinates) as well as triangulation information. Implementing this feature meant that I needed to store separate triangles for one mesh, with each triangle containing the correct vertex data.
+
+As expected, high poly-count meshes take longer to render (see below)
+
+| Wahoo | [Spider](https://poly.google.com/view/cbFePDoI8yi) |
+| ------------- | ----------- |
+|  |  |
+
+### Performance & Optimization
+Since GPUs aren't able to use resizable arrays, I couldn't store vectors of triangles for each mesh. Instead, I loaded my meshes this way:
+
+| Mesh 1 | Triangle 1-1 | Triangle 1-... | Triangle 1-n | ... | Mesh 2 | Triangle 2-1 | Triangle 2- ...| Triangle 2-m |
+| ------------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- | ----------- |
+| End Index of Triangle data | Vertex data | Vertex data | Vertex data | ... | End Index of Triangle data | Vertex data | Vertex data | Vertex data |
+
+So that when I tested intersections against a mesh, I would access the next index to find the first triangle, and I would access the end index to find the last triangle of the mesh.
+
+To test intersections against the mesh, I did two optimizations:
+1. *Bounding-Volume Culling*: I compute the bounds of the mesh and surround it by a bounding box. Using this bounding box, I can check if a ray can potentially hit the mesh. If it can't, then no intersection is possible, so we skip it. Else, we test intersections against all triangles.
+2. *Deferred Intersection Calculations*: I calculate intersections against triangles in a mesh in one giant batch. In other words, I only keep the closest triangle I intersected for a ray, and THEN perform all needed calculations. This significantly improves the performance of mesh intersection since for high poly-count meshes, simple calculations such as point retrieval become expensive.
+
+## Anti-Aliasing
+| Without Anti-Aliasing | With Anti-Aliasing |
+| ------------- | ----------- |
+|  |  |
+
+In computer graphics, anti-aliasing is a software technique used to diminish stair-step like lines that should be smooth instead. This usually occurs when the resolution isn't high enough. In path tracing, we apply anti-aliasing by firing the ray with additional noise. If the ray is supposed to color pixel (x,y), we sample more colors around that pixel, and average them out to color pixel (x,y).
+
+The picture below explains the approach. My implementation is represented by the image on the right. A dot is a sample (ray), a square is a pixel.
+
+
+
+
+
+### Performance & Optimization
+Anti-Aliasing has no impact on performance since it is essentially 2 more lines of code per ray. All rays do the same anti-aliasing computation, which means that no warps diverge. No further optimization needed.
+
+## Depth of Field
+
+| Without Depth of Field | With Depth of Field |
+| ------------- | ----------- |
+|  |  |
+
+In optics, depth of field is the distance about the plane of focus where objects appear acceptably sharp in an image. In path tracing, this is achieved by adding noise to the ray direction about the focal point, as explained in this [article](https://medium.com/@elope139/depth-of-field-in-path-tracing-e61180417027).
+
+### Performance & Optimization
+Depth of Field is just a few extra vector computations per ray, and much like anti-aliasing, it has no impact on performance. All rays do the same computation, which means that no warps diverge. No further optimization needed.
+
+
+## Materials Support
+
+
+
+My implementation supports diffuse and specular materials. For specular materials, I support pure refractive, reflective, and transmissive materials.
+
+### Diffuse
+
+
+
+
+### Specular Reflective
+
+
+
+
+### Specular Refractive
+
+
+
+
+### Transmissive using Schlick's Approximation
+
+
+
+
+# Bloopers & Bugs
+| Weak Seed for Randomly Generated Numbers | Incorrect Access of Path Index in a Thread | Missing Internal Total Reflection & Wrong Normals |
+| ------------- | ----------- | ------------- |
+| 
| 
| 
|
+
+# Build Instructions
+1. Install [CMake](https://cmake.org/install/)
+2. Install [Visual Studio 2015](https://docs.microsoft.com/en-us/visualstudio/welcome-to-visual-studio-2015?view=vs-2015) with C++ compativility
+3. Install [CUDA 8](https://developer.nvidia.com/cuda-80-ga2-download-archive) (make sure your GPU supports this)
+4. Clone this Repo
+5. Create a folder named "build" in the root of the local repo
+6. Navigate to the build folder and run "cmake-gui .." in a CLI
+7. Configure the build with Visual Studio 14 2015 Win64, then generate the solution
+8. Run the solution using Visual Studio 2015
+10. Build cis_565_path_tracer and set it as Startup Project
+9. Run cis_565_path_tracer with command line arguments: ../scenes/name_of_scene.txt
diff --git a/images/aa.png b/images/aa.png
new file mode 100644
index 0000000..f652a93
Binary files /dev/null and b/images/aa.png differ
diff --git a/images/aa_exp.png b/images/aa_exp.png
new file mode 100644
index 0000000..0d33e01
Binary files /dev/null and b/images/aa_exp.png differ
diff --git a/images/aa_none.png b/images/aa_none.png
new file mode 100644
index 0000000..61b910f
Binary files /dev/null and b/images/aa_none.png differ
diff --git a/images/deca.png b/images/deca.png
new file mode 100644
index 0000000..51372e4
Binary files /dev/null and b/images/deca.png differ
diff --git a/images/diffuse.png b/images/diffuse.png
new file mode 100644
index 0000000..cb8a197
Binary files /dev/null and b/images/diffuse.png differ
diff --git a/images/dof.png b/images/dof.png
new file mode 100644
index 0000000..e6ed1ec
Binary files /dev/null and b/images/dof.png differ
diff --git a/images/dof_none.png b/images/dof_none.png
new file mode 100644
index 0000000..0b49c2e
Binary files /dev/null and b/images/dof_none.png differ
diff --git a/images/explanation.png b/images/explanation.png
new file mode 100644
index 0000000..9c3c692
Binary files /dev/null and b/images/explanation.png differ
diff --git a/images/mix.png b/images/mix.png
new file mode 100644
index 0000000..4630ce2
Binary files /dev/null and b/images/mix.png differ
diff --git a/images/no_sort.png b/images/no_sort.png
new file mode 100644
index 0000000..4e949a5
Binary files /dev/null and b/images/no_sort.png differ
diff --git a/images/open_vs_closed_compact.png b/images/open_vs_closed_compact.png
new file mode 100644
index 0000000..07ae80f
Binary files /dev/null and b/images/open_vs_closed_compact.png differ
diff --git a/images/reflect.png b/images/reflect.png
new file mode 100644
index 0000000..f2095b7
Binary files /dev/null and b/images/reflect.png differ
diff --git a/images/refract.png b/images/refract.png
new file mode 100644
index 0000000..cd4010d
Binary files /dev/null and b/images/refract.png differ
diff --git a/images/refraction_bug.png b/images/refraction_bug.png
new file mode 100644
index 0000000..8375b36
Binary files /dev/null and b/images/refraction_bug.png differ
diff --git a/images/seed_bug.png b/images/seed_bug.png
new file mode 100644
index 0000000..531e14b
Binary files /dev/null and b/images/seed_bug.png differ
diff --git a/images/spider.png b/images/spider.png
new file mode 100644
index 0000000..8ce8cc8
Binary files /dev/null and b/images/spider.png differ
diff --git a/images/stream_compaction_bug.png b/images/stream_compaction_bug.png
new file mode 100644
index 0000000..ac30105
Binary files /dev/null and b/images/stream_compaction_bug.png differ
diff --git a/images/stream_compaction_scenes.png b/images/stream_compaction_scenes.png
new file mode 100644
index 0000000..3988353
Binary files /dev/null and b/images/stream_compaction_scenes.png differ
diff --git a/images/sword.png b/images/sword.png
new file mode 100644
index 0000000..03d9652
Binary files /dev/null and b/images/sword.png differ
diff --git a/images/top.png b/images/top.png
new file mode 100644
index 0000000..4525a19
Binary files /dev/null and b/images/top.png differ
diff --git a/images/transmit.png b/images/transmit.png
new file mode 100644
index 0000000..ce4f4ed
Binary files /dev/null and b/images/transmit.png differ
diff --git a/images/wahoo.png b/images/wahoo.png
new file mode 100644
index 0000000..11bf00c
Binary files /dev/null and b/images/wahoo.png differ
diff --git a/images/with_sort.png b/images/with_sort.png
new file mode 100644
index 0000000..25cc588
Binary files /dev/null and b/images/with_sort.png differ
diff --git a/scenes/cornell.txt b/scenes/cornell.txt
index 83ff820..3734015 100644
--- a/scenes/cornell.txt
+++ b/scenes/cornell.txt
@@ -38,12 +38,33 @@ REFR 0
REFRIOR 0
EMITTANCE 0
-// Specular white
+// Specular blue (refr + ref)
MATERIAL 4
+RGB .8 .98 .98
+SPECEX 0
+SPECRGB .8 .98 .98
+REFL 0.5
+REFR 0.3
+REFRIOR 1.4
+EMITTANCE 0
+
+// Specular blue (ref only)
+MATERIAL 5
+RGB .8 .98 .98
+SPECEX 0
+SPECRGB .8 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+
+// Diffuse White
+MATERIAL 5
RGB .98 .98 .98
SPECEX 0
SPECRGB .98 .98 .98
-REFL 1
+REFL 0
REFR 0
REFRIOR 0
EMITTANCE 0
@@ -58,6 +79,8 @@ FILE cornell
EYE 0.0 5 10.5
LOOKAT 0 5 0
UP 0 1 0
+FOCAL 10
+LENSR 0.5
// Ceiling light
@@ -115,3 +138,12 @@ material 4
TRANS -1 4 -1
ROTAT 0 0 0
SCALE 3 3 3
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS 4 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
diff --git a/scenes/cube.txt b/scenes/cube.txt
new file mode 100644
index 0000000..c13060f
--- /dev/null
+++ b/scenes/cube.txt
@@ -0,0 +1,176 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 4
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse blue
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse 1
+MATERIAL 5
+RGB .50 .98 .5
+SPECEX 0
+SPECRGB .50 .98 .5
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse 1
+MATERIAL 6
+RGB .2 .2 .98
+SPECEX 0
+SPECRGB .2 .2 .98
+REFL 0.5
+REFR 0.6
+REFRIOR 1.3
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 7
+RGB .8 .0 .0
+SPECEX 0
+SPECRGB .98 .7 .8
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse yellow
+MATERIAL 8
+RGB .8 .8 .3
+SPECEX 0
+SPECRGB .98 .98 .0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 6 .3 6
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Mesh
+OBJECT 6
+mesh ../models/cube.obj
+material 6
+TRANS -4 6 -2
+ROTAT 0 45 45
+SCALE 1 1 1
+
+
+// Mesh
+OBJECT 7
+mesh ../models/cube.obj
+material 7
+TRANS 0 4 -2
+ROTAT 0 50 0
+SCALE 1 2 1
+
+// Mesh
+OBJECT 8
+mesh ../models/cube.obj
+material 8
+TRANS -2 2 0
+ROTAT 0 30 0
+SCALE 1 1 2
diff --git a/scenes/diffuse.txt b/scenes/diffuse.txt
new file mode 100644
index 0000000..327f646
--- /dev/null
+++ b/scenes/diffuse.txt
@@ -0,0 +1,135 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Refractive white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 5
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 5
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Cube
+OBJECT 6
+cube
+material 4
+TRANS -2 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Cube
+OBJECT 6
+sphere
+material 4
+TRANS 2 3 0
+ROTAT 0 0 0
+SCALE 4 4 4
diff --git a/scenes/dodecahedron.txt b/scenes/dodecahedron.txt
new file mode 100644
index 0000000..3e6235a
--- /dev/null
+++ b/scenes/dodecahedron.txt
@@ -0,0 +1,153 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 0.2 0.2 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Specular
+MATERIAL 5
+RGB .98 .98 .2
+SPECEX 0
+SPECRGB .98 .98 .2
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Mesh
+OBJECT 6
+mesh ../models/dodecahedron.obj
+material 5
+TRANS 0 4 -1
+ROTAT 0 20 10
+SCALE 2 2 2
+
+// Ceiling light
+OBJECT 7
+cube
+material 0
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 8
+cube
+material 0
+TRANS -5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 9
+cube
+material 0
+TRANS 5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
\ No newline at end of file
diff --git a/scenes/excalibur.txt b/scenes/excalibur.txt
new file mode 100644
index 0000000..701b233
--- /dev/null
+++ b/scenes/excalibur.txt
@@ -0,0 +1,153 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 0.2 0.2 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Specular
+MATERIAL 5
+RGB .7 .7 .2
+SPECEX 0
+SPECRGB .7 .7 .2
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 30
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 20 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 20 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 40 40
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 40 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 40 10
+
+// Mesh
+OBJECT 6
+mesh ../models/sword.obj
+material 5
+TRANS 0 5 5
+ROTAT 0 20 10
+SCALE 0.5 0.5 0.5
+
+// Ceiling light
+OBJECT 7
+cube
+material 0
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 8
+cube
+material 0
+TRANS -5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 9
+cube
+material 0
+TRANS 5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
\ No newline at end of file
diff --git a/scenes/mix.txt b/scenes/mix.txt
new file mode 100644
index 0000000..338e87d
--- /dev/null
+++ b/scenes/mix.txt
@@ -0,0 +1,181 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Transimissive white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0.7
+REFR 0.3
+REFRIOR 1.45
+EMITTANCE 0
+
+// Refr
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0
+REFR 1
+REFRIOR 1.7
+EMITTANCE 0
+
+// Refl
+MATERIAL 6
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse
+MATERIAL 7
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 8
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 30
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 8
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 9
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Sphere
+OBJECT 6
+sphere
+material 4
+TRANS 2 4 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS -2 4 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 8
+sphere
+material 6
+TRANS 0 1 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 9
+sphere
+material 7
+TRANS 0 7 0
+ROTAT 0 0 0
+SCALE 4 4 4
\ No newline at end of file
diff --git a/scenes/mix_closed.txt b/scenes/mix_closed.txt
new file mode 100644
index 0000000..a9869f7
--- /dev/null
+++ b/scenes/mix_closed.txt
@@ -0,0 +1,189 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Transimissive white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0.7
+REFR 0.3
+REFRIOR 1.45
+EMITTANCE 0
+
+// Refr
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0
+REFR 1
+REFRIOR 1.7
+EMITTANCE 0
+
+// Refl
+MATERIAL 6
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse
+MATERIAL 7
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 8
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 30
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 8
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 9
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Sphere
+OBJECT 6
+sphere
+material 4
+TRANS 2 4 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS -2 4 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 8
+sphere
+material 6
+TRANS 0 1 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 9
+sphere
+material 7
+TRANS 0 7 0
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Front wall
+OBJECT 10
+cube
+material 1
+TRANS 0 5 5
+ROTAT 0 90 0
+SCALE .01 10 10
\ No newline at end of file
diff --git a/scenes/reflect.txt b/scenes/reflect.txt
new file mode 100644
index 0000000..4530bb1
--- /dev/null
+++ b/scenes/reflect.txt
@@ -0,0 +1,135 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Reflective white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 5
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 5
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Cube
+OBJECT 6
+cube
+material 4
+TRANS -2 4 -1
+ROTAT 0 45 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 6
+sphere
+material 4
+TRANS 2 3 0
+ROTAT 0 0 0
+SCALE 4 4 4
diff --git a/scenes/refract.txt b/scenes/refract.txt
new file mode 100644
index 0000000..84677f9
--- /dev/null
+++ b/scenes/refract.txt
@@ -0,0 +1,135 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Refractive white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0
+REFR 1
+REFRIOR 1.45
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 5
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 5
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Cube
+OBJECT 6
+cube
+material 4
+TRANS -2 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Cube
+OBJECT 6
+sphere
+material 4
+TRANS 2 3 0
+ROTAT 0 0 0
+SCALE 4 4 4
diff --git a/scenes/sphere.txt b/scenes/sphere.txt
index a74b545..0bc2bc4 100644
--- a/scenes/sphere.txt
+++ b/scenes/sphere.txt
@@ -13,7 +13,7 @@ CAMERA
RES 800 800
FOVY 45
ITERATIONS 5000
-DEPTH 8
+DEPTH 30
FILE sphere
EYE 0.0 5 10.5
LOOKAT 0 5 0
@@ -25,4 +25,4 @@ sphere
material 0
TRANS 0 0 0
ROTAT 0 0 0
-SCALE 3 3 3
+SCALE 6 6 6
diff --git a/scenes/sphere_and_cube.txt b/scenes/sphere_and_cube.txt
new file mode 100644
index 0000000..4c64953
--- /dev/null
+++ b/scenes/sphere_and_cube.txt
@@ -0,0 +1,184 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse 1
+MATERIAL 5
+RGB .50 .98 .0
+SPECEX 0
+SPECRGB .50 .98 .0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse 1
+MATERIAL 6
+RGB .50 .50 .98
+SPECEX 0
+SPECRGB .50 .50 .98
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Specular blue (refr + ref)
+MATERIAL 7
+RGB .8 .98 .98
+SPECEX 0
+SPECRGB .8 .98 .98
+REFL 0.5
+REFR 0.3
+REFRIOR 1.4
+EMITTANCE 0
+
+// Specular red (refr + ref)
+MATERIAL 8
+RGB .50 .98 .3
+SPECEX 0
+SPECRGB .50 .98 .3
+REFL 0.6
+REFR 0.2
+REFRIOR 1.5
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Mesh
+OBJECT 6
+mesh ../models/cube.obj
+material 6
+TRANS 2 4 -1
+ROTAT 0 -45 0
+SCALE 1 1 1
+
+// Mesh
+OBJECT 7
+mesh ../models/cube.obj
+material 6
+TRANS -4 4 -2
+ROTAT 0 45 0
+SCALE 1 1 1
+
+
+// Sphere
+OBJECT 8
+sphere
+material 7
+TRANS 0 4 -5
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Sphere
+OBJECT 9
+sphere
+material 8
+TRANS -1 2 -3
+ROTAT 0 0 0
+SCALE 4 4 4
diff --git a/scenes/spider.txt b/scenes/spider.txt
new file mode 100644
index 0000000..25d5f45
--- /dev/null
+++ b/scenes/spider.txt
@@ -0,0 +1,153 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 0.2 0.2 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Specular
+MATERIAL 5
+RGB .98 .01 .01
+SPECEX 0
+SPECRGB .98 .01 .01
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Mesh
+OBJECT 6
+mesh ../models/spider.obj
+material 5
+TRANS 0 4 -1
+ROTAT 0 45 0
+SCALE 3 3 3
+
+// Ceiling light
+OBJECT 7
+cube
+material 0
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 8
+cube
+material 0
+TRANS -5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
+
+// Ceiling light
+OBJECT 9
+cube
+material 0
+TRANS 5 5 0
+ROTAT 0 0 90
+SCALE 5 .3 5
\ No newline at end of file
diff --git a/scenes/transmit.txt b/scenes/transmit.txt
new file mode 100644
index 0000000..9f7df72
--- /dev/null
+++ b/scenes/transmit.txt
@@ -0,0 +1,135 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Transimissive white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 0.7
+REFR 0.3
+REFRIOR 1.45
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 5
+RGB .98 .50 .50
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 5
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Cube
+OBJECT 6
+cube
+material 4
+TRANS -2 4 -1
+ROTAT 0 45 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 6
+sphere
+material 4
+TRANS 2 3 0
+ROTAT 0 0 0
+SCALE 4 4 4
diff --git a/scenes/wahoo.txt b/scenes/wahoo.txt
new file mode 100644
index 0000000..92aeea9
--- /dev/null
+++ b/scenes/wahoo.txt
@@ -0,0 +1,130 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 5
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse green
+MATERIAL 3
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// neutral
+MATERIAL 4
+RGB 1 1 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Specular red
+MATERIAL 5
+RGB .98 .2 .7
+SPECEX 0
+SPECRGB .98 .2 .7
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+
+// Camera
+CAMERA
+RES 800 800
+FOVY 45
+ITERATIONS 5000
+DEPTH 30
+FILE cornell
+EYE 0.0 5 10.5
+LOOKAT 0 5 0
+UP 0 1 0
+FOCAL 10
+LENSR 0.5
+
+
+// Ceiling light
+OBJECT 0
+cube
+material 0
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 3 .3 3
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 0 0
+ROTAT 0 0 0
+SCALE 10 .01 10
+
+// Ceiling
+OBJECT 2
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 90
+SCALE .01 10 10
+
+// Back wall
+OBJECT 3
+cube
+material 1
+TRANS 0 5 -5
+ROTAT 0 90 0
+SCALE .01 10 10
+
+// Left wall
+OBJECT 4
+cube
+material 2
+TRANS -5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Right wall
+OBJECT 5
+cube
+material 3
+TRANS 5 5 0
+ROTAT 0 0 0
+SCALE .01 10 10
+
+// Mesh
+OBJECT 6
+mesh ../models/wahoo.obj
+material 5
+TRANS 0 4 0
+ROTAT 0 0 0
+SCALE 1 1 1
diff --git a/src/interactions.h b/src/interactions.h
index 5ce3628..97528d1 100644
--- a/src/interactions.h
+++ b/src/interactions.h
@@ -2,7 +2,27 @@
#include "intersections.h"
-// CHECKITOUT
+/**
+ * Maps a (u,v) in [0, 1)^2 to a 2D unit disk centered at (0,0). Based on PBRT
+ */
+__host__ __device__
+glm::vec2 calculateConcentricSampleDisk(float u, float v) {
+ glm::vec2 uOffset = 2.0f * glm::vec2(u, v) - glm::vec2(1, 1);
+ if (uOffset.x == 0 && uOffset.y == 0) {
+ return glm::vec2(0.0f);
+ }
+
+ float theta, r;
+ if (glm::abs(uOffset.x) > glm::abs(uOffset.y)) {
+ r = uOffset.x;
+ theta = PI / 4 * (uOffset.y / uOffset.x);
+ }
+ else {
+ r = uOffset.y;
+ theta = (PI / 2) - (PI / 4 * (uOffset.x / uOffset.y));
+ }
+ return r * glm::vec2(glm::cos(theta), glm::sin(theta));
+}
/**
* Computes a cosine-weighted random direction in a hemisphere.
* Used for diffuse lighting.
@@ -68,12 +88,53 @@ glm::vec3 calculateRandomDirectionInHemisphere(
*/
__host__ __device__
void scatterRay(
- PathSegment & pathSegment,
- glm::vec3 intersect,
- glm::vec3 normal,
- const Material &m,
- thrust::default_random_engine &rng) {
- // TODO: implement this.
- // A basic implementation of pure-diffuse shading will just call the
- // calculateRandomDirectionInHemisphere defined above.
+ PathSegment & pathSegment,
+ glm::vec3 intersect,
+ glm::vec3 normal,
+ const Material &m,
+ thrust::default_random_engine &rng)
+{
+ glm::vec3 newDir(0);
+ thrust::uniform_real_distribution u01(0, 1);
+ float p = u01(rng);
+
+ if (m.hasRefractive > p) {
+ // adjust eta & normal according to direction of ray (inside or outside mat)
+ bool inside = glm::dot(pathSegment.ray.direction, normal) > 0.f;
+ glm::vec3 tempNormal = normal * (inside ? -1.0f : 1.0f);
+ float eta = inside ? m.indexOfRefraction : (1.0f / m.indexOfRefraction);
+
+ // normal refraction
+ newDir = glm::refract(pathSegment.ray.direction, tempNormal, eta);
+
+ // internal total reflection
+ if (glm::length(newDir) < 0.01f) {
+ pathSegment.color *= 0;
+ newDir = glm::reflect(pathSegment.ray.direction, normal);
+ }
+
+ // use schlick's approx
+ float schlick_0 = powf((inside ? m.indexOfRefraction - 1.0f : 1.0f - m.indexOfRefraction) /
+ (1.0f + m.indexOfRefraction), 2.0f);
+ float schlick_coef = schlick_0 +
+ (1 - schlick_0) * powf(1 - max(0.0f, glm::dot(pathSegment.ray.direction, normal)), 5);
+
+ // based on coef, pick either a refraction or reflection
+ newDir = schlick_coef < u01(rng) ? glm::reflect(pathSegment.ray.direction, normal) : newDir;
+ pathSegment.color *= m.specular.color;
+ }
+ else if (m.hasReflective > p) {
+ // reflection
+ newDir = glm::reflect(pathSegment.ray.direction, normal);
+ pathSegment.color *= m.specular.color;
+ }
+ else {
+ // diffuse
+ newDir = calculateRandomDirectionInHemisphere(normal, rng);
+ pathSegment.color *= m.color;
+
+ }
+
+ pathSegment.ray.direction = newDir;
+ pathSegment.ray.origin = intersect + pathSegment.ray.direction * 0.01f;
}
diff --git a/src/intersections.h b/src/intersections.h
index 6f23872..f81135d 100644
--- a/src/intersections.h
+++ b/src/intersections.h
@@ -19,7 +19,6 @@ __host__ __device__ inline unsigned int utilhash(unsigned int a) {
return a;
}
-// CHECKITOUT
/**
* Compute a point at parameter value `t` on ray `r`.
* Falls slightly short so that it doesn't intersect the object it's hitting.
@@ -35,7 +34,6 @@ __host__ __device__ glm::vec3 multiplyMV(glm::mat4 m, glm::vec4 v) {
return glm::vec3(m * v);
}
-// CHECKITOUT
/**
* Test intersection between a ray and a transformed cube. Untransformed,
* the cube ranges from -0.5 to 0.5 in each axis and is centered at the origin.
@@ -89,7 +87,6 @@ __host__ __device__ float boxIntersectionTest(Geom box, Ray r,
return -1;
}
-// CHECKITOUT
/**
* Test intersection between a ray and a transformed sphere. Untransformed,
* the sphere always has radius 0.5 and is centered at the origin.
@@ -113,6 +110,7 @@ __host__ __device__ float sphereIntersectionTest(Geom sphere, Ray r,
float vDotDirection = glm::dot(rt.origin, rt.direction);
float radicand = vDotDirection * vDotDirection - (glm::dot(rt.origin, rt.origin) - powf(radius, 2));
if (radicand < 0) {
+ //no solution
return -1;
}
@@ -123,6 +121,7 @@ __host__ __device__ float sphereIntersectionTest(Geom sphere, Ray r,
float t = 0;
if (t1 < 0 && t2 < 0) {
+ // no intersection
return -1;
} else if (t1 > 0 && t2 > 0) {
t = min(t1, t2);
@@ -142,3 +141,96 @@ __host__ __device__ float sphereIntersectionTest(Geom sphere, Ray r,
return glm::length(r.origin - intersectionPoint);
}
+
+/**
+* Test intersection between a ray and a transformed triangle.
+*
+* @param intersectionPoint Output parameter for point of intersection.
+* @param normal Output parameter for surface normal.
+* @param outside Output param for whether the ray came from outside.
+* @return Ray parameter `t` value. -1 if no intersection.
+*/
+__host__ __device__ float triIntersectionTest(Geom triangle, Ray r,
+ glm::vec3 &intersectionPoint, glm::vec3 &normal, bool &outside) {
+
+ glm::vec3 ro = multiplyMV(triangle.inverseTransform, glm::vec4(r.origin, 1.0f));
+ glm::vec3 rd = glm::normalize(multiplyMV(triangle.inverseTransform, glm::vec4(r.direction, 0.0f)));
+
+ Ray rt;
+ rt.origin = ro;
+ rt.direction = rd;
+
+ glm::vec3 hit;
+ glm::intersectRayTriangle(rt.origin, rt.direction, triangle.pos[0], triangle.pos[1], triangle.pos[2], hit);
+ float t = hit.z;
+
+ glm::vec3 objspaceIntersection = getPointOnRay(rt, t);
+
+ outside = (glm::dot(r.direction, normal) < 0.0f);
+ intersectionPoint = multiplyMV(triangle.transform, glm::vec4(objspaceIntersection, 1.f));
+ normal = glm::normalize(multiplyMV(triangle.invTranspose, glm::vec4(objspaceIntersection, 0.f)));
+
+ return t;
+}
+
+/**
+* Test intersection between a ray and a transformed bounding box.
+*
+* @param mesh Mesh that contains the bounding box.
+* @param ray Ray to test with.
+* @return True if intersected bounding box, false otherwise
+*/
+
+__host__ __device__ bool meshBoundingVolumeIntersectionTest(Geom mesh, Ray ray) {
+ glm::vec3 ro = multiplyMV(mesh.inverseTransform, glm::vec4(ray.origin, 1.0f));
+ glm::vec3 rd = glm::normalize(multiplyMV(mesh.inverseTransform, glm::vec4(ray.direction, 0.0f)));
+
+ Ray r;
+ r.origin = ro;
+ r.direction = rd;
+
+ // BB intersection from CIS 460
+ float minX, maxX, minY, maxY, minZ, maxZ;
+
+ glm::vec3 inverseDirection = 1.0f / r.direction;
+ glm::ivec3 sign(inverseDirection.x < 0, inverseDirection.y < 0, inverseDirection.z < 0);
+
+ glm::vec3 bounds[2];
+ bounds[0] = mesh.min;
+ bounds[1] = mesh.max;
+
+ minX = (bounds[sign[0]].x - r.origin.x) * inverseDirection.x;
+ maxX = (bounds[1 - sign[0]].x - r.origin.x) * inverseDirection.x;
+ minY = (bounds[sign[1]].y - r.origin.y) * inverseDirection.y;
+ maxY = (bounds[1 - sign[1]].y - r.origin.y) * inverseDirection.y;
+
+ // X and Y slab intersections
+ if ((minX > maxY) || (minY > maxX)) {
+ return false;
+ }
+
+ if (minY > maxX) {
+ maxX = minY;
+ }
+
+ if (maxY < maxX) {
+ maxX = maxY;
+ }
+
+ // Z slab intersections
+ minZ = (bounds[sign[2]].z - r.origin.z) * inverseDirection.z;
+ maxZ = (bounds[1 - sign[2]].z - r.origin.z) * inverseDirection.z;
+
+ if ((minX > maxZ) || (minZ > maxX)) {
+ return false;
+ }
+
+ if (minZ > minX) {
+ minX = minZ;
+ }
+
+ if (maxZ < maxX) {
+ maxX = maxZ;
+ }
+ return true;
+}
diff --git a/src/main.cpp b/src/main.cpp
index fe8e85e..2fdc41e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -2,6 +2,8 @@
#include "preview.h"
#include
+
+
static std::string startTimeString;
// For camera controls
diff --git a/src/pathtrace.cu b/src/pathtrace.cu
index c1ec122..5382dc2 100644
--- a/src/pathtrace.cu
+++ b/src/pathtrace.cu
@@ -16,6 +16,17 @@
#define ERRORCHECK 1
+// Toggleable features (0 for false, 1 for true)
+#define STREAM_COMPACTION 1
+#define CACHE_FIRST_BOUNCE 1
+#define SORT_BY_MATERIALS 1
+
+#define AA !CACHE_FIRST_BOUNCE
+#define DEPTH_OF_FIELD 0
+// WARNING: For depth of field, add a FOCAL argument (e.g 10) and a LENSR argument (e.g 0.5) for this feature to work
+
+#define BOUNDING_VOLUME_CULLING 1
+
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__)
void checkCUDAErrorFn(const char *msg, const char *file, int line) {
@@ -73,42 +84,51 @@ static Geom * dev_geoms = NULL;
static Material * dev_materials = NULL;
static PathSegment * dev_paths = NULL;
static ShadeableIntersection * dev_intersections = NULL;
-// TODO: static variables for device memory, any extra info you need, etc
-// ...
+static ShadeableIntersection * dev_intersections_first = NULL;
+static int* dev_indicesCompact = NULL;
+static int* dev_matIndicesSorted = NULL;
void pathtraceInit(Scene *scene) {
- hst_scene = scene;
- const Camera &cam = hst_scene->state.camera;
- const int pixelcount = cam.resolution.x * cam.resolution.y;
+ hst_scene = scene;
+ const Camera &cam = hst_scene->state.camera;
+ const int pixelcount = cam.resolution.x * cam.resolution.y;
- cudaMalloc(&dev_image, pixelcount * sizeof(glm::vec3));
- cudaMemset(dev_image, 0, pixelcount * sizeof(glm::vec3));
+ cudaMalloc(&dev_image, pixelcount * sizeof(glm::vec3));
+ cudaMemset(dev_image, 0, pixelcount * sizeof(glm::vec3));
- cudaMalloc(&dev_paths, pixelcount * sizeof(PathSegment));
+ cudaMalloc(&dev_paths, pixelcount * sizeof(PathSegment));
- cudaMalloc(&dev_geoms, scene->geoms.size() * sizeof(Geom));
- cudaMemcpy(dev_geoms, scene->geoms.data(), scene->geoms.size() * sizeof(Geom), cudaMemcpyHostToDevice);
+ cudaMalloc(&dev_geoms, scene->geoms.size() * sizeof(Geom));
+ cudaMemcpy(dev_geoms, scene->geoms.data(), scene->geoms.size() * sizeof(Geom), cudaMemcpyHostToDevice);
+
+ cudaMalloc(&dev_materials, scene->materials.size() * sizeof(Material));
+ cudaMemcpy(dev_materials, scene->materials.data(), scene->materials.size() * sizeof(Material), cudaMemcpyHostToDevice);
+
+ cudaMalloc(&dev_intersections, pixelcount * sizeof(ShadeableIntersection));
+ cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
- cudaMalloc(&dev_materials, scene->materials.size() * sizeof(Material));
- cudaMemcpy(dev_materials, scene->materials.data(), scene->materials.size() * sizeof(Material), cudaMemcpyHostToDevice);
+ cudaMalloc(&dev_indicesCompact, pixelcount * sizeof(int));
- cudaMalloc(&dev_intersections, pixelcount * sizeof(ShadeableIntersection));
- cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
+ cudaMalloc(&dev_intersections_first, pixelcount * sizeof(ShadeableIntersection));
+ cudaMemset(dev_intersections_first, 0, pixelcount * sizeof(ShadeableIntersection));
- // TODO: initialize any extra device memeory you need
+ cudaMalloc(&dev_matIndicesSorted, pixelcount * sizeof(int));
- checkCUDAError("pathtraceInit");
+ checkCUDAError("pathtraceInit");
}
void pathtraceFree() {
- cudaFree(dev_image); // no-op if dev_image is null
- cudaFree(dev_paths);
- cudaFree(dev_geoms);
- cudaFree(dev_materials);
- cudaFree(dev_intersections);
- // TODO: clean up any extra device memory you created
-
- checkCUDAError("pathtraceFree");
+ cudaFree(dev_image); // no-op if dev_image is null
+ cudaFree(dev_paths);
+ cudaFree(dev_geoms);
+ cudaFree(dev_materials);
+ cudaFree(dev_intersections);
+
+ cudaFree(dev_indicesCompact); // indices used to access paths/intersections (needed if stream compaction is implemented)
+ cudaFree(dev_intersections_first); // buffer to store iteration 1 & depth 0 intersections for caching
+ cudaFree(dev_matIndicesSorted); // key buffer used to sort dev_indicesCompact according to the material ID
+
+ checkCUDAError("pathtraceFree");
}
/**
@@ -119,7 +139,7 @@ void pathtraceFree() {
* motion blur - jitter rays "in time"
* lens effect - jitter ray origin positions based on a lens
*/
-__global__ void generateRayFromCamera(Camera cam, int iter, int traceDepth, PathSegment* pathSegments)
+__global__ void generateRayFromCamera(Camera cam, int iter, int traceDepth, PathSegment* pathSegments, int* indicesCompact)
{
int x = (blockIdx.x * blockDim.x) + threadIdx.x;
int y = (blockIdx.y * blockDim.y) + threadIdx.y;
@@ -129,20 +149,38 @@ __global__ void generateRayFromCamera(Camera cam, int iter, int traceDepth, Path
PathSegment & segment = pathSegments[index];
segment.ray.origin = cam.position;
- segment.color = glm::vec3(1.0f, 1.0f, 1.0f);
-
- // TODO: implement antialiasing by jittering the ray
+ segment.color = glm::vec3(1.0f, 1.0f, 1.0f);
+
+ float epsilonX = 0;
+ float epsilonY = 0;
+ thrust::default_random_engine rng = makeSeededRandomEngine(iter, index, traceDepth);
+ thrust::uniform_real_distribution u01(0, 1);
+#if AA
+ epsilonX = u01(rng);
+ epsilonY = u01(rng);
+#endif
segment.ray.direction = glm::normalize(cam.view
- - cam.right * cam.pixelLength.x * ((float)x - (float)cam.resolution.x * 0.5f)
- - cam.up * cam.pixelLength.y * ((float)y - (float)cam.resolution.y * 0.5f)
- );
+ - cam.right * cam.pixelLength.x * ((float)x - (float)cam.resolution.x * 0.5f + epsilonX)
+ - cam.up * cam.pixelLength.y * ((float)y - (float)cam.resolution.y * 0.5f + epsilonY)
+ );
+
+#if DEPTH_OF_FIELD
+ float lensRad = cam.lensRadius;
+ float focalDist = cam.focalDistance;
+ glm::vec3 pLens = glm::vec3(lensRad * calculateConcentricSampleDisk(u01(rng), u01(rng)), 0.0f);
+ float ft = focalDist / glm::abs(segment.ray.direction.z);
+ glm::vec3 pFocus = segment.ray.direction * ft;
+
+ segment.ray.origin += pLens;
+ segment.ray.direction = glm::normalize(pFocus - pLens);
+#endif
segment.pixelIndex = index;
segment.remainingBounces = traceDepth;
+ indicesCompact[index] = index;
}
}
-// TODO:
// computeIntersections handles generating ray intersections ONLY.
// Generating new rays is handled in your shader(s).
// Feel free to modify the code below.
@@ -153,13 +191,16 @@ __global__ void computeIntersections(
, Geom * geoms
, int geoms_size
, ShadeableIntersection * intersections
+ , int* indicesCompact
+ , int* indicesMaterial
)
{
int path_index = blockIdx.x * blockDim.x + threadIdx.x;
if (path_index < num_paths)
{
- PathSegment pathSegment = pathSegments[path_index];
+ int idx = indicesCompact[path_index];
+ PathSegment pathSegment = pathSegments[idx];
float t;
glm::vec3 intersect_point;
@@ -172,7 +213,6 @@ __global__ void computeIntersections(
glm::vec3 tmp_normal;
// naive parse through global geoms
-
for (int i = 0; i < geoms_size; i++)
{
Geom & geom = geoms[i];
@@ -185,7 +225,19 @@ __global__ void computeIntersections(
{
t = sphereIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
}
- // TODO: add more intersection tests here... triangle? metaball? CSG?
+ else if (geom.type == MESH) {
+#if BOUNDING_VOLUME_CULLING
+ if (!meshBoundingVolumeIntersectionTest(geom, pathSegment.ray)) {
+ // if the ray completely misses the bounding volume, then skip the mesh by jumping to the next
+ // geom (skipping by nbTriangles)
+ i += geom.nbTriangles;
+ }
+#endif
+ continue;
+ }
+ else if (geom.type == TRI) {
+ t = triIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
+ }
// Compute the minimum t from the intersection tests to determine what
// scene geometry object was hit first.
@@ -200,69 +252,81 @@ __global__ void computeIntersections(
if (hit_geom_index == -1)
{
- intersections[path_index].t = -1.0f;
+ intersections[idx].t = -1.0f;
}
else
{
- //The ray hits something
- intersections[path_index].t = t_min;
- intersections[path_index].materialId = geoms[hit_geom_index].materialid;
- intersections[path_index].surfaceNormal = normal;
+ // The ray hits something
+ intersections[idx].t = t_min;
+ intersections[idx].materialId = geoms[hit_geom_index].materialid;
+ indicesMaterial[path_index] = intersections[idx].materialId; // store material ID for sorting
+ intersections[idx].surfaceNormal = normal;
}
}
}
-// LOOK: "fake" shader demonstrating what you might do with the info in
-// a ShadeableIntersection, as well as how to use thrust's random number
-// generator. Observe that since the thrust random number generator basically
-// adds "noise" to the iteration, the image should start off noisy and get
-// cleaner as more iterations are computed.
-//
-// Note that this shader does NOT do a BSDF evaluation!
-// Your shaders should handle that - this can allow techniques such as
-// bump mapping.
-__global__ void shadeFakeMaterial (
- int iter
- , int num_paths
- , ShadeableIntersection * shadeableIntersections
- , PathSegment * pathSegments
- , Material * materials
- )
-{
- int idx = blockIdx.x * blockDim.x + threadIdx.x;
- if (idx < num_paths)
- {
- ShadeableIntersection intersection = shadeableIntersections[idx];
- if (intersection.t > 0.0f) { // if the intersection exists...
- // Set up the RNG
- // LOOK: this is how you use thrust's RNG! Please look at
- // makeSeededRandomEngine as well.
- thrust::default_random_engine rng = makeSeededRandomEngine(iter, idx, 0);
- thrust::uniform_real_distribution u01(0, 1);
-
- Material material = materials[intersection.materialId];
- glm::vec3 materialColor = material.color;
-
- // If the material indicates that the object was a light, "light" the ray
- if (material.emittance > 0.0f) {
- pathSegments[idx].color *= (materialColor * material.emittance);
- }
- // Otherwise, do some pseudo-lighting computation. This is actually more
- // like what you would expect from shading in a rasterizer like OpenGL.
- // TODO: replace this! you should be able to start with basically a one-liner
- else {
- float lightTerm = glm::dot(intersection.surfaceNormal, glm::vec3(0.0f, 1.0f, 0.0f));
- pathSegments[idx].color *= (materialColor * lightTerm) * 0.3f + ((1.0f - intersection.t * 0.02f) * materialColor) * 0.7f;
- pathSegments[idx].color *= u01(rng); // apply some noise because why not
- }
- // If there was no intersection, color the ray black.
- // Lots of renderers use 4 channel color, RGBA, where A = alpha, often
- // used for opacity, in which case they can indicate "no opacity".
- // This can be useful for post-processing and image compositing.
- } else {
- pathSegments[idx].color = glm::vec3(0.0f);
- }
- }
+__global__ void shadeRealMaterial(
+ int iter
+ , int depth
+ , int num_paths
+ , ShadeableIntersection* shadeableIntersections
+ , PathSegment* pathSegments
+ , Material* materials
+ , int* indicesCompact) {
+
+ int idx = blockIdx.x * blockDim.x + threadIdx.x;
+ if (idx < num_paths) {
+ int compactIndex = indicesCompact[idx];
+
+#if STREAM_COMPACTION
+ bool deadPath = compactIndex == -1 || pathSegments[compactIndex].remainingBounces <= 0;
+#else
+ bool deadPath = pathSegments[compactIndex].remainingBounces <= 0;
+#endif
+
+ if (!deadPath) {
+ PathSegment* path = &pathSegments[compactIndex];
+ ShadeableIntersection intersection = shadeableIntersections[compactIndex];
+ if (intersection.t > 0.0f) {
+ // if the intersection exists...
+ // Set up the RNG
+ thrust::default_random_engine rng = makeSeededRandomEngine(iter, compactIndex, depth);
+ thrust::uniform_real_distribution u01(0, 1);
+
+ Material material = materials[intersection.materialId];
+ glm::vec3 materialColor = material.color;
+
+ // If the material indicates that the object was a light, "light" the ray
+ if (material.emittance > 0.0f) {
+ path->color *= (materialColor * material.emittance);
+ path->remainingBounces = 0;
+ }
+ // Otherwise, do some pseudo-lighting computation. This is actually more
+ // like what you would expect from shading in a rasterizer like OpenGL.
+ else {
+ scatterRay(*path,
+ getPointOnRay(path->ray, intersection.t),
+ intersection.surfaceNormal,
+ material,
+ rng);
+ path->remainingBounces -= 1;
+ }
+ // If there was no intersection, color the ray black.
+ // Lots of renderers use 4 channel color, RGBA, where A = alpha, often
+ // used for opacity, in which case they can indicate "no opacity".
+ // This can be useful for post-processing and image compositing.
+ }
+ else {
+ path->color = glm::vec3(0.0f);
+ path->remainingBounces = 0;
+ }
+
+#if STREAM_COMPACTION
+ // set the index to -1 if the path terminates
+ indicesCompact[idx] = path->remainingBounces <= 0 ? -1 : indicesCompact[idx];
+#endif
+ }
+ }
}
// Add the current iteration's output to the overall image
@@ -277,56 +341,34 @@ __global__ void finalGather(int nPaths, glm::vec3 * image, PathSegment * iterati
}
}
+struct is_path_complete {
+ __host__ __device__
+ bool operator()(const int index) {
+ return index == -1;
+ }
+};
+
/**
* Wrapper for the __global__ call that sets up the kernel calls and does a ton
* of memory management
*/
void pathtrace(uchar4 *pbo, int frame, int iter) {
- const int traceDepth = hst_scene->state.traceDepth;
- const Camera &cam = hst_scene->state.camera;
- const int pixelcount = cam.resolution.x * cam.resolution.y;
+ const int traceDepth = hst_scene->state.traceDepth;
+ const Camera &cam = hst_scene->state.camera;
+ const int pixelcount = cam.resolution.x * cam.resolution.y;
// 2D block for generating ray from camera
- const dim3 blockSize2d(8, 8);
- const dim3 blocksPerGrid2d(
- (cam.resolution.x + blockSize2d.x - 1) / blockSize2d.x,
- (cam.resolution.y + blockSize2d.y - 1) / blockSize2d.y);
+ const dim3 blockSize2d(8, 8);
+ const dim3 blocksPerGrid2d(
+ (cam.resolution.x + blockSize2d.x - 1) / blockSize2d.x,
+ (cam.resolution.y + blockSize2d.y - 1) / blockSize2d.y);
// 1D block for path tracing
const int blockSize1d = 128;
///////////////////////////////////////////////////////////////////////////
- // Recap:
- // * Initialize array of path rays (using rays that come out of the camera)
- // * You can pass the Camera object to that kernel.
- // * Each path ray must carry at minimum a (ray, color) pair,
- // * where color starts as the multiplicative identity, white = (1, 1, 1).
- // * This has already been done for you.
- // * For each depth:
- // * Compute an intersection in the scene for each path ray.
- // A very naive version of this has been implemented for you, but feel
- // free to add more primitives and/or a better algorithm.
- // Currently, intersection distance is recorded as a parametric distance,
- // t, or a "distance along the ray." t = -1.0 indicates no intersection.
- // * Color is attenuated (multiplied) by reflections off of any object
- // * TODO: Stream compact away all of the terminated paths.
- // You may use either your implementation or `thrust::remove_if` or its
- // cousins.
- // * Note that you can't really use a 2D kernel launch any more - switch
- // to 1D.
- // * TODO: Shade the rays that intersected something or didn't bottom out.
- // That is, color the ray by performing a color computation according
- // to the shader, then generate a new ray to continue the ray path.
- // We recommend just updating the ray's PathSegment in place.
- // Note that this step may come before or after stream compaction,
- // since some shaders you write may also cause a path to terminate.
- // * Finally, add this iteration's results to the image. This has been done
- // for you.
-
- // TODO: perform one iteration of path tracing
-
- generateRayFromCamera <<>>(cam, iter, traceDepth, dev_paths);
+ generateRayFromCamera << > > (cam, iter, traceDepth, dev_paths, dev_indicesCompact);
checkCUDAError("generate camera ray");
int depth = 0;
@@ -336,58 +378,117 @@ void pathtrace(uchar4 *pbo, int frame, int iter) {
// --- PathSegment Tracing Stage ---
// Shoot ray into scene, bounce between objects, push shading chunks
- bool iterationComplete = false;
+ bool iterationComplete = false;
while (!iterationComplete) {
+ // clean shading chunks
+ cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
+
+ // tracing
+ dim3 numblocksPathSegmentTracing((num_paths + blockSize1d - 1) / blockSize1d);
+
+ // which device array of intersections to use in the shading kernel
+ ShadeableIntersection* intersectionsToUse = dev_intersections;
+
+#if CACHE_FIRST_BOUNCE
+ if (depth == 0) {
+ // might reuse cache if depth == 0
+ if (iter == 1) {
+ // compute 1st bounce and cache it
+ computeIntersections << > > (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_indicesCompact
+ , dev_matIndicesSorted
+ );
+ checkCUDAError("trace one bounce");
+ cudaMemcpy(dev_intersections_first, dev_intersections,
+ pixelcount * sizeof(ShadeableIntersection), cudaMemcpyDeviceToDevice);
+ }
+ else {
+ // reuse cache
+ intersectionsToUse = dev_intersections_first;
+ }
+ }
+ else {
+ // cannot reuse cache if depth > 0
+ computeIntersections << > > (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_indicesCompact
+ , dev_matIndicesSorted
+ );
+ checkCUDAError("trace one bounce");
+ }
+#else
+ computeIntersections << > > (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_indicesCompact
+ , dev_matIndicesSorted
+ );
+ checkCUDAError("trace one bounce");
+#endif
+ cudaDeviceSynchronize();
+
- // clean shading chunks
- cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
-
- // tracing
- dim3 numblocksPathSegmentTracing = (num_paths + blockSize1d - 1) / blockSize1d;
- computeIntersections <<>> (
- depth
- , num_paths
- , dev_paths
- , dev_geoms
- , hst_scene->geoms.size()
- , dev_intersections
- );
- checkCUDAError("trace one bounce");
- cudaDeviceSynchronize();
- depth++;
-
-
- // TODO:
- // --- Shading Stage ---
- // Shade path segments based on intersections and generate new rays by
- // evaluating the BSDF.
- // Start off with just a big kernel that handles all the different
- // materials you have in the scenefile.
- // TODO: compare between directly shading the path segments and shading
- // path segments that have been reshuffled to be contiguous in memory.
-
- shadeFakeMaterial<<>> (
- iter,
- num_paths,
- dev_intersections,
- dev_paths,
- dev_materials
- );
- iterationComplete = true; // TODO: should be based off stream compaction results.
- }
+ // --- Shading Stage ---
+ // Shade path segments based on intersections and generate new rays by
+ // evaluating the BSDF.
+ // Start off with just a big kernel that handles all the different
+ // materials you have in the scenefile.
- // Assemble this iteration and apply it to the image
- dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d;
- finalGather<<>>(num_paths, dev_image, dev_paths);
+#if SORT_BY_MATERIALS
+ thrust::sort_by_key(thrust::device, dev_matIndicesSorted, dev_matIndicesSorted + num_paths, dev_indicesCompact);
+#endif
+ shadeRealMaterial << > > (
+ iter,
+ depth,
+ num_paths,
+ intersectionsToUse,
+ dev_paths,
+ dev_materials,
+ dev_indicesCompact
+ );
+ depth++;
+
+ if (depth >= traceDepth) {
+ // iteration ends due to depth
+ iterationComplete = true;
+ }
+#if STREAM_COMPACTION
+ else {
+ // stream compaction
+ int* new_indices_end = thrust::remove_if(thrust::device, dev_indicesCompact, dev_indicesCompact + num_paths, is_path_complete());
+ num_paths = new_indices_end - dev_indicesCompact; // shrink number paths
+ iterationComplete = (num_paths <= 0);
+ }
+#endif
+ }
- ///////////////////////////////////////////////////////////////////////////
+ // Assemble this iteration and apply it to the image
+ num_paths = dev_path_end - dev_paths; // reset num paths to pixel count for a correct final gather
+ dim3 numBlocksPixels((pixelcount + blockSize1d - 1) / blockSize1d);
+ finalGather << > > (num_paths, dev_image, dev_paths);
- // Send results to OpenGL buffer for rendering
- sendImageToPBO<<>>(pbo, cam.resolution, iter, dev_image);
+ ///////////////////////////////////////////////////////////////////////////
- // Retrieve image from GPU
- cudaMemcpy(hst_scene->state.image.data(), dev_image,
- pixelcount * sizeof(glm::vec3), cudaMemcpyDeviceToHost);
+ // Send results to OpenGL buffer for rendering
+ sendImageToPBO << > > (pbo, cam.resolution, iter, dev_image);
- checkCUDAError("pathtrace");
+ // Retrieve image from GPU
+ cudaMemcpy(hst_scene->state.image.data(), dev_image,
+ pixelcount * sizeof(glm::vec3), cudaMemcpyDeviceToHost);
+ checkCUDAError("pathtrace");
}
diff --git a/src/scene.cpp b/src/scene.cpp
index cbae043..a80d3cd 100644
--- a/src/scene.cpp
+++ b/src/scene.cpp
@@ -3,7 +3,10 @@
#include
#include
#include
+#include "tinyobjloader\tiny_obj_loader.h"
+using namespace tinyobj;
+using namespace std;
Scene::Scene(string filename) {
cout << "Reading scene from " << filename << " ..." << endl;
cout << " " << endl;
@@ -32,61 +35,181 @@ Scene::Scene(string filename) {
}
}
+/**
+ * Given an initialized triangle Geom, fill it with appropriate data using the parsed obj file
+ */
+void createTriangle(Geom& triangle, glm::vec3& boundMin, glm::vec3& boundMax,
+ int triIdx, const shape_t currPoly, const attrib_t tinyObjAttrib) {
+ // triangle indices
+ index_t i0 = currPoly.mesh.indices[3 * triIdx + 0];
+ index_t i1 = currPoly.mesh.indices[3 * triIdx + 1];
+ index_t i2 = currPoly.mesh.indices[3 * triIdx + 2];
+
+ // triangle positions
+ glm::vec3 pos0, pos1, pos2;
+ for (int i = 0; i < 3; i++) {
+ pos0[i] = tinyObjAttrib.vertices[3 * i0.vertex_index + i];
+ pos1[i] = tinyObjAttrib.vertices[3 * i1.vertex_index + i];
+ pos2[i] = tinyObjAttrib.vertices[3 * i2.vertex_index + i];
+
+ boundMin[i] = min(pos2[i], min(pos1[i], min(pos0[i], boundMin[i])));
+ boundMax[i] = max(pos2[i], max(pos1[i], max(pos0[i], boundMax[i])));
+ }
+ triangle.pos[0] = pos0;
+ triangle.pos[1] = pos1;
+ triangle.pos[2] = pos2;
+
+ // triangle uvs
+ glm::vec2 uv0(0.0f);
+ glm::vec2 uv1(0.0f);
+ glm::vec2 uv2(0.0f);
+ if (tinyObjAttrib.texcoords.size()) {
+ for (int i = 0; i < 2; i++) {
+ uv0[i] = tinyObjAttrib.texcoords[2 * i0.texcoord_index + i];
+ uv1[i] = tinyObjAttrib.texcoords[2 * i1.texcoord_index + i];
+ uv2[i] = tinyObjAttrib.texcoords[2 * i2.texcoord_index + i];
+
+ uv0[i] = i == 1 ? 1.0f - uv0[i] : uv0[i];
+ uv1[i] = i == 1 ? 1.0f - uv1[i] : uv1[i];
+ uv2[i] = i == 1 ? 1.0f - uv2[i] : uv2[i];
+ }
+ }
+ triangle.uv[0] = uv0;
+ triangle.uv[1] = uv1;
+ triangle.uv[2] = uv2;
+
+ // triangle normals
+ glm::vec3 norm0, norm1, norm2;
+ if (tinyObjAttrib.normals.size()) {
+ for (int i = 0; i < 3; i++) {
+ norm0[i] = tinyObjAttrib.normals[3 * i0.normal_index + i];
+ norm1[i] = tinyObjAttrib.normals[3 * i1.normal_index + i];
+ norm2[i] = tinyObjAttrib.normals[3 * i2.normal_index + i];
+ }
+ }
+ else {
+ glm::vec3 normal = calculate_geometric_normals(pos0, pos1, pos2);
+ norm0 = normal;
+ norm1 = normal;
+ norm2 = normal;
+ }
+ triangle.norm[0] = norm0;
+ triangle.norm[1] = norm1;
+ triangle.norm[2] = norm2;
+}
+
int Scene::loadGeom(string objectid) {
int id = atoi(objectid.c_str());
if (id != geoms.size()) {
cout << "ERROR: OBJECT ID does not match expected number of geoms" << endl;
- return -1;
- } else {
- cout << "Loading Geom " << id << "..." << endl;
- Geom newGeom;
- string line;
+ }
- //load object type
- utilityCore::safeGetline(fp_in, line);
- if (!line.empty() && fp_in.good()) {
- if (strcmp(line.c_str(), "sphere") == 0) {
- cout << "Creating new sphere..." << endl;
- newGeom.type = SPHERE;
- } else if (strcmp(line.c_str(), "cube") == 0) {
- cout << "Creating new cube..." << endl;
- newGeom.type = CUBE;
- }
- }
+ cout << "Loading Geom " << id << "..." << endl;
+ Geom newGeom;
+ vector parsedTriangles; // we might create many geoms if its a mesh
+ string line;
- //link material
- utilityCore::safeGetline(fp_in, line);
- if (!line.empty() && fp_in.good()) {
- vector tokens = utilityCore::tokenizeString(line);
- newGeom.materialid = atoi(tokens[1].c_str());
- cout << "Connecting Geom " << objectid << " to Material " << newGeom.materialid << "..." << endl;
- }
+ //load object type
+ utilityCore::safeGetline(fp_in, line);
+ if (!line.empty() && fp_in.good()) {
+ vector tokens = utilityCore::tokenizeString(line);
+ if (strcmp(tokens[0].c_str(), "sphere") == 0) {
+ cout << "Creating new sphere..." << endl;
+ newGeom.type = SPHERE;
+ } else if (strcmp(tokens[0].c_str(), "cube") == 0) {
+ cout << "Creating new cube..." << endl;
+ newGeom.type = CUBE;
+ }
+ else if (strcmp(tokens[0].c_str(), "mesh") == 0) {
+ cout << "Loading new mesh..." << endl;
+ newGeom.type = MESH;
+ attrib_t tinyObjAttrib;
+ vector mats;
+ vector shapes;
+ string error;
+ if (!LoadObj(&tinyObjAttrib, &shapes, &mats, &error, tokens[1].c_str())) {
+ cout << "FAILURE: Loading " << tokens[1].c_str() << " did not succeed!" << endl;
+ }
- //load transformations
- utilityCore::safeGetline(fp_in, line);
- while (!line.empty() && fp_in.good()) {
- vector tokens = utilityCore::tokenizeString(line);
+ if (!error.empty()) {
+ cout << "FAILURE: Loading OBJ resulted in error - " << error << endl;
+ }
- //load tranformations
- if (strcmp(tokens[0].c_str(), "TRANS") == 0) {
- newGeom.translation = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
- } else if (strcmp(tokens[0].c_str(), "ROTAT") == 0) {
- newGeom.rotation = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
- } else if (strcmp(tokens[0].c_str(), "SCALE") == 0) {
- newGeom.scale = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
- }
+ // init bounds of mesh
+ glm::vec3 min(FLT_MAX);
+ glm::vec3 max(FLT_MIN);
- utilityCore::safeGetline(fp_in, line);
- }
+ for (int poly = 0; poly < shapes.size(); poly++) {
+ // traverse each triangle in this polygon and create a triangle Geom accordingly
+ shape_t currPoly = shapes[poly];
+ for (int tri = 0; tri < currPoly.mesh.indices.size() / 3; tri++) {
+ Geom triangle;
+ triangle.type = TRI;
+ createTriangle(triangle, min, max, tri, currPoly, tinyObjAttrib);
+ parsedTriangles.push_back(triangle);
+ }
+ }
+ int nbTriangles = parsedTriangles.size();
+ newGeom.nbTriangles = nbTriangles;
+ newGeom.min = min;
+ newGeom.max = max;
+ }
+ }
- newGeom.transform = utilityCore::buildTransformationMatrix(
- newGeom.translation, newGeom.rotation, newGeom.scale);
- newGeom.inverseTransform = glm::inverse(newGeom.transform);
- newGeom.invTranspose = glm::inverseTranspose(newGeom.transform);
+ //link material
+ utilityCore::safeGetline(fp_in, line);
+ if (!line.empty() && fp_in.good()) {
+ vector tokens = utilityCore::tokenizeString(line);
+ int matId = atoi(tokens[1].c_str());
+ for (Geom& g : parsedTriangles) {
+ g.materialid = matId;
+ }
+ newGeom.materialid = matId;
+ cout << "Connecting Geom " << objectid << " to Material " << newGeom.materialid << "..." << endl;
+ }
- geoms.push_back(newGeom);
- return 1;
+ //load transformations
+ utilityCore::safeGetline(fp_in, line);
+ while (!line.empty() && fp_in.good()) {
+ vector tokens = utilityCore::tokenizeString(line);
+
+ //load tranformations
+ if (strcmp(tokens[0].c_str(), "TRANS") == 0) {
+ glm::vec3 t(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
+ for (Geom& g : parsedTriangles) {
+ g.translation = t;
+ }
+ newGeom.translation = t;
+ } else if (strcmp(tokens[0].c_str(), "ROTAT") == 0) {
+ glm::vec3 r(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
+ for (Geom& g : parsedTriangles) {
+ g.rotation = r;
+ }
+ newGeom.rotation = r;
+ } else if (strcmp(tokens[0].c_str(), "SCALE") == 0) {
+ glm::vec3 s(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
+ for (Geom& g : parsedTriangles) {
+ g.scale = s;
+ }
+ newGeom.scale = s;
+ }
+
+ utilityCore::safeGetline(fp_in, line);
}
+
+ newGeom.transform = utilityCore::buildTransformationMatrix(
+ newGeom.translation, newGeom.rotation, newGeom.scale);
+ newGeom.inverseTransform = glm::inverse(newGeom.transform);
+ newGeom.invTranspose = glm::inverseTranspose(newGeom.transform);
+
+ geoms.push_back(newGeom);
+ for (Geom& g : parsedTriangles) {
+ g.transform = utilityCore::buildTransformationMatrix(g.translation, g.rotation, g.scale);
+ g.inverseTransform = glm::inverse(g.transform);
+ g.invTranspose = glm::inverseTranspose(g.transform);
+ geoms.push_back(g);
+ }
+ return 1;
}
int Scene::loadCamera() {
@@ -124,7 +247,13 @@ int Scene::loadCamera() {
camera.lookAt = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
} else if (strcmp(tokens[0].c_str(), "UP") == 0) {
camera.up = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
- }
+ }
+ else if (strcmp(tokens[0].c_str(), "FOCAL") == 0) {
+ camera.focalDistance = atof(tokens[1].c_str());
+ }
+ else if (strcmp(tokens[0].c_str(), "LENSR") == 0) {
+ camera.lensRadius = atof(tokens[1].c_str());
+ }
utilityCore::safeGetline(fp_in, line);
}
@@ -186,3 +315,11 @@ int Scene::loadMaterial(string materialid) {
return 1;
}
}
+
+glm::vec3 calculate_geometric_normals(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) {
+ glm::vec3 edge10 = p1 - p0;
+
+ glm::vec3 edge20 = p2 - p0;
+
+ return glm::normalize(glm::cross(edge20, edge10));
+}
diff --git a/src/scene.h b/src/scene.h
index f29a917..0d736ac 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -10,6 +10,8 @@
using namespace std;
+static glm::vec3 calculate_geometric_normals(glm::vec3 p0, glm::vec3 p1, glm::vec3 p2);
+
class Scene {
private:
ifstream fp_in;
diff --git a/src/sceneStructs.h b/src/sceneStructs.h
index b38b820..5e20d82 100644
--- a/src/sceneStructs.h
+++ b/src/sceneStructs.h
@@ -8,8 +8,10 @@
#define BACKGROUND_COLOR (glm::vec3(0.0f))
enum GeomType {
- SPHERE,
- CUBE,
+ SPHERE,
+ CUBE,
+ MESH,
+ TRI,
};
struct Ray {
@@ -26,6 +28,14 @@ struct Geom {
glm::mat4 transform;
glm::mat4 inverseTransform;
glm::mat4 invTranspose;
+
+ // mesh loading requires more info: positions, uv coords, normals, and size (i.e number of triangles)
+ glm::vec3 pos[3];
+ glm::vec2 uv[3];
+ glm::vec3 norm[3];
+ int nbTriangles = 0; // used to know the number of triangles contained within a mesh (to traverse list of geoms)
+ glm::vec3 max; //bounds of mesh
+ glm::vec3 min;
};
struct Material {
@@ -49,6 +59,8 @@ struct Camera {
glm::vec3 right;
glm::vec2 fov;
glm::vec2 pixelLength;
+ float focalDistance;
+ float lensRadius;
};
struct RenderState {
@@ -70,7 +82,7 @@ struct PathSegment {
// 1) color contribution computation
// 2) BSDF evaluation: generate a new ray
struct ShadeableIntersection {
- float t;
- glm::vec3 surfaceNormal;
- int materialId;
+ float t;
+ glm::vec3 surfaceNormal;
+ int materialId;
};
diff --git a/tinyobjloader/CMakeLists.txt b/tinyobjloader/CMakeLists.txt
new file mode 100644
index 0000000..da1c00e
--- /dev/null
+++ b/tinyobjloader/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(SOURCE_FILES
+ "tiny_obj_loader.cc"
+ "tiny_obj_loader.h"
+ )
+
+cuda_add_library(tinyobj
+ ${SOURCE_FILES}
+ )
\ No newline at end of file
diff --git a/tinyobjloader/tiny_obj_loader.cc b/tinyobjloader/tiny_obj_loader.cc
new file mode 100644
index 0000000..e57d044
--- /dev/null
+++ b/tinyobjloader/tiny_obj_loader.cc
@@ -0,0 +1,2 @@
+#define TINYOBJLOADER_IMPLEMENTATION
+#include "tiny_obj_loader.h"
diff --git a/tinyobjloader/tiny_obj_loader.h b/tinyobjloader/tiny_obj_loader.h
new file mode 100644
index 0000000..f627e94
--- /dev/null
+++ b/tinyobjloader/tiny_obj_loader.h
@@ -0,0 +1,2518 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2012-2018 Syoyo Fujita and many contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+//
+// version 1.2.3 : Added color space extension('-colorspace') to tex opts.
+// version 1.2.2 : Parse multiple group names.
+// version 1.2.1 : Added initial support for line('l') primitive(PR #178)
+// version 1.2.0 : Hardened implementation(#175)
+// version 1.1.1 : Support smoothing groups(#162)
+// version 1.1.0 : Support parsing vertex color(#144)
+// version 1.0.8 : Fix parsing `g` tag just after `usemtl`(#138)
+// version 1.0.7 : Support multiple tex options(#126)
+// version 1.0.6 : Add TINYOBJLOADER_USE_DOUBLE option(#124)
+// version 1.0.5 : Ignore `Tr` when `d` exists in MTL(#43)
+// version 1.0.4 : Support multiple filenames for 'mtllib'(#112)
+// version 1.0.3 : Support parsing texture options(#85)
+// version 1.0.2 : Improve parsing speed by about a factor of 2 for large
+// files(#105)
+// version 1.0.1 : Fixes a shape is lost if obj ends with a 'usemtl'(#104)
+// version 1.0.0 : Change data structure. Change license from BSD to MIT.
+//
+
+//
+// Use this in *one* .cc
+// #define TINYOBJLOADER_IMPLEMENTATION
+// #include "tiny_obj_loader.h"
+//
+
+#ifndef TINY_OBJ_LOADER_H_
+#define TINY_OBJ_LOADER_H_
+
+#include