Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ local.properties
.externalToolBuilders/

# Locally stored "Eclipse launch configurations"
#*.launch
*.launch

# CDT-specific
#.cproject
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.0)
project(cis565_path_tracer)

set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

# Set up include and lib paths
set(EXTERNAL "external")
Expand Down Expand Up @@ -84,6 +85,7 @@ target_link_libraries(${CMAKE_PROJECT_NAME}
src
#stream_compaction # TODO: uncomment if using your stream compaction
${CORELIBS}
X11
)

add_custom_command(
Expand Down
22 changes: 0 additions & 22 deletions Project3-CUDA-Path-Tracer.launch

This file was deleted.

97 changes: 89 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,94 @@
CUDA Path Tracer
================
CUDA Stream Compaction
======================

**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3**
**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 2**

* (TODO) YOUR NAME HERE
* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
* Edward Atter
* [LinkedIn](https://www.linkedin.com/in/atter/)
* Tested on: Linux Mint 18.3 Sylvia (4.13.0-41-generic), Ryzen 7 2700x @ 3.7 ghz (base clock) 16GB, GTX 1070 TI 8GB GDDR5 (Personal)
* CUDA 9

### (TODO: Your README)
![](img/intro.png)

*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.
## Overview

This project implements a path tracer with the following features:
- A shading kernel capable of diffuse, specular, and refractive surfaces with Fresnel effects
- Antialiasing
- Stream compaction
- Material sorting
- First bounce caching
- Built in frame timer

All features may be toggled by changing the defined constants in `src/pathtracer.cu`.

## Performance


#### Methodology

Frame timings were calculated by taking the average over 5 frames. In general, after the initial frame, very little variance was observed. The timing for each feature was performed with all other toggleable features disabled.

#### Relative Performance Impact

![](img/relative-performance.png)

The graph above shows the relative performance impact of each feature, represented in seconds. A negative time indicates the feature had a positive impact on performance, while a positive number represents a decrease in performance. "None" is with all features disabled and is the baseline in this test, set to 0.

Analysis of `nvidia-smi` suggests the application is GPU bound, as expected, pegging the GPU usage at 100%.

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 396.26 Driver Version: 396.26 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
|===============================+======================+======================|
| 0 GeForce GTX 107... Off | 00000000:0A:00.0 On | N/A |
| 47% 65C P2 116W / 180W | 1055MiB / 8116MiB | 100% Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes: GPU Memory |
| GPU PID Type Process name Usage |
|=============================================================================|
| 0 1570 G /usr/lib/xorg/Xorg 400MiB |
| 0 3563 G cinnamon 140MiB |
| 0 4200 G ...-token=A7907B8E58127E7F09984994A2B09AB9 48MiB |
| 0 6463 C gimp-2.8 107MiB |
| 0 10622 G ...-token=A3470AA5428C5FF1C71554B5AC8CA77C 41MiB |
| 0 12950 C+G ...DA-Path-Tracer/build/cis565_path_tracer 209MiB |
| 0 23722 C /usr/lib/libreoffice/program/soffice.bin 105MiB |
+-----------------------------------------------------------------------------+


## Features

#### Anti-Aliasing

![](img/circle-no-aa.png) ![](img/circle-with-aa.png)

Anti-aliasing can even out rough edges. This is done by "jittering" the rays' x and y positions slightly at each iteration. The effect is most noticible in high-contrast situations, as in the images above. The image on the left has anti-aliasing disabled, while the image on the right has AA enabled. The image on the right appears less pixelated.

Anti-aliasing had a negligible impact on performance, .003 seconds. This is well within the margin of error.

#### Sorting
In theory, sorting by material type should yield a large improvement. The GPUs scheduler can skip warps if each thread in the warp is returned. Sorting by material type should increase the liklihood of this happening. In addition, it should clean up memory accesses and branch prediction since most warps will consist of a single material type.

Unfortunately, as shown in the graph above, this theoretical gain was not realized. The overhead of sorting is much greater than the gains it provides.

#### Stream Compaction
Interestingly, stream compaction resulted in a slight decrease in performance. In theory, we should be able to eliminate many of the dead, black rays; but at least with the current implementation, the compaction overhead was to great to yield any appreciable results.

I hypothesized that mostly black images would benefit the most from stream compaction, so I tried again on the `sphere.txt` scene. The results were the same, rendering was faster without stream compaction.

#### First Bounce Caching
Caching the first bounce yielded the largest increase. Unfortunately, it has the effect of dampening the anti-aliasing affect (if both are enabled simultaneously), since the random jitter of the array is cached and reused for each iteration, only being updated during a new frame.

## Gallery

![](img/l1.png)
![](img/m1.png) ![](img/m2.png)

![](img/g1.png) ![](img/g2.png)
![](img/g3.png) ![](img/g4.png)
![](img/g5.png) ![](img/g6.png)
Binary file modified external/lib/linux/libglfw3.a
Binary file not shown.
Binary file added img/circle-no-aa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/circle-with-aa.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/intro.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/l1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/m1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/m2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/relative-performance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ set(SOURCE_FILES

cuda_add_library(src
${SOURCE_FILES}
OPTIONS -arch=sm_20
OPTIONS -arch=sm_60
)
103 changes: 101 additions & 2 deletions src/interactions.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "intersections.h"

#define THRESH_INTERNAL_REFLECTION .01f

// CHECKITOUT
/**
* Computes a cosine-weighted random direction in a hemisphere.
Expand Down Expand Up @@ -41,6 +43,41 @@ glm::vec3 calculateRandomDirectionInHemisphere(
+ sin(around) * over * perpendicularDirection2;
}

__host__ __device__
void reflect(PathSegment & pathSegment,
glm::vec3 intersect,
glm::vec3 normal,
const Material &material,
glm::vec3 direction) {
pathSegment.color *= material.color;
pathSegment.ray.direction = glm::reflect(direction, normal);
pathSegment.ray.origin = intersect + .0001f * pathSegment.ray.direction;
}

__host__ __device__
void refract(PathSegment & pathSegment,
glm::vec3 intersect,
glm::vec3 normal,
const Material &material,
glm::vec3 direction) {
float refractive_index = material.indexOfRefraction;
// Not sure why this fixes the bug...
if (glm::dot(direction, normal) < 0) {
refractive_index = 1.0f / refractive_index;
} else {
normal = -normal;
}

if (glm::length(pathSegment.ray.direction) > THRESH_INTERNAL_REFLECTION) {
pathSegment.ray.direction = glm::refract(direction, normal, refractive_index);
} else {
pathSegment.ray.direction = glm::reflect(direction, normal);
}

pathSegment.color *= material.color;
pathSegment.ray.origin = intersect + .001f * pathSegment.ray.direction;
}

/**
* Scatter a ray with some probabilities according to the material properties.
* For example, a diffuse surface scatters in a cosine-weighted hemisphere.
Expand Down Expand Up @@ -71,9 +108,71 @@ void scatterRay(
PathSegment & pathSegment,
glm::vec3 intersect,
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.
glm::vec3 direction = glm::normalize(pathSegment.ray.direction);

if (material.hasReflective && material.hasRefractive) {
thrust::uniform_real_distribution<float> u01(0,1);

float cosAngle = glm::dot(direction, normal);
if (cosAngle > 1) {
cosAngle = 1;
} else if (cosAngle < -1) {
cosAngle = -1;
}

float refractive_index_before = 1;
float refractive_index_after;

if (cosAngle > 0) {
std::swap(refractive_index_before, refractive_index_after);
} else {
cosAngle *= -1;
pathSegment.ray.origin = intersect + .001f * direction;
}

float refractive_index = refractive_index_before / refractive_index_after;
float sinAngleBefore = 1 - std::pow(cosAngle, 2);
if (sinAngleBefore > 0) {
sinAngleBefore = std::sqrt(sinAngleBefore);
} else {
sinAngleBefore = 0;
}
float sinAngleAfter = refractive_index * sinAngleBefore;

if (sinAngleAfter > 1) {
pathSegment.color *= material.specular.color;
pathSegment.ray.direction = glm::reflect(pathSegment.ray.direction, normal);
} else {
//Shlick
//R = R_0 + (1 - R_0) * (1 - cos(theta))^5
//R_0 = [(n_1 - n_2) / (n_1 + n_2)]^2

float r0 = (refractive_index_before - refractive_index_after) / (refractive_index_before + refractive_index_after);
r0 *= r0;
float cosAngleRaised = std::pow(1 - cosAngle, 5);
float R = r0 + (1 - r0) * cosAngleRaised;

//Randomize
if (R < u01(rng)) {
refract(pathSegment, intersect, normal, material, direction);
} else {
reflect(pathSegment, intersect, normal, material, direction);
}
}

} else if (material.hasReflective) {
reflect(pathSegment, intersect, normal, material, direction);
} else if (material.hasRefractive) {
refract(pathSegment, intersect, normal, material, direction);
} else {
//Diffuse
pathSegment.color *= material.color;
pathSegment.ray.direction = calculateRandomDirectionInHemisphere(normal, rng);
pathSegment.ray.origin = intersect + .001f * pathSegment.ray.direction;
}

}
Loading