diff --git a/README.md b/README.md
index 110697c..33bdb8c 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,111 @@
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)
+* Daniel Daley-Montgomery
+* Tested on: MacBook Pro, OSX 10.12, i7 @ 2.3GHz, 16GB RAM, GT 750M 2048MB (Personal Machine)
+
+ High-speed rasterization pipelines like OpenGL might be able to put a lot of images on the screen in short time, but when it comes to true physical realism, path tracing is king. Used by modern movie studios, path tracing models rays of light by tracing them in reverse from the camera eye to light sources, bouncing around the scene in the process. This allows for the capture of indirect light, as shown below.
+
+
+
+ The problem with path tracing is that, at its best, it's extrmely slow. This project will aim to accelerate a path tracing system by parallelizing over light rays on the GPU. At a high level, the program will look like this:
+```
+//Generate rays
+for each pixel in parallel {
+ new path (x,y)
+}
+
+//Bounce around the scene
+while (active paths exist) {
+
+ //path collisions
+ for each path in parallel {
+ path.collide(scene)
+
+ //shading
+ if (collision) {
+ path.color(collision)
+ if (isLight(collision) path.deactivate
+ path.redirect(collision, random variable)
+ }
+
+ else path.deactivate
+` }
+}
+
+for each path in parallel {
+ screen.add(path.color)
+}
+```
+
+
+### Path Generation and Collision
+
+ While writing *path.collide(scene)* is easy, this is the most performance-intensive part of my path tracer. The basic approach -
+ ```
+ for each path in parallel {
+ for each primitive in parallel {
+ detect collision (path, primitive)
+ }
+ }
+ ```
+- is ghastly. With a 900x900 scene, 2,000 primitives, and 12 bounces I could have as many as *19.4 million* collision tests to do in one iteration in the worst case. Fortunately, we've got a few optimizations to speed things up:
+
+
+
+###### Stream Compaction
+
+ First, that worst case should never have to happen when we can stop tracing paths that hit lights or fly into space. But we dont want to launch kernels that check *if (path is dead) return;*, because if a warp of 31 dead paths and 1 active path is launched, it will take just as long as 32 active paths... the last bounce will be as costly as the first!
+
+ Instead, I specifically separated active paths from inactive ones using *stream compaction*, provided by the [thrust](https://developer.nvidia.com/thrust) library, and was thus able to only launch as many threads as was necessary to cover the active ones.
+
+###### Caching
+
+ One easy way to save a round of intersections tests is caching the first bounce. Since we're casting them through pixels, they'll always hit the same location. I accurately simulated the first bounce, saved it to device memory, then was able to start on the second bounce every subsequent frame.
+
+ When I decided I wanted anti aliasing, this became an issue. A jittered antialiasing solution would change my first bounce, rendering my cache incorrect. To get the best of both worlds, I jittered and re-filled my cache every *x* frames. This means that after the first bounce my caching is *1/x*% less effective, but effective nonetheless.
+
+###### Primitive Reduction
+
+ While 5 or 6 boxes or spheres can be practically nigligible in terms of compute time, even my <1000 triangle elephant blew up iteration time. As the first step to cutting down this nested path-geometry relationship, i implemented a bounding box for my mesh imports. If a ray didn't hit the bounding box (or already had a closer hit), it ignored the rest of the mesh as well.
+
+ While this was extremely helpful, it won't get my CUDA path tracer to any competitive speeds. Instead, my next improvement will be to the entire scene: A [bounding volume heirarchy](https://en.wikipedia.org/wiki/Octree) accesible on the GPU. Like [light clustering](https://github.com/illDivino/Project5-WebGL-Clustered-Deferred-Forward-Plus), I predict this will have a massive impact.
+
+#### Shading and Redirection
+
+ Once we have a collision, we can get to the best part. At the shading stage, we try our best to represent physical meterials and their light reflection/absorbtion/transmission/emission tendencies. In my project, I included the following three material properties, which could be blended together by assigning the likelihood that a given collision would chose any lighting model. Pictured as well are the [BRDFs](https://en.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function) used to represent the new direction of an incident ray:
+
+|Perfect Difusse|Perfect Specular|Fresnel Refractive|
+|-----|-----|-----|
+|
|
|
|
+|
|
|
|
+
+ Because different materials have different amounts of work involved in processing (and also different likelihoods of deactivating a ray on this iteration or the next), we can prevent even more divergence if we sort rays by the material they hit.
+
+ The level of randomness in choosing in a direction necessarily produces noise in the output image. While it's impossible to sample the continuous hemisphere around a normal, we can get a good picture given enough random samples:
+
+|Iterations|Result|
+|-----|-----|
+3||
+10||
+25||
+50||
+200||
+1000||
+
+This is the '[Monte Carlo](https://en.wikipedia.org/wiki/Monte_Carlo_method)' in Monte Carlo Path Tracing. Various techniques, like bidirectional, direct, and importance sampling can help accelerate convergence to the final image. These methods have unfortunately not yet been implemented in my project.
+
+##### Performance
-### (TODO: Your README)
+The following stats were captured with the test image at the top of this wiki. Notably, his image had a wall behind the camera preventing rays from being deactivated due to no collision.
-*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.
+
+
+ With any mesh in the scene, the far-and-away most important improvement is bounding boxes. For paths who do not intersect the mesh, this reduced intersection tests by 95%, especially influential when more than %90 of my GPU time was spent in generating collisions.
+
+ Surprisingly, the materials sorting was influential even though my materials were rather simple. My guess is that the important separation was between those rays that hit the mesh (gold material) and those that didn't, since those that did would be guarunteed to hit the mesh's bounding box on the next collision generation.
diff --git a/img/12Bounce.png b/img/12Bounce.png
new file mode 100644
index 0000000..ebdbf7f
Binary files /dev/null and b/img/12Bounce.png differ
diff --git a/img/5Bounce.png b/img/5Bounce.png
new file mode 100644
index 0000000..1f3eb32
Binary files /dev/null and b/img/5Bounce.png differ
diff --git a/img/Cornell_10.png b/img/Cornell_10.png
new file mode 100644
index 0000000..27e564f
Binary files /dev/null and b/img/Cornell_10.png differ
diff --git a/img/Cornell_1000.png b/img/Cornell_1000.png
new file mode 100644
index 0000000..c2e6f9d
Binary files /dev/null and b/img/Cornell_1000.png differ
diff --git a/img/Cornell_200.png b/img/Cornell_200.png
new file mode 100644
index 0000000..5891d4f
Binary files /dev/null and b/img/Cornell_200.png differ
diff --git a/img/Cornell_25.png b/img/Cornell_25.png
new file mode 100644
index 0000000..ad5d2c1
Binary files /dev/null and b/img/Cornell_25.png differ
diff --git a/img/Cornell_3.png b/img/Cornell_3.png
new file mode 100644
index 0000000..69ff148
Binary files /dev/null and b/img/Cornell_3.png differ
diff --git a/img/Cornell_50.png b/img/Cornell_50.png
new file mode 100644
index 0000000..aeb586b
Binary files /dev/null and b/img/Cornell_50.png differ
diff --git a/img/DeerAndBall50.png b/img/DeerAndBall50.png
new file mode 100644
index 0000000..468d17a
Binary files /dev/null and b/img/DeerAndBall50.png differ
diff --git a/img/Diff.png b/img/Diff.png
new file mode 100644
index 0000000..9e5c21c
Binary files /dev/null and b/img/Diff.png differ
diff --git a/img/EarlyGlass.png b/img/EarlyGlass.png
new file mode 100644
index 0000000..41ec192
Binary files /dev/null and b/img/EarlyGlass.png differ
diff --git a/img/Normals.png b/img/Normals.png
new file mode 100644
index 0000000..9661cad
Binary files /dev/null and b/img/Normals.png differ
diff --git a/img/Refr.png b/img/Refr.png
new file mode 100644
index 0000000..729845c
Binary files /dev/null and b/img/Refr.png differ
diff --git a/img/Screen Shot 2017-12-09 at 5.45.24 PM.png b/img/Screen Shot 2017-12-09 at 5.45.24 PM.png
new file mode 100644
index 0000000..991e88b
Binary files /dev/null and b/img/Screen Shot 2017-12-09 at 5.45.24 PM.png differ
diff --git a/img/Screen Shot 2017-12-09 at 5.45.47 PM.png b/img/Screen Shot 2017-12-09 at 5.45.47 PM.png
new file mode 100644
index 0000000..f42dd1a
Binary files /dev/null and b/img/Screen Shot 2017-12-09 at 5.45.47 PM.png differ
diff --git a/img/Screen Shot 2017-12-09 at 5.46.01 PM.png b/img/Screen Shot 2017-12-09 at 5.46.01 PM.png
new file mode 100644
index 0000000..0e8c738
Binary files /dev/null and b/img/Screen Shot 2017-12-09 at 5.46.01 PM.png differ
diff --git a/img/Spec.png b/img/Spec.png
new file mode 100644
index 0000000..ed69287
Binary files /dev/null and b/img/Spec.png differ
diff --git a/img/cornell.2017-11-10_04-26-44z.181samp.png b/img/cornell.2017-11-10_04-26-44z.181samp.png
new file mode 100644
index 0000000..dd51121
Binary files /dev/null and b/img/cornell.2017-11-10_04-26-44z.181samp.png differ
diff --git a/img/elephant1001.png b/img/elephant1001.png
new file mode 100644
index 0000000..4fac01d
Binary files /dev/null and b/img/elephant1001.png differ
diff --git a/img/stream.png b/img/stream.png
new file mode 100644
index 0000000..aa46a52
Binary files /dev/null and b/img/stream.png differ
diff --git a/scenes/Wallpaper.txt b/scenes/Wallpaper.txt
new file mode 100644
index 0000000..1521f0d
--- /dev/null
+++ b/scenes/Wallpaper.txt
@@ -0,0 +1,172 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 1
+
+// Diffuse white
+MATERIAL 1
+RGB .98 .98 .98
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse red
+MATERIAL 2
+RGB .85 .35 .35
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse blue
+MATERIAL 3
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Gold
+MATERIAL 4
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB .35 .85 .35
+REFL 1.0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Gold
+MATERIAL 5
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB .35 .85 .35
+REFL 0.2
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// refractive blue
+MATERIAL 6
+RGB .35 .85 .35
+SPECEX 0
+SPECRGB .35 .85 .35
+REFL 0.0
+REFR 1.0
+REFRIOR 1.5
+EMITTANCE 0
+
+// Diffuse blue
+MATERIAL 7
+RGB .35 .35 .85
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 2880 1800
+FOVY 45
+ITERATIONS 5000
+DEPTH 8
+FILE cornell
+EYE 0.0 5 1.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 10 .03 10
+
+// 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 .1 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 4
+TRANS 0 7.5 -4
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 7
+sphere
+material 5
+TRANS 0 5 -4
+ROTAT 0 0 0
+SCALE 3 3 3
+
+// Sphere
+OBJECT 8
+sphere
+material 6
+TRANS 0 2.5 -4
+ROTAT 0 0 0
+SCALE 3 3 3
+
+//Front wall
+OBJECT 9
+cube
+material 1
+TRANS 0 5 5
+ROTAT 0 90 0
+SCALE .1 100 100
\ No newline at end of file
diff --git a/scenes/cornell.txt b/scenes/cornell.txt
index 83ff820..7afb4f5 100644
--- a/scenes/cornell.txt
+++ b/scenes/cornell.txt
@@ -6,13 +6,13 @@ SPECRGB 0 0 0
REFL 0
REFR 0
REFRIOR 0
-EMITTANCE 5
+EMITTANCE 3.75
// Diffuse white
MATERIAL 1
RGB .98 .98 .98
SPECEX 0
-SPECRGB 0 0 0
+SPECRGB 1 1 1
REFL 0
REFR 0
REFRIOR 0
@@ -22,7 +22,7 @@ EMITTANCE 0
MATERIAL 2
RGB .85 .35 .35
SPECEX 0
-SPECRGB 0 0 0
+SPECRGB 1 1 1
REFL 0
REFR 0
REFRIOR 0
@@ -32,30 +32,50 @@ EMITTANCE 0
MATERIAL 3
RGB .35 .85 .35
SPECEX 0
-SPECRGB 0 0 0
+SPECRGB 1 1 1
REFL 0
REFR 0
REFRIOR 0
EMITTANCE 0
-// Specular white
+// Gold
MATERIAL 4
-RGB .98 .98 .98
+RGB 1 1 1
SPECEX 0
-SPECRGB .98 .98 .98
+SPECRGB 0.8 0.7 0.43
REFL 1
REFR 0
REFRIOR 0
EMITTANCE 0
+// refractive blue
+MATERIAL 5
+RGB 0.75 0.75 1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0.0
+REFR 1.0
+REFRIOR 1.33
+EMITTANCE 0
+
+// black
+MATERIAL 6
+RGB 0.1 0.1 0.1
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0.3
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
// Camera
CAMERA
-RES 800 800
+RES 900 900
FOVY 45
ITERATIONS 5000
-DEPTH 8
+DEPTH 7
FILE cornell
-EYE 0.0 5 10.5
+EYE 0.0 5 4.8
LOOKAT 0 5 0
UP 0 1 0
@@ -64,9 +84,9 @@ UP 0 1 0
OBJECT 0
cube
material 0
-TRANS 0 10 0
+TRANS 0 9.8 -2
ROTAT 0 0 0
-SCALE 3 .3 3
+SCALE 4 .1 4
// Floor
OBJECT 1
@@ -76,24 +96,17 @@ 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
+OBJECT 2
cube
material 1
TRANS 0 5 -5
ROTAT 0 90 0
-SCALE .01 10 10
+SCALE .1 10 10
+
// Left wall
-OBJECT 4
+OBJECT 3
cube
material 2
TRANS -5 5 0
@@ -101,17 +114,48 @@ ROTAT 0 0 0
SCALE .01 10 10
// Right wall
-OBJECT 5
+OBJECT 4
cube
material 3
TRANS 5 5 0
ROTAT 0 0 0
SCALE .01 10 10
-// Sphere
+//Front wall
+OBJECT 5
+cube
+material 1
+TRANS 0 5 6
+ROTAT 0 90 0
+SCALE 0.01 100 100
+
+//Sphere 2
OBJECT 6
sphere
-material 4
-TRANS -1 4 -1
+material 5
+TRANS -1.2 3.6 -1
ROTAT 0 0 0
-SCALE 3 3 3
+SCALE 2.5 2.5 2.5
+
+// Ceiling
+OBJECT 7
+cube
+material 1
+TRANS 0 10 0
+ROTAT 0 0 0
+SCALE 10 0.1 10
+
+OBJECT 8
+cube
+material 1
+TRANS 0 1 -1
+ROTAT 0 0 0
+SCALE 7 2 4
+
+// Sphere
+OBJECT 9
+C:\Users\Alex\Documents\565\Project3-CUDA-Path-Tracer\scenes\elephav.obj
+material 4
+TRANS 1.7 2 -1.8
+ROTAT 0 20 0
+SCALE .0035 .0035 .0035
\ No newline at end of file
diff --git a/scenes/outside.txt b/scenes/outside.txt
new file mode 100644
index 0000000..6a27e13
--- /dev/null
+++ b/scenes/outside.txt
@@ -0,0 +1,125 @@
+// Emissive material (light)
+MATERIAL 0
+RGB 1 1 1
+SPECEX 0
+SPECRGB 0 0 0
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 2.5
+
+// Diffuse gray
+MATERIAL 1
+RGB .8 .8 .8
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// Diffuse orange
+MATERIAL 2
+RGB .9 .4 .5
+SPECEX 0
+SPECRGB 1 1 1
+REFL 0.1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// chrome
+MATERIAL 3
+RGB 1 1 1
+SPECEX 0
+SPECRGB .95 .95 .95
+REFL 1
+REFR 0
+REFRIOR 0
+EMITTANCE 0
+
+// green
+MATERIAL 4
+RGB .6 1 .6
+SPECEX 0
+SPECRGB .95 .95 .95
+REFL 1
+REFR 1
+REFRIOR 1.55
+EMITTANCE 0
+
+// Camera
+CAMERA
+RES 900 900
+FOVY 45
+ITERATIONS 5000
+DEPTH 16
+FILE cornell
+EYE 0.0 1 4
+LOOKAT 0 3 0
+UP 0 1 0
+
+
+// Ceiling light
+OBJECT 0
+sphere
+material 0
+TRANS 0 23 0
+ROTAT 0 0 0
+SCALE 15 15 15
+
+// Floor
+OBJECT 1
+cube
+material 1
+TRANS 0 -0.5 0
+ROTAT 0 0 0
+SCALE 100 1 100
+
+//Sphere 2
+OBJECT 2
+sphere
+material 2
+TRANS 2 2 -2
+ROTAT 0 0 0
+SCALE 4 4 4
+
+//Sphere 3
+OBJECT 3
+sphere
+material 1
+TRANS 0 2 -2
+ROTAT 0 0 0
+SCALE 100 100 100
+
+//Sphere 2
+OBJECT 4
+sphere
+material 2
+TRANS 2 4.75 -2
+ROTAT 0 0 0
+SCALE 1.5 1.5 1.5
+
+//Sphere 2
+OBJECT 5
+sphere
+material 2
+TRANS 2 5.78 -2
+ROTAT 0 0 0
+SCALE .56 .56 .56
+
+//Sphere 2
+OBJECT 6
+sphere
+material 3
+TRANS -2 2.25 -4
+ROTAT 0 0 0
+SCALE 4.5 4.5 4.5
+
+//Sphere 2
+OBJECT 7
+sphere
+material 3
+TRANS 0 1 1
+ROTAT 0 0 0
+SCALE 2 2 2
diff --git a/scenes/sphere.txt b/scenes/sphere.txt
index a74b545..348cc9e 100644
--- a/scenes/sphere.txt
+++ b/scenes/sphere.txt
@@ -1,6 +1,6 @@
// Emissive material (light)
MATERIAL 0
-RGB 1 1 1
+RGB 1 0 0
SPECEX 0
SPECRGB 0 0 0
REFL 0
diff --git a/src/interactions.h b/src/interactions.h
index 5ce3628..0d720a9 100644
--- a/src/interactions.h
+++ b/src/interactions.h
@@ -1,7 +1,7 @@
#pragma once
#include "intersections.h"
-
+#define DISPLAY_NORMALS 0
// CHECKITOUT
/**
* Computes a cosine-weighted random direction in a hemisphere.
@@ -73,7 +73,65 @@ void scatterRay(
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.
+
+
+#if DISPLAY_NORMALS
+ pathSegment.remainingBounces = 0;
+ pathSegment.color = abs(normal);
+#else
+
+ pathSegment.ray.origin = intersect;
+ thrust::uniform_real_distribution u01;
+ float rand = u01(rng);
+
+ //refraction
+ if (m.hasRefractive) {
+
+ float coeff = 0;
+ float cosTheta = -glm::dot(pathSegment.ray.direction, normal);
+ bool incomingFromOutside = (pathSegment.insideT == 0);
+
+ //get indices of refraction and r0 for Schlick's
+ float n1 = incomingFromOutside ? 1 : m.indexOfRefraction;
+ float n2 = incomingFromOutside ? m.indexOfRefraction : 1;
+ float n = n1 / n2;
+ float R0 = (n1 - n2) / (n1 + n2);
+ R0 *= R0;
+
+ if (n1 > n2) {
+ float sinT2 = n*n*(1.0 - cosTheta*cosTheta);
+ cosTheta = sqrt(1.0 - sinT2);
+ // Total internal reflection
+ if (sinT2 > 1.0f)
+ coeff = 1.0f;
+ }
+
+ if (coeff == 0.0f) {
+ float x = 1.0 - cosTheta;
+ coeff = R0 + (1.0 - R0)*x*x*x*x*x;
+ }
+
+ if (rand > coeff) {
+ pathSegment.ray.origin += 0.01f * pathSegment.ray.direction;
+ pathSegment.ray.direction = glm::refract(pathSegment.ray.direction, normal, n);
+ if (!incomingFromOutside) {
+ glm::vec3 c_absorb = (glm::vec3(1.1f) - m.color);
+ glm::vec3 absorb = glm::clamp(glm::exp(-c_absorb * 0.33f * pathSegment.insideT), glm::vec3(0.0f), glm::vec3(1.0f));
+ pathSegment.color *= m.color;
+ }
+ } else {
+ pathSegment.ray.direction = glm::reflect(pathSegment.ray.direction, normal);
+ pathSegment.color *= m.specular.color;
+ }
+
+ }
+ //diffuse
+ else if (rand > m.hasReflective) {
+ pathSegment.color *= m.color;
+ pathSegment.ray.direction = calculateRandomDirectionInHemisphere(normal, rng);
+ } else {
+ pathSegment.ray.direction = glm::reflect(pathSegment.ray.direction, normal);
+ pathSegment.color *= m.specular.color;
+ }
+#endif
}
diff --git a/src/intersections.h b/src/intersections.h
index 6f23872..d857834 100644
--- a/src/intersections.h
+++ b/src/intersections.h
@@ -89,6 +89,17 @@ __host__ __device__ float boxIntersectionTest(Geom box, Ray r,
return -1;
}
+__device__ float triangleIntersectionTest(Geom triangle, Ray r, glm::vec3 &intersectionPoint, glm::vec3 &normal, bool &outside) {
+
+ glm::vec3 result;
+ glm::intersectRayTriangle(r.origin, r.direction, triangle.vertices[0], triangle.vertices[1], triangle.vertices[2], result);
+
+ intersectionPoint = result.z * r.direction + r.origin;
+ outside = glm::dot(r.direction, triangle.normal) < 0;
+ normal = outside ? triangle.normal : - triangle.normal;
+ return result.z;
+}
+
// CHECKITOUT
/**
* Test intersection between a ray and a transformed sphere. Untransformed,
diff --git a/src/main.cpp b/src/main.cpp
index fe8e85e..f90168b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,7 @@
#include "main.h"
#include "preview.h"
#include
+#include
static std::string startTimeString;
@@ -22,6 +23,7 @@ glm::vec3 ogLookAt; // for recentering the camera
Scene *scene;
RenderState *renderState;
int iteration;
+double total;
int width;
int height;
@@ -134,7 +136,13 @@ void runCuda() {
// execute the kernel
int frame = 0;
- pathtrace(pbo_dptr, frame, iteration);
+ clock_t begin = clock();
+ pathtrace(pbo_dptr, frame, iteration);
+ clock_t end = clock();
+
+ total += (end - begin) * 1000.0f / CLOCKS_PER_SEC;
+ std::cout << iteration << ": " << total / (CLOCKS_PER_SEC) << std::endl;
+ total = 0;
// unmap buffer object
cudaGLUnmapBufferObject(pbo);
diff --git a/src/pathtrace.cu b/src/pathtrace.cu
index c1ec122..a0eea8d 100644
--- a/src/pathtrace.cu
+++ b/src/pathtrace.cu
@@ -4,6 +4,8 @@
#include
#include
#include
+#include
+#include
#include "sceneStructs.h"
#include "scene.h"
@@ -14,7 +16,9 @@
#include "intersections.h"
#include "interactions.h"
-#define ERRORCHECK 1
+#define ERRORCHECK 0
+#define SORT_MATERIALS 1
+#define CACHE_PATHS 1
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define checkCUDAError(msg) checkCUDAErrorFn(msg, FILENAME, __LINE__)
@@ -71,8 +75,14 @@ static Scene * hst_scene = NULL;
static glm::vec3 * dev_image = NULL;
static Geom * dev_geoms = NULL;
static Material * dev_materials = NULL;
+static PathSegment* dev_cached_paths = NULL;
static PathSegment * dev_paths = NULL;
+static ShadeableIntersection * dev_cached_intersections = NULL;
static ShadeableIntersection * dev_intersections = NULL;
+
+thrust::device_ptr dev_thrust_paths;
+thrust::device_ptr dev_thrust_intersections;
+
// TODO: static variables for device memory, any extra info you need, etc
// ...
@@ -85,16 +95,21 @@ void pathtraceInit(Scene *scene) {
cudaMemset(dev_image, 0, pixelcount * sizeof(glm::vec3));
cudaMalloc(&dev_paths, pixelcount * sizeof(PathSegment));
+ cudaMalloc(&dev_cached_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_materials, scene->materials.size() * sizeof(Material));
cudaMemcpy(dev_materials, scene->materials.data(), scene->materials.size() * sizeof(Material), cudaMemcpyHostToDevice);
-
+
+ cudaMalloc(&dev_cached_intersections, pixelcount * sizeof(ShadeableIntersection));
cudaMalloc(&dev_intersections, pixelcount * sizeof(ShadeableIntersection));
cudaMemset(dev_intersections, 0, pixelcount * sizeof(ShadeableIntersection));
+ dev_thrust_paths = thrust::device_ptr(dev_paths);
+ dev_thrust_intersections = thrust::device_ptr(dev_intersections);
+
// TODO: initialize any extra device memeory you need
checkCUDAError("pathtraceInit");
@@ -129,12 +144,15 @@ __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);
+ segment.color = glm::vec3(1.0f, 1.0f, 1.0f);
+
+ thrust::default_random_engine rng = makeSeededRandomEngine(iter, index, 0);
+ thrust::uniform_real_distribution u01(0, 1);
// 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)
+ - 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)
);
segment.pixelIndex = index;
@@ -166,7 +184,7 @@ __global__ void computeIntersections(
glm::vec3 normal;
float t_min = FLT_MAX;
int hit_geom_index = -1;
- bool outside = true;
+ bool isOutside = true;
glm::vec3 tmp_intersect;
glm::vec3 tmp_normal;
@@ -175,6 +193,7 @@ __global__ void computeIntersections(
for (int i = 0; i < geoms_size; i++)
{
+ bool outside;
Geom & geom = geoms[i];
if (geom.type == CUBE)
@@ -185,6 +204,19 @@ __global__ void computeIntersections(
{
t = sphereIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
}
+ else if (geom.type == TRIANGLE) {
+ t = triangleIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
+ }
+ else if (geom.type = BB) {
+ t = boxIntersectionTest(geom, pathSegment.ray, tmp_intersect, tmp_normal, outside);
+ if (t <= 0.0 || t > t_min) {
+ i += geom.numTris;
+ //hit_geom_index = -1;
+ //break;
+ }
+ continue;
+ }
+
// TODO: add more intersection tests here... triangle? metaball? CSG?
// Compute the minimum t from the intersection tests to determine what
@@ -195,19 +227,24 @@ __global__ void computeIntersections(
hit_geom_index = i;
intersect_point = tmp_intersect;
normal = tmp_normal;
+ isOutside = outside;
}
}
-
+
if (hit_geom_index == -1)
{
intersections[path_index].t = -1.0f;
+ pathSegments[path_index].remainingBounces = 0;
}
else
{
//The ray hits something
+ //pathSegment.remainingBounces--;
intersections[path_index].t = t_min;
+ intersections[path_index].point = intersect_point;
intersections[path_index].materialId = geoms[hit_geom_index].materialid;
intersections[path_index].surfaceNormal = normal;
+ pathSegments[path_index].insideT = isOutside ? 0.0f : t_min;
}
}
}
@@ -221,48 +258,53 @@ __global__ void computeIntersections(
// 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
+__global__ void shadeFakeMaterial(
+ int iter
+ , int num_paths
, ShadeableIntersection * shadeableIntersections
, PathSegment * pathSegments
, Material * materials
- )
+ , glm::vec3 *image
+)
{
- 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);
- }
- }
+ 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) {
+ image[pathSegments[idx].pixelIndex] += pathSegments[idx].color * (materialColor * material.emittance);
+ pathSegments[idx].remainingBounces = 0;
+ }
+ // Otherwise, color and bounce
+ else {
+ scatterRay(pathSegments[idx], intersection.point, intersection.surfaceNormal, material, rng);
+#if DISPLAY_NORMALS
+ image[pathSegments[idx].pixelIndex] += pathSegments[idx].color;
+#endif
+ pathSegments[idx].remainingBounces--;
+ }
+ // 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 {
+ //image[pathSegments[idx].pixelIndex] += glm::vec3(0.0f);
+ //pathSegments[idx].color = glm::vec3(0.0f);
+ pathSegments[idx].remainingBounces = 0;
+ }
+ }
}
// Add the current iteration's output to the overall image
@@ -277,117 +319,112 @@ __global__ void finalGather(int nPaths, glm::vec3 * image, PathSegment * iterati
}
}
+//from https://thrust.github.io/doc/group__stream__compaction.html#ga307d7f64566909172a3f9e16b7e2ad53
+struct path_complete {
+ __host__ __device__ bool operator()(PathSegment p) {
+ return p.remainingBounces <= 0;
+ }
+};
+
+//from http://www.sgi.com/tech/stl/StrictWeakOrdering.html and the above link
+struct matComparator
+{
+ __host__ __device__ bool operator()(ShadeableIntersection &isThis, ShadeableIntersection &lessThanThis)
+ {
+ return isThis.materialId < lessThanThis.materialId;
+ }
+};
+
/**
* 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;
+ dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d;
+
+ //perform first bounce OR load cached first bounce
+#if CACHE_PATHS
+ //store the initial paths and intersections, which will remain constant until camera moves (at which point iter == 0)
+ //currently jitters rays every 25 passes for antialiasing
+ if (iter % 25 == 1) {
+ generateRayFromCamera << > > (cam, iter, traceDepth, dev_cached_paths);
+ computeIntersections << > > (0, pixelcount, dev_cached_paths, dev_geoms,
+ hst_scene->geoms.size(), dev_cached_intersections);
+ cudaDeviceSynchronize();
+ }
+ cudaMemcpy(dev_paths, dev_cached_paths, pixelcount * sizeof(PathSegment), cudaMemcpyDefault);
+ cudaMemcpy(dev_intersections, dev_cached_intersections, pixelcount * sizeof(ShadeableIntersection), cudaMemcpyDefault);
+#else
+ generateRayFromCamera <<>>(cam, iter, traceDepth, dev_paths);
+ computeIntersections << > > (
+ 0
+ , pixelcount
+ , dev_paths
+ , dev_geoms
+ , hst_scene->geoms.size()
+ , dev_intersections
+ );
+#endif
+ checkCUDAError("First Bounce");
- ///////////////////////////////////////////////////////////////////////////
-
- // 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);
- checkCUDAError("generate camera ray");
-
- int depth = 0;
+ int depth = 1;
PathSegment* dev_path_end = dev_paths + pixelcount;
int num_paths = dev_path_end - dev_paths;
+ bool iterationComplete = false;
+ dim3 numblocksPathSegmentTracing = numBlocksPixels;
- // --- 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));
+#if SORT_MATERIALS
+ thrust::sort_by_key(dev_thrust_intersections, dev_thrust_intersections + num_paths, dev_thrust_paths, matComparator());
+ checkCUDAError("Failed to Sort Material IDs");
+#endif
- // 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.
+ //SHADE INTERSECTIONS, SCATTER RAYS
+ shadeFakeMaterial << > > (iter, num_paths, dev_intersections, dev_paths,
+ dev_materials, dev_image);
+
+ //STREAM COMPACT TO REMOVE USELESS PATHS
+ PathSegment* new_dev_paths_end = thrust::remove_if(thrust::device, dev_paths, dev_paths + num_paths, path_complete());//-- 2: cull those paths that don't need any more shading
+ num_paths = new_dev_paths_end - dev_paths;
+
+ //END THIS ITERATION IF WE'VE FINISHED NEARLY EVERY PATH, OR ELSE COMPUTE NEW INTERSECTIONS
+ if (num_paths == 0) iterationComplete = true;
+ else {
+ cudaMemset(dev_intersections, 0, num_paths * sizeof(ShadeableIntersection));
+ // tracing
+ 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++;
+ }
}
- // Assemble this iteration and apply it to the image
- dim3 numBlocksPixels = (pixelcount + blockSize1d - 1) / blockSize1d;
- finalGather<<>>(num_paths, dev_image, dev_paths);
-
- ///////////////////////////////////////////////////////////////////////////
+ //FINAL GATHER
+ finalGather << > >(num_paths, dev_image, dev_paths); //----------------------------------------- add final contributions to the frame
- // Send results to OpenGL buffer for rendering
- sendImageToPBO<<>>(pbo, cam.resolution, iter, dev_image);
+ ///////////////////////////////////////////////////////////////////////////
+ if (true || iter % 1 == 0 && iter > 1) {
+ // 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);
-
- 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..c4f3dd1 100644
--- a/src/scene.cpp
+++ b/src/scene.cpp
@@ -4,6 +4,9 @@
#include
#include
+#define TINYOBJLOADER_IMPLEMENTATION
+#include "tiny_obj_loader.h"
+
Scene::Scene(string filename) {
cout << "Reading scene from " << filename << " ..." << endl;
cout << " " << endl;
@@ -27,7 +30,9 @@ Scene::Scene(string filename) {
} else if (strcmp(tokens[0].c_str(), "CAMERA") == 0) {
loadCamera();
cout << " " << endl;
- }
+ } else if (strcmp(tokens[0].c_str(), "END") == 0) {
+ break;
+ }
}
}
}
@@ -41,6 +46,7 @@ int Scene::loadGeom(string objectid) {
cout << "Loading Geom " << id << "..." << endl;
Geom newGeom;
string line;
+ string meshName;
//load object type
utilityCore::safeGetline(fp_in, line);
@@ -51,7 +57,11 @@ int Scene::loadGeom(string objectid) {
} else if (strcmp(line.c_str(), "cube") == 0) {
cout << "Creating new cube..." << endl;
newGeom.type = CUBE;
- }
+ }
+ else {
+ newGeom.type = BB;
+ meshName = line.c_str();
+ }
}
//link material
@@ -79,16 +89,93 @@ int Scene::loadGeom(string objectid) {
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);
+ newGeom.transform = utilityCore::buildTransformationMatrix(
+ newGeom.translation, newGeom.rotation, newGeom.scale);
+ newGeom.inverseTransform = glm::inverse(newGeom.transform);
+ newGeom.invTranspose = glm::inverseTranspose(newGeom.transform);
+
+ if (newGeom.type == SPHERE || newGeom.type == CUBE) {
+ geoms.push_back(newGeom);
+ } else {
+ geoms.push_back(newGeom);
+ loadObj(meshName, newGeom.transform, newGeom.inverseTransform, newGeom.invTranspose, newGeom.materialid);
+ }
- geoms.push_back(newGeom);
return 1;
}
}
+//straight from the tinyOBJ readme
+int Scene::loadObj(string meshName, glm::mat4 transform, glm::mat4 inverseTransform, glm::mat4 inverseTranspose, int materialID) {
+
+ Geom bb = geoms[geoms.size() - 1];
+ int bbIdx = geoms.size() - 1;
+ glm::vec3 minPoint = glm::vec3(100, 100, 100);
+ glm::vec3 maxPoint = glm::vec3(-100, -100, -100);
+
+ tinyobj::attrib_t attrib;
+ std::vector shapes;
+ std::vector materials;
+
+ std::string err;
+ bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, meshName.c_str(),NULL,true);
+
+ if (!err.empty()) { // `err` may contain warning message.
+ std::cerr << err << std::endl;
+ }
+
+ if (!ret) {
+ exit(1);
+ }
+
+ // Loop over shapes
+ 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++) {
+ Geom newGeom;
+ newGeom.type = TRIANGLE;
+ newGeom.materialid = materialID;
+ newGeom.transform = transform;
+ newGeom.inverseTransform = inverseTransform;
+ newGeom.invTranspose = inverseTranspose;
+
+ // Loop over vertices in the face.
+ for (size_t v = 0; v < 3; v++) {
+ // access to vertex
+ tinyobj::index_t idx = shapes[s].mesh.indices[index_offset + v];
+ newGeom.vertices[v][0] = attrib.vertices[3 * idx.vertex_index + 0];
+ newGeom.vertices[v][1] = attrib.vertices[3 * idx.vertex_index + 1];
+ newGeom.vertices[v][2] = attrib.vertices[3 * idx.vertex_index + 2];
+ }
+
+ for (int i = 0; i < 3; i++) {
+ newGeom.vertices[i] = (glm::vec3) (transform * glm::vec4(newGeom.vertices[i],1));
+ minPoint = glm::min(newGeom.vertices[i], minPoint);
+ maxPoint = glm::max(newGeom.vertices[i], maxPoint);
+ }
+ newGeom.normal = glm::normalize(glm::cross(newGeom.vertices[1] - newGeom.vertices[0], newGeom.vertices[2] - newGeom.vertices[0]));
+
+ geoms.push_back(newGeom);
+ index_offset += 3;
+ }
+ }
+
+ bb.translation = 0.5f * (minPoint + maxPoint);
+ bb.rotation = glm::vec3(0, 0, 0);
+ bb.scale = maxPoint - minPoint;
+ bb.materialid = materialID;
+ bb.transform = utilityCore::buildTransformationMatrix(
+ bb.translation, bb.rotation, bb.scale);
+ bb.inverseTransform = glm::inverse(bb.transform);
+ bb.invTranspose = glm::inverseTranspose(bb.transform);
+ bb.numTris = geoms.size() - bbIdx;
+ geoms[bbIdx] = bb;
+
+ return 1;
+}
+
int Scene::loadCamera() {
cout << "Loading Camera ..." << endl;
RenderState &state = this->state;
diff --git a/src/scene.h b/src/scene.h
index f29a917..9292ff4 100644
--- a/src/scene.h
+++ b/src/scene.h
@@ -15,6 +15,7 @@ class Scene {
ifstream fp_in;
int loadMaterial(string materialid);
int loadGeom(string objectid);
+ int loadObj(string meshName, glm::mat4 transform, glm::mat4 inverseTransform, glm::mat4 inverseTranspose, int materialID);
int loadCamera();
public:
Scene(string filename);
diff --git a/src/sceneStructs.h b/src/sceneStructs.h
index b38b820..5288f17 100644
--- a/src/sceneStructs.h
+++ b/src/sceneStructs.h
@@ -10,6 +10,8 @@
enum GeomType {
SPHERE,
CUBE,
+ TRIANGLE,
+ BB
};
struct Ray {
@@ -19,6 +21,9 @@ struct Ray {
struct Geom {
enum GeomType type;
+ glm::vec3 vertices[3];//if triangle
+ glm::vec3 normal;
+ int numTris;
int materialid;
glm::vec3 translation;
glm::vec3 rotation;
@@ -64,6 +69,7 @@ struct PathSegment {
glm::vec3 color;
int pixelIndex;
int remainingBounces;
+ float insideT; //will be zero except if inside a refractive surface; there it will reflect distance traveled for beers law
};
// Use with a corresponding PathSegment to do:
@@ -71,6 +77,7 @@ struct PathSegment {
// 2) BSDF evaluation: generate a new ray
struct ShadeableIntersection {
float t;
+ glm::vec3 point;
glm::vec3 surfaceNormal;
int materialId;
};
diff --git a/src/tiny_obj_loader.h b/src/tiny_obj_loader.h
new file mode 100644
index 0000000..5abe459
--- /dev/null
+++ b/src/tiny_obj_loader.h
@@ -0,0 +1,2125 @@
+/*
+The MIT License (MIT)
+Copyright (c) 2012-2017 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.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.
+//
+
+
+#ifndef TINY_OBJ_LOADER_H_
+#define TINY_OBJ_LOADER_H_
+
+#include