diff --git a/CMakeLists.txt b/CMakeLists.txt
index 62c0e599..cf1fc20c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -73,6 +73,7 @@ set(headers
src/sceneStructs.h
src/preview.h
src/utilities.h
+ src/tiny_obj_loader.h
)
set(sources
diff --git a/README.md b/README.md
index 110697ce..51543876 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,65 @@ 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)
+* Weiyu Du
+* Tested on: CETS Virtual Lab
-### (TODO: Your README)
+### Part 2
+### Refraction
+Refraction rendering with Frensel effects using Schlick's approximation
-*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.
+
+### Depth of Field
+From left to right: focus on foreground, focus on background
+
+
+
+
+### Stochastic Sampled Antialiasing
+From left to right: rendering with antialiasing, rendering without antialiasing. Please zoom in to see the difference on the edge of the sphere. The left has a smooth edge while the one on the right is more rigged.
+
+
+
+### Arbitrary OBJ Mesh Loader
+
+
+Performance comparison regarding bounding volume interseciton culling (measured in time per iteration):
+
+| OBJ file | bounding volume intersection culling | naive implementation |
+| --- | --- | --- |
+| Sphere | 98.122 | 129.479 |
+| Wahoo | 1068.55 | 1453.84 |
+| Stanford Bunny | 11970.6 | 22964.9 |
+
+We observe that such optimization reduces the run time per iteration consistenly across different obj files, specifically, the more vertices an obj file has, we observe more significant improvement using bounding volume intersection culling.
+
+### Stratified Sampling
+
+1) Comparison of stratified sampling (10x10 grid, left) and uniform random sampling (right) at 5000 iterations
+
+
+
+2) Comparison of stratified sampling (10x10 grid, left) and uniform random sampling (right) at 100 iterations
+
+
+
+### Motion Blur
+1) Defined motion in scene file
+
+
+
+2) User input camera motion (user drag the camera while rendering)
+
+
+
+### Part 1
+### Render Result
+
+
+### Analysis
+1) Plot of elapsed time per iteration versus max ray depth (timed when sorting_material set to true)
+
+
+- We expected that sorting the rays/path segments by material should improve the performance, because this will make the threads more likely to finish at around the same time, reducing waiting time for threads in the same warp. However, in reality we found that rendering without sorting is actually significantly faster. This may because that there isn't a variety of different materials in the scene. Since we're sorting the entire set of rays, this operation takes much more time than it saves.
+- From the plot above we see that increasing max ray depth results in longer run time per iteration. Rendering using first bounce cache is consistently faster than rendering without cache, though not by a large margin. This is expected as we save time by avoiding the initial intersection computation.
diff --git a/img/antialiasing.png b/img/antialiasing.png
new file mode 100644
index 00000000..9448ad4c
Binary files /dev/null and b/img/antialiasing.png differ
diff --git a/img/defined_motion1.png b/img/defined_motion1.png
new file mode 100644
index 00000000..3f42a296
Binary files /dev/null and b/img/defined_motion1.png differ
diff --git a/img/defined_motion2.png b/img/defined_motion2.png
new file mode 100644
index 00000000..0ffb4581
Binary files /dev/null and b/img/defined_motion2.png differ
diff --git a/img/dof_close.png b/img/dof_close.png
new file mode 100644
index 00000000..eff43ef6
Binary files /dev/null and b/img/dof_close.png differ
diff --git a/img/dof_far.png b/img/dof_far.png
new file mode 100644
index 00000000..07ecd798
Binary files /dev/null and b/img/dof_far.png differ
diff --git a/img/real_time_motion.png b/img/real_time_motion.png
new file mode 100644
index 00000000..7661ed2c
Binary files /dev/null and b/img/real_time_motion.png differ
diff --git a/img/ref_100iter_10x10.png b/img/ref_100iter_10x10.png
new file mode 100644
index 00000000..78f13c96
Binary files /dev/null and b/img/ref_100iter_10x10.png differ
diff --git a/img/ref_5000.png b/img/ref_5000.png
new file mode 100644
index 00000000..e6062769
Binary files /dev/null and b/img/ref_5000.png differ
diff --git a/img/ref_antialiasing.png b/img/ref_antialiasing.png
new file mode 100644
index 00000000..0974b565
Binary files /dev/null and b/img/ref_antialiasing.png differ
diff --git a/img/refract.png b/img/refract.png
new file mode 100644
index 00000000..86c8445e
Binary files /dev/null and b/img/refract.png differ
diff --git a/img/strat_100iter_10x10.png b/img/strat_100iter_10x10.png
new file mode 100644
index 00000000..74b70e34
Binary files /dev/null and b/img/strat_100iter_10x10.png differ
diff --git a/img/strat_5000.png b/img/strat_5000.png
new file mode 100644
index 00000000..4d614cd7
Binary files /dev/null and b/img/strat_5000.png differ
diff --git a/img/wahoo.png b/img/wahoo.png
new file mode 100644
index 00000000..1f776754
Binary files /dev/null and b/img/wahoo.png differ
diff --git a/scenes/cornell.txt b/scenes/cornell.txt
index 83ff8202..8dc515dd 100644
--- a/scenes/cornell.txt
+++ b/scenes/cornell.txt
@@ -10,7 +10,7 @@ EMITTANCE 5
// Diffuse white
MATERIAL 1
-RGB .98 .98 .98
+RGB .98 0.98 0.98
SPECEX 0
SPECRGB 0 0 0
REFL 0
@@ -48,6 +48,36 @@ REFR 0
REFRIOR 0
EMITTANCE 0
+// Glass
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 1.5
+EMITTANCE 0
+
+// Glass
+MATERIAL 6
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2
+EMITTANCE 0
+
+// Glass
+MATERIAL 7
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2.5
+EMITTANCE 0
+
// Camera
CAMERA
RES 800 800
@@ -111,7 +141,39 @@ SCALE .01 10 10
// Sphere
OBJECT 6
sphere
-material 4
-TRANS -1 4 -1
+material 5
+TRANS -3 4 -1
ROTAT 0 0 0
SCALE 3 3 3
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS 0 4 -1
+ROTAT 0 0 0
+SCALE 2 2 2
+
+// Sphere
+OBJECT 8
+sphere
+material 5
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Cube
+OBJECT 9
+cube
+material 2
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
+
+// Cube
+OBJECT 10
+cube
+material 3
+TRANS -3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
\ No newline at end of file
diff --git a/scenes/cornell_move.txt b/scenes/cornell_move.txt
new file mode 100644
index 00000000..ee878d64
--- /dev/null
+++ b/scenes/cornell_move.txt
@@ -0,0 +1,179 @@
+// 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 0.98 0.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
+
+// Specular white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Glass
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 1.5
+EMITTANCE 0
+
+// Glass
+MATERIAL 6
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2
+EMITTANCE 0
+
+// Glass
+MATERIAL 7
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2.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
+//MOVE -0.15 0 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 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
+
+// Sphere
+OBJECT 6
+sphere
+material 5
+TRANS -3 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS 0 4 -1
+ROTAT 0 0 0
+SCALE 2 2 2
+
+// Sphere
+OBJECT 8
+sphere
+material 5
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Cube
+OBJECT 9
+cube
+material 2
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
+
+// Cube
+OBJECT 10
+cube
+material 3
+TRANS -3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
\ No newline at end of file
diff --git a/scenes/cornell_obj.txt b/scenes/cornell_obj.txt
new file mode 100644
index 00000000..9e6ab126
--- /dev/null
+++ b/scenes/cornell_obj.txt
@@ -0,0 +1,180 @@
+// 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 0.98 0.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
+
+// Specular white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Glass
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 1.5
+EMITTANCE 0
+
+// Glass
+MATERIAL 6
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2
+EMITTANCE 0
+
+// Glass
+MATERIAL 7
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 2.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
+
+
+// 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
+
+// Sphere
+OBJECT 6
+sphere
+material 5
+TRANS -3 4 -1
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS 0 4 -1
+ROTAT 0 0 0
+SCALE 2 2 2
+
+// Sphere
+OBJECT 8
+mesh
+../scenes/sphere.obj
+material 1
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Cube
+OBJECT 9
+cube
+material 2
+TRANS 3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
+
+// Cube
+OBJECT 10
+cube
+material 3
+TRANS -3 4 -1
+ROTAT 0 0 0
+SCALE 1 1 1
\ No newline at end of file
diff --git a/scenes/sphere.txt b/scenes/sphere.txt
index a74b5458..dee4e5a4 100644
--- a/scenes/sphere.txt
+++ b/scenes/sphere.txt
@@ -8,21 +8,160 @@ 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
+
+// Specular white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Glass
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+REFRIOR 1.5
+EMITTANCE 0
+
// Camera
CAMERA
RES 800 800
FOVY 45
ITERATIONS 5000
DEPTH 8
-FILE sphere
+FILE cornell
EYE 0.0 5 10.5
LOOKAT 0 5 0
UP 0 1 0
-// Sphere
+
+// Ceiling light
OBJECT 0
-sphere
+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
+
+// Object
+OBJECT 6
+sphere
+material 1
+TRANS -2 5 -3
+ROTAT 0 0 0
SCALE 3 3 3
+
+// Object
+OBJECT 7
+sphere
+material 1
+TRANS -1 2 -1
+ROTAT 0 0 0
+SCALE 4 4 4
+
+// Object
+OBJECT 8
+sphere
+material 1
+TRANS 2 6 3
+ROTAT 0 0 0
+SCALE 2 2 2
+
+// Object
+OBJECT 9
+sphere
+material 1
+TRANS -3 1.5 5
+ROTAT 0 0 0
+SCALE 1 1 1
+
+// Object
+OBJECT 10
+sphere
+material 1
+TRANS 4 3 -2
+ROTAT 0 0 0
+SCALE 1.5 1.5 1.5
+
+// Object
+OBJECT 11
+sphere
+material 1
+TRANS 2.5 1.5 4.5
+ROTAT 0 0 0
+SCALE 2 2 2
\ No newline at end of file
diff --git a/scenes/sphereobj.txt b/scenes/sphereobj.txt
new file mode 100644
index 00000000..835fa105
--- /dev/null
+++ b/scenes/sphereobj.txt
@@ -0,0 +1,128 @@
+// 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
+
+// Specular white
+MATERIAL 4
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB .98 .98 .98
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Glass
+MATERIAL 5
+RGB .98 .98 .98
+SPECEX 0.2
+SPECRGB .98 .98 .98
+REFL 1
+REFR 1
+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
+
+
+// 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
+
+// Object
+OBJECT 6
+mesh
+../scenes/wahoo.obj
+material 1
+TRANS 0 2 -1
+ROTAT 0 0 0
+SCALE 1 1 1
\ No newline at end of file
diff --git a/src/interactions.h b/src/interactions.h
index 5ce36285..d12b9a12 100644
--- a/src/interactions.h
+++ b/src/interactions.h
@@ -1,7 +1,10 @@
#pragma once
#include "intersections.h"
-
+#include "glm/glm.hpp"
+#include "glm/gtx/norm.hpp"
+#include
+#define BETTER_SAMPLING 0
// CHECKITOUT
/**
* Computes a cosine-weighted random direction in a hemisphere.
@@ -11,11 +14,44 @@ __host__ __device__
glm::vec3 calculateRandomDirectionInHemisphere(
glm::vec3 normal, thrust::default_random_engine &rng) {
thrust::uniform_real_distribution u01(0, 1);
-
+ int num_samples_squared = 10;
+#if BETTER_SAMPLING
+ float side_len = 1.f / (float)num_samples_squared;
+ int idx = (int)(u01(rng) * num_samples_squared * num_samples_squared);
+ int i = idx / num_samples_squared;
+ int j = idx % num_samples_squared;
+ float center_x = i * side_len + u01(rng) * side_len;
+ float center_y = j * side_len + u01(rng) * side_len;
+ float up = sqrt(center_x); // cos(theta)
+ float over = sqrt(1 - up * up); // sin(theta)
+ float around = (center_y)*TWO_PI;
+ /*
+ // shirley mapping, creates darker images
+ float s;
+ float t;
+ float s_prime;
+ float t_prime;
+ if (s < 0.5f) {
+ s_prime = -0.5f + sqrt(2.f * s);
+}
+ else {
+ s_prime = 1.5f - sqrt(2.f - 2.f * s);
+ }
+ if (t < 0.5f) {
+ t_prime = -0.5f + sqrt(2.f * t);
+ }
+ else {
+ t_prime = 1.5f - sqrt(2.f - 2.f * t);
+ }
+ float up = sqrt(s_prime); // cos(theta)
+ float over = sqrt(1 - up * up); // sin(theta)
+ float around = (t_prime) * TWO_PI;
+ */
+#else
float up = sqrt(u01(rng)); // cos(theta)
float over = sqrt(1 - up * up); // sin(theta)
float around = u01(rng) * TWO_PI;
-
+#endif
// Find a direction that is not the normal based off of whether or not the
// normal's components are all equal to sqrt(1/3) or whether or not at
// least one component is less than sqrt(1/3). Learned this trick from
@@ -66,14 +102,79 @@ glm::vec3 calculateRandomDirectionInHemisphere(
*
* You may need to change the parameter list for your purposes!
*/
+
+__host__ __device__ void reflect(PathSegment& pathSegment,
+ glm::vec3 intersection, glm::vec3 normal, const Material& material) {
+ pathSegment.ray.direction = glm::reflect(pathSegment.ray.direction, normal);
+ pathSegment.ray.origin = intersection + 0.01f * normal;
+ pathSegment.color *= material.specular.color;
+}
+
__host__ __device__
void scatterRay(
PathSegment & pathSegment,
- glm::vec3 intersect,
+ glm::vec3 intersection,
glm::vec3 normal,
- const Material &m,
+ const Material &material,
thrust::default_random_engine &rng) {
// TODO: implement this.
// A basic implementation of pure-diffuse shading will just call the
// calculateRandomDirectionInHemisphere defined above.
+
+ //pathSegment.color *= material.color;
+ // reflective
+ pathSegment.remainingBounces--;
+ thrust::uniform_real_distribution u01(0, 1);
+ float rand_num = u01(rng);
+ if (material.hasRefractive == 1.f) {
+ glm::vec3 dir(0.f);
+ glm::vec3 real_norm(0.f);
+ float ior_ratio = 0.f;
+ if (glm::dot(pathSegment.ray.direction, normal) > 0.f) {
+ real_norm = -1.f * normal;
+ ior_ratio = material.indexOfRefraction;
+ }
+ else {
+ real_norm = normal;
+ ior_ratio = 1.f / material.indexOfRefraction;
+ }
+
+ dir = glm::refract(pathSegment.ray.direction, real_norm, ior_ratio);
+ float threshold = asin(ior_ratio);
+ float theta = acos(glm::dot(pathSegment.ray.direction, real_norm));
+
+ if (theta < threshold) {
+ // total internal reflection
+ reflect(pathSegment, intersection, real_norm, material);
+ pathSegment.color = glm::vec3(0.f);
+ return;
+ }
+
+ float R0 = powf((1.f - ior_ratio)/(1.f + ior_ratio), 2.f);
+ float R_theta = R0 + (1.f - R0) * powf(1.f - fmax(0.f, glm::dot(pathSegment.ray.direction, real_norm)), 5.f);
+
+ if (material.hasReflective == 1.f && rand_num > R_theta / 1.5f) {
+ // reflection
+ reflect(pathSegment, intersection, real_norm, material);
+ return;
+ }
+ else {
+ // refraction
+ pathSegment.ray.direction = glm::normalize(dir);
+ pathSegment.ray.origin = intersection + dir * 0.01f;
+ pathSegment.color *= material.specular.color;
+ return;
+ }
+ } else if (material.hasReflective == 1.f) {
+ reflect(pathSegment, intersection, normal, material);
+ return;
+ }
+ else {
+ glm::vec3 bounce_dir = calculateRandomDirectionInHemisphere(normal, rng);
+ pathSegment.ray.direction = bounce_dir;
+ pathSegment.ray.origin = intersection + 0.01f * normal;
+ pathSegment.color *= material.color;
+
+ return;
+ }
}
diff --git a/src/intersections.h b/src/intersections.h
index b1504071..b91ad547 100644
--- a/src/intersections.h
+++ b/src/intersections.h
@@ -5,7 +5,7 @@
#include "sceneStructs.h"
#include "utilities.h"
-
+#define BOUNDING_BOX 1
/**
* Handy-dandy hash function that provides seeds for random number generation.
*/
@@ -99,6 +99,7 @@ __host__ __device__ float boxIntersectionTest(Geom box, Ray r,
* @param outside Output param for whether the ray came from outside.
* @return Ray parameter `t` value. -1 if no intersection.
*/
+
__host__ __device__ float sphereIntersectionTest(Geom sphere, Ray r,
glm::vec3 &intersectionPoint, glm::vec3 &normal, bool &outside) {
float radius = .5;
@@ -142,3 +143,104 @@ __host__ __device__ float sphereIntersectionTest(Geom sphere, Ray r,
return glm::length(r.origin - intersectionPoint);
}
+
+
+__host__ __device__ glm::vec3 barycentricInterp(glm::vec3 p, glm::vec3 a, glm::vec3 b, glm::vec3 c) {
+ glm::vec3 v0 = b - a;
+ glm::vec3 v1 = c - a;
+ glm::vec3 v2 = p - a;
+ float prod_00 = glm::dot(v0, v0);
+ float prod_01 = glm::dot(v0, v1);
+ float prod_11 = glm::dot(v1, v1);
+ float prod_20 = glm::dot(v2, v0);
+ float prod_21 = glm::dot(v2, v1);
+ float denominator = prod_00 * prod_11 - prod_01 * prod_01;
+ float y = (prod_11 * prod_20 - prod_01 * prod_21) / denominator;
+ float z = (prod_00 * prod_21 - prod_01 * prod_20) / denominator;
+ float x = 1.0f - y - z;
+ return glm::vec3(x, y, z);
+}
+
+__host__ __device__ bool boundingBoxIntersectionTest(Geom box, Ray r) {
+ Ray q;
+ q.origin = multiplyMV(box.inverseTransform, glm::vec4(r.origin, 1.0f));
+ q.direction = glm::normalize(multiplyMV(box.inverseTransform, glm::vec4(r.direction, 0.0f)));
+
+ float tmin = -1e38f;
+ float tmax = 1e38f;
+ glm::vec3 tmin_n;
+ glm::vec3 tmax_n;
+ for (int xyz = 0; xyz < 3; ++xyz) {
+ float qdxyz = q.direction[xyz];
+ /*if (glm::abs(qdxyz) > 0.00001f)*/ {
+ float t1 = (box.min_coord[xyz] - q.origin[xyz]) / qdxyz;
+ float t2 = (box.max_coord[xyz] - q.origin[xyz]) / qdxyz;
+ float ta = glm::min(t1, t2);
+ float tb = glm::max(t1, t2);
+ glm::vec3 n;
+ n[xyz] = t2 < t1 ? +1 : -1;
+ if (ta > 0 && ta > tmin) {
+ tmin = ta;
+ tmin_n = n;
+ }
+ if (tb < tmax) {
+ tmax = tb;
+ tmax_n = n;
+ }
+ }
+ }
+
+ if (tmax >= tmin && tmax > 0) {
+ return true;
+ }
+ else {
+ return false;
+ }
+}
+
+__host__ __device__ float meshIntersectionTest(Geom mesh, Triangle* tris, Ray r,
+ glm::vec3& intersectionPoint, glm::vec3& normal, bool& outside) {
+#if BOUNDING_BOX
+ if (!boundingBoxIntersectionTest(mesh, r)) {
+ return -1;
+ }
+#endif
+ glm::vec3 ro = multiplyMV(mesh.inverseTransform, glm::vec4(r.origin, 1.0f));
+ glm::vec3 rd = glm::normalize(multiplyMV(mesh.inverseTransform, glm::vec4(r.direction, 0.0f)));
+
+ int min_idx = -1;
+ glm::vec3 min_coord(FLT_MAX, FLT_MAX, FLT_MAX);
+ for (int i = mesh.startidx; i < mesh.endidx; i++) {
+
+ Triangle tri = tris[i];
+ glm::vec3 baryPosition;
+
+ if (glm::intersectRayTriangle(ro, rd, tri.verts[0], tri.verts[1], tri.verts[2], baryPosition)) {
+ if (baryPosition[2] > 0.f && baryPosition[2] < min_coord[2]) {
+ min_coord = baryPosition;
+ min_idx = i;
+ }
+ }
+
+ }
+ if (min_idx == -1) {
+ return -1;
+ }
+ intersectionPoint = min_coord[2] * rd + ro;
+ intersectionPoint = multiplyMV(mesh.transform, glm::vec4(intersectionPoint, 1.f));
+ //min_coord = barycentricInterp(intersectionPoint, tris[min_idx].verts[0], tris[min_idx].verts[1], tris[min_idx].verts[2]);
+ //normal = glm::normalize(glm::cross(tris[min_idx].verts[0] - tris[min_idx].verts[1], tris[min_idx].verts[0] - tris[min_idx].verts[2]));
+ normal = min_coord[0] * tris[min_idx].normal[0]
+ + min_coord[1] * tris[min_idx].normal[1]
+ + (1.f - min_coord[0] - min_coord[1]) * tris[min_idx].normal[2];
+ normal = glm::normalize(multiplyMV(mesh.invTranspose, glm::vec4(normal, 0.f)));
+ if (glm::dot(rd, normal) > 0.f) {
+ outside = false;
+ }
+ else {
+ outside = true;
+ }
+ return glm::length(r.origin - intersectionPoint);
+
+}
+
diff --git a/src/main.cpp b/src/main.cpp
index fe8e85ec..3bac70ae 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -54,7 +54,6 @@ int main(int argc, char** argv) {
glm::vec3 up = cam.up;
glm::vec3 right = glm::cross(view, up);
up = glm::cross(right, view);
-
cameraPosition = cam.position;
// compute phi (horizontal) and theta (vertical) relative 3D axis
@@ -100,13 +99,16 @@ void saveImage() {
void runCuda() {
if (camchanged) {
- iteration = 0;
+ cout << "cam changed" << endl;
+ //iteration = 0;
Camera &cam = renderState->camera;
cameraPosition.x = zoom * sin(phi) * sin(theta);
cameraPosition.y = zoom * cos(theta);
cameraPosition.z = zoom * cos(phi) * sin(theta);
-
+ glm::vec3 tmp = cam.view;
cam.view = -glm::normalize(cameraPosition);
+ cam.move += (tmp - cam.view);
+ cout << "cam.move" << cam.move[0] << " " << cam.move[1] << " " << cam.move[2] << " " << endl;
glm::vec3 v = cam.view;
glm::vec3 u = glm::vec3(0, 1, 0);//glm::normalize(cam.up);
glm::vec3 r = glm::cross(v, u);
@@ -134,8 +136,18 @@ void runCuda() {
// execute the kernel
int frame = 0;
- pathtrace(pbo_dptr, frame, iteration);
+ // add timer
+ cudaEvent_t start, stop;
+ cudaEventCreate(&start);
+ cudaEventCreate(&stop);
+ cudaEventRecord(start);
+ pathtrace(pbo_dptr, frame, iteration);
+ cudaEventRecord(stop);
+ cudaEventSynchronize(stop);
+ float milliseconds = 0;
+ cudaEventElapsedTime(&milliseconds, start, stop);
+ //std::cout << milliseconds << std::endl;
// unmap buffer object
cudaGLUnmapBufferObject(pbo);
} else {
diff --git a/src/pathtrace.cu b/src/pathtrace.cu
index c1ec122c..8fec32e0 100644
--- a/src/pathtrace.cu
+++ b/src/pathtrace.cu
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
@@ -13,7 +14,6 @@
#include "pathtrace.h"
#include "intersections.h"
#include "interactions.h"
-
#define ERRORCHECK 1
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
@@ -38,6 +38,12 @@ void checkCUDAErrorFn(const char *msg, const char *file, int line) {
#endif
}
+#define SORTMATERIAL 0
+#define CACHE 0
+#define ANTIALISING 1
+#define DEPTH_OF_FIELD 0
+#define DIRECT_LIGHT 1
+
__host__ __device__
thrust::default_random_engine makeSeededRandomEngine(int iter, int index, int depth) {
int h = utilhash((1 << 31) | (depth << 22) | iter) ^ utilhash(index);
@@ -73,8 +79,15 @@ static Geom * dev_geoms = NULL;
static Material * dev_materials = NULL;
static PathSegment * dev_paths = NULL;
static ShadeableIntersection * dev_intersections = NULL;
+static Triangle * dev_tris = NULL;
+static int num_samples = 9;
+static float lens_r = 0.4f;
+static float f = 10.f;
// TODO: static variables for device memory, any extra info you need, etc
// ...
+#if CACHE
+static ShadeableIntersection* dev_intersections_cache = NULL;
+#endif
void pathtraceInit(Scene *scene) {
hst_scene = scene;
@@ -95,8 +108,14 @@ void pathtraceInit(Scene *scene) {
cudaMalloc(&dev_intersections, pixelcount * sizeof(ShadeableIntersection));
cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
- // TODO: initialize any extra device memeory you need
+ cudaMalloc(&dev_tris, scene->tris.size() * sizeof(Triangle));
+ cudaMemcpy(dev_tris, scene->tris.data(), scene->tris.size() * sizeof(Triangle), cudaMemcpyHostToDevice);
+ // TODO: initialize any extra device memeory you need
+#if CACHE
+ cudaMalloc(&dev_intersections_cache, pixelcount * sizeof(ShadeableIntersection));
+ cudaMemset(dev_intersections_cache, 0, pixelcount * sizeof(ShadeableIntersection));
+#endif
checkCUDAError("pathtraceInit");
}
@@ -106,6 +125,10 @@ void pathtraceFree() {
cudaFree(dev_geoms);
cudaFree(dev_materials);
cudaFree(dev_intersections);
+ cudaFree(dev_tris);
+#if CACHE
+ cudaFree(dev_intersections_cache);
+#endif
// TODO: clean up any extra device memory you created
checkCUDAError("pathtraceFree");
@@ -129,14 +152,28 @@ __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.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)
- );
-
+ segment.color = glm::vec3(1.0f, 1.0f, 1.0f);
+
+#if ANTIALISING
+ thrust::default_random_engine rng = makeSeededRandomEngine(iter, index, 0);
+ thrust::uniform_real_distribution u01(0, 1);
+ glm::vec3 view = cam.view;
+ float interp = u01(rng);
+ if (cam.move != glm::vec3(0.f)) {
+ view = cam.view * (1 - interp) + (cam.view - cam.move) * interp;
+ }
+
+ segment.ray.direction = glm::normalize(view
+ - cam.right * cam.pixelLength.x * ((float)x + u01(rng) - (float)cam.resolution.x * 0.5f)
+ - cam.up * cam.pixelLength.y * ((float)y + u01(rng) - (float)cam.resolution.y * 0.5f)
+ );
+
+#else
+ 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)
+ );
+#endif
segment.pixelIndex = index;
segment.remainingBounces = traceDepth;
}
@@ -147,12 +184,13 @@ __global__ void generateRayFromCamera(Camera cam, int iter, int traceDepth, Path
// Generating new rays is handled in your shader(s).
// Feel free to modify the code below.
__global__ void computeIntersections(
- int depth
+ const int depth
, int num_paths
, PathSegment * pathSegments
, Geom * geoms
, int geoms_size
, ShadeableIntersection * intersections
+ , Triangle * tris
)
{
int path_index = blockIdx.x * blockDim.x + threadIdx.x;
@@ -184,7 +222,12 @@ __global__ void computeIntersections(
else if (geom.type == SPHERE)
{
t = sphereIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
- }
+ }
+ else if (geom.type == MESH)
+ {
+ t = meshIntersectionTest(geom, tris, pathSegment.ray, tmp_intersect, tmp_normal, outside);
+ }
+
// TODO: add more intersection tests here... triangle? metaball? CSG?
// Compute the minimum t from the intersection tests to determine what
@@ -245,7 +288,11 @@ __global__ void shadeFakeMaterial (
// If the material indicates that the object was a light, "light" the ray
if (material.emittance > 0.0f) {
+#if DIRECT_LIGHT
+#else if
pathSegments[idx].color *= (materialColor * material.emittance);
+ pathSegments[idx].remainingBounces = 0;
+#endif
}
// Otherwise, do some pseudo-lighting computation. This is actually more
// like what you would expect from shading in a rasterizer like OpenGL.
@@ -254,6 +301,7 @@ __global__ void shadeFakeMaterial (
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
+ pathSegments[idx].remainingBounces--;
}
// If there was no intersection, color the ray black.
// Lots of renderers use 4 channel color, RGBA, where A = alpha, often
@@ -261,10 +309,60 @@ __global__ void shadeFakeMaterial (
// This can be useful for post-processing and image compositing.
} else {
pathSegments[idx].color = glm::vec3(0.0f);
+ pathSegments[idx].remainingBounces = 0;
}
}
}
+__global__ void shadeBSDF(
+ 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) {
+ 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);
+ pathSegments[idx].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 {
+ glm::vec3 intersect = pathSegments[idx].ray.origin + intersection.t * pathSegments[idx].ray.direction;
+
+ scatterRay(
+ pathSegments[idx],
+ intersect,
+ intersection.surfaceNormal,
+ material,
+ rng);
+
+ }
+ // 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);
+ pathSegments[idx].remainingBounces = 0;
+ }
+ }
+}
+
// Add the current iteration's output to the overall image
__global__ void finalGather(int nPaths, glm::vec3 * image, PathSegment * iterationPaths)
{
@@ -277,12 +375,70 @@ __global__ void finalGather(int nPaths, glm::vec3 * image, PathSegment * iterati
}
}
+struct bounce_end {
+ __host__ __device__ bool operator()(const PathSegment& seg) {
+ return seg.remainingBounces > 0;
+ }
+};
+
+struct sort_material {
+ __host__ __device__ bool operator()(const ShadeableIntersection& intersect1, const ShadeableIntersection& intersect2) {
+ return intersect1.materialId > intersect2.materialId;
+ }
+};
+
+__host__ __device__ glm::vec2 concentricSampleDisk(glm::vec2 u) {
+ glm::vec2 uOffset = 2.f * u - glm::vec2(1.f);
+ if (uOffset.x == 0 && uOffset.y == 0) {
+ return glm::vec2(0.f);
+ }
+ float theta, r;
+ float PiOver4 = PI / 4.f;
+ float PiOver2 = PI / 2.f;
+ if (std::abs(uOffset.x) > std::abs(uOffset.y)) {
+ r = uOffset.x;
+ theta = PiOver4 * (uOffset.y / uOffset.x);
+ }
+ else {
+ r = uOffset.y;
+ theta = PiOver2 - PiOver4 * (uOffset.x / uOffset.y);
+ }
+ return r * glm::vec2(std::cos(theta), std::sin(theta));
+
+}
+
+__global__ void updateRayForDepth(Camera cam, float f, float lens_r, PathSegment* pathSegments, int iter)
+{
+ int x = (blockIdx.x * blockDim.x) + threadIdx.x;
+ int y = (blockIdx.y * blockDim.y) + threadIdx.y;
+ if (x >= cam.resolution.x && y >= cam.resolution.y) {
+ return;
+ }
+ int index = x + (y * cam.resolution.x);
+ if (lens_r > 0.f) {
+ thrust::default_random_engine rng = makeSeededRandomEngine(iter, index, 0);
+ thrust::uniform_real_distribution u01(-1, 1);
+
+ float rand_x = u01(rng);
+ float rand_y = u01(rng);
+ glm::vec2 pLens = lens_r * concentricSampleDisk(glm::vec2(rand_x, rand_y));
+
+ float ft = glm::abs(f / pathSegments[index].ray.direction.z);
+ glm::vec3 pFocus = ft * pathSegments[index].ray.direction + pathSegments[index].ray.origin;
+
+ pathSegments[index].ray.origin += glm::vec3(pLens.x, pLens.y, 0);
+ pathSegments[index].ray.direction = glm::normalize(pFocus - pathSegments[index].ray.origin);
+ }
+
+}
/**
* 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 int traceDepth = 16;
+ //std::cout << traceDepth << std::endl;
const Camera &cam = hst_scene->state.camera;
const int pixelcount = cam.resolution.x * cam.resolution.y;
@@ -324,61 +480,107 @@ void pathtrace(uchar4 *pbo, int frame, int iter) {
// * Finally, add this iteration's results to the image. This has been done
// for you.
- // TODO: perform one iteration of path tracing
+ // perform one iteration of path tracing
generateRayFromCamera <<>>(cam, iter, traceDepth, dev_paths);
checkCUDAError("generate camera ray");
-
+#if DEPTH_OF_FIELD
+ updateRayForDepth << > > (cam, f, lens_r, dev_paths, iter);
+ checkCUDAError("depth of field");
+#endif
int depth = 0;
+ int ctr = 0;
PathSegment* dev_path_end = dev_paths + pixelcount;
int num_paths = dev_path_end - dev_paths;
// --- PathSegment Tracing Stage ---
// Shoot ray into scene, bounce between objects, push shading chunks
- bool iterationComplete = false;
- while (!iterationComplete) {
-
- // 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.
+ bool iterationComplete = false;
+ int num_paths_orig = num_paths;
+
+ while (!iterationComplete) {
+ //cout << depth << endl;
+ // clean shading chunks
+ cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
+ dim3 numblocksPathSegmentTracing = (num_paths + blockSize1d - 1) / blockSize1d;
+#if CACHE
+ if (iter <= 1 && depth == 0) {
+ computeIntersections << > > (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_tris
+ );
+ checkCUDAError("trace one bounce");
+ cudaDeviceSynchronize();
+ cudaMemcpy(dev_intersections_cache, dev_intersections,
+ pixelcount * sizeof(ShadeableIntersection), cudaMemcpyDeviceToDevice);
+ }
+ else if (depth == 0) {
+ cudaMemcpy(dev_intersections, dev_intersections_cache,
+ pixelcount * sizeof(ShadeableIntersection), cudaMemcpyDeviceToDevice);
+ }
+ else {
+ computeIntersections << > > (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_tris
+ );
+ checkCUDAError("trace one bounce");
+ cudaDeviceSynchronize();
+ }
+#else
+ // tracing
+ computeIntersections <<>> (
+ depth
+ , num_paths
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ , dev_tris
+ );
+ checkCUDAError("trace one bounce");
+ cudaDeviceSynchronize();
+#endif
+
+ depth++;
+ // --- 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.
+#if SORTMATERIAL
+ thrust::sort_by_key(thrust::device, dev_intersections, dev_intersections+num_paths, dev_paths, sort_material());
+#endif
+
+ shadeBSDF << > > (
+ iter,
+ num_paths,
+ dev_intersections,
+ dev_paths,
+ dev_materials
+ );
+ dev_path_end = thrust::stable_partition(thrust::device, dev_paths, dev_path_end, bounce_end());
+ if (dev_path_end == dev_paths) {
+ iterationComplete = true;
+ }
+ num_paths = dev_path_end - dev_paths;
}
// Assemble this iteration and apply it to the image
dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d;
- finalGather<<>>(num_paths, dev_image, dev_paths);
+ finalGather<<>>(num_paths_orig, dev_image, dev_paths);
///////////////////////////////////////////////////////////////////////////
diff --git a/src/scene.cpp b/src/scene.cpp
index cbae043c..54323d45 100644
--- a/src/scene.cpp
+++ b/src/scene.cpp
@@ -3,6 +3,10 @@
#include
#include
#include
+#define TINYOBJLOADER_IMPLEMENTATION
+#include "tiny_obj_loader.h"
+
+#define BOUNDING_BOX 1
Scene::Scene(string filename) {
cout << "Reading scene from " << filename << " ..." << endl;
@@ -32,6 +36,65 @@ Scene::Scene(string filename) {
}
}
+int Scene::loadOBJ(string line, glm::vec3& min_coord, glm::vec3& max_coord) {
+ tinyobj::attrib_t attrib;
+ std::vector shapes;
+ std::vector materials;
+ std::string warn;
+ std::string err;
+ bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, line.c_str());
+
+ if (!warn.empty()) {
+ std::cout << warn << std::endl;
+ }
+
+ if (!err.empty()) {
+ std::cerr << err << std::endl;
+ }
+
+ if (!ret) {
+ exit(1);
+ }
+ std::cout << "loadobj starting " << std::endl;
+ for (size_t s = 0; s < shapes.size(); s++) {
+ // Loop over faces(polygon)
+ size_t index_offset = 0;
+ for (size_t f = 0; f < shapes[s].mesh.num_face_vertices.size(); f++) {
+ int fv = shapes[s].mesh.num_face_vertices[f];
+ Triangle tri;
+ // Loop over vertices in the face.
+ for (size_t v = 0; v < fv; v++) {
+ // access to vertex
+ tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v];
+ tinyobj::real_t vx = attrib.vertices[3 * idx.vertex_index + 0];
+ tinyobj::real_t vy = attrib.vertices[3 * idx.vertex_index + 1];
+ tinyobj::real_t vz = attrib.vertices[3 * idx.vertex_index + 2];
+ tinyobj::real_t nx = attrib.normals[3 * idx.normal_index + 0];
+ tinyobj::real_t ny = attrib.normals[3 * idx.normal_index + 1];
+ tinyobj::real_t nz = attrib.normals[3 * idx.normal_index + 2];
+ tri.verts[v] = glm::vec3(vx, vy, vz);
+ tri.normal[v] = glm::vec3(nx, ny, nz);
+#if BOUNDING_BOX
+ min_coord[0] = fmin(min_coord[0], vx);
+ min_coord[1] = fmin(min_coord[1], vy);
+ min_coord[2] = fmin(min_coord[2], vz);
+ max_coord[0] = fmax(max_coord[0], vx);
+ max_coord[1] = fmax(max_coord[1], vy);
+ max_coord[2] = fmax(max_coord[2], vz);
+#endif
+ }
+ //tri.normal = glm::normalize(glm::cross(tri.verts[0] - tri.verts[1], tri.verts[0] - tri.verts[2]));
+ tris.push_back(tri);
+ index_offset += fv;
+
+ // per-face material
+ //shapes[s].mesh.material_ids[f];
+ }
+ }
+ std::cout << "part finished" << std::endl;
+ return 1;
+}
+
int Scene::loadGeom(string objectid) {
int id = atoi(objectid.c_str());
if (id != geoms.size()) {
@@ -44,6 +107,8 @@ int Scene::loadGeom(string objectid) {
//load object type
utilityCore::safeGetline(fp_in, line);
+ cout << line << endl;
+ //cout << strcmp(line.c_str(), "mesh");
if (!line.empty() && fp_in.good()) {
if (strcmp(line.c_str(), "sphere") == 0) {
cout << "Creating new sphere..." << endl;
@@ -52,6 +117,15 @@ int Scene::loadGeom(string objectid) {
cout << "Creating new cube..." << endl;
newGeom.type = CUBE;
}
+ else if (strcmp(line.c_str(), "mesh") == 0) {
+ newGeom.type = MESH;
+ newGeom.startidx = tris.size();
+ utilityCore::safeGetline(fp_in, line);
+ newGeom.min_coord = glm::vec3(FLT_MAX);
+ newGeom.max_coord = glm::vec3(FLT_MIN);
+ Scene::loadOBJ(line, newGeom.min_coord, newGeom.max_coord);
+ newGeom.endidx = tris.size();
+ }
}
//link material
@@ -94,7 +168,7 @@ int Scene::loadCamera() {
RenderState &state = this->state;
Camera &camera = state.camera;
float fovy;
-
+ camera.move = glm::vec3(0.f);
//load static properties
for (int i = 0; i < 5; i++) {
string line;
@@ -112,6 +186,7 @@ int Scene::loadCamera() {
} else if (strcmp(tokens[0].c_str(), "FILE") == 0) {
state.imageName = tokens[1];
}
+
}
string line;
@@ -125,6 +200,9 @@ int Scene::loadCamera() {
} 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(), "MOVE") == 0) {
+ camera.move = glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()));
+ }
utilityCore::safeGetline(fp_in, line);
}
diff --git a/src/scene.h b/src/scene.h
index f29a9171..b96c25fc 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -7,7 +7,6 @@
#include "glm/glm.hpp"
#include "utilities.h"
#include "sceneStructs.h"
-
using namespace std;
class Scene {
@@ -16,11 +15,13 @@ class Scene {
int loadMaterial(string materialid);
int loadGeom(string objectid);
int loadCamera();
+ int loadOBJ(string line, glm::vec3& min_coord, glm::vec3& max_coord);
public:
Scene(string filename);
~Scene();
std::vector geoms;
std::vector materials;
+ std::vector tris;
RenderState state;
};
diff --git a/src/sceneStructs.h b/src/sceneStructs.h
index b38b8204..4ec0beea 100644
--- a/src/sceneStructs.h
+++ b/src/sceneStructs.h
@@ -10,8 +10,16 @@
enum GeomType {
SPHERE,
CUBE,
+ MESH,
};
+
+struct Triangle {
+ glm::vec3 verts[3];
+ glm::vec3 normal[3];
+};
+
+
struct Ray {
glm::vec3 origin;
glm::vec3 direction;
@@ -26,6 +34,10 @@ struct Geom {
glm::mat4 transform;
glm::mat4 inverseTransform;
glm::mat4 invTranspose;
+ int startidx;
+ int endidx;
+ glm::vec3 min_coord;
+ glm::vec3 max_coord;
};
struct Material {
@@ -49,6 +61,7 @@ struct Camera {
glm::vec3 right;
glm::vec2 fov;
glm::vec2 pixelLength;
+ glm::vec3 move;
};
struct RenderState {
diff --git a/src/tiny_obj_loader.h b/src/tiny_obj_loader.h
new file mode 100644
index 00000000..edd18f58
--- /dev/null
+++ b/src/tiny_obj_loader.h
@@ -0,0 +1,3082 @@
+/*
+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 2.0.0 : Add new object oriented API. 1.x API is still provided.
+// * Support line primitive.
+// * Support points primitive.
+// * Support multiple search path for .mtl(v1 API).
+// * Support vertex weight `vw`(as an tinyobj extension)
+// version 1.4.0 : Modifed ParseTextureNameAndOption API
+// version 1.3.1 : Make ParseTextureNameAndOption API public
+// version 1.3.0 : Separate warning and error message(breaking API of LoadObj)
+// 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