Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
8421472
Remove unused freeglut copyrights
shehzan10 Jan 16, 2021
a79ca55
Update CUDA Computes List
shehzan10 Sep 10, 2021
78e028a
Retab
shehzan10 Sep 10, 2021
c03780d
Update instructions for Fall 2021
shehzan10 Sep 18, 2021
80ad206
Add vulkan option
shehzan10 Sep 18, 2021
cc3da39
Add sample readmes for inspiration
shehzan10 Sep 19, 2021
784f5ca
Merge pull request #1 from CIS565-Fall-2021/update-instructions-2021
shehzan10 Sep 21, 2021
6d7e696
Added Imgui Integration Files
codeplay9800 Sep 19, 2022
8462d52
Updated Instruction.md
codeplay9800 Sep 19, 2022
c44fccb
Update Instruction.md
codeplay9800 Sep 19, 2022
7165f5c
Merge branch 'main' into Integrate_Imgui
codeplay9800 Sep 19, 2022
26e8bf3
Imgui Integrated
codeplay9800 Sep 20, 2022
3dac24e
Added GUIDataContainer Class
codeplay9800 Sep 20, 2022
e666e6a
Removed ImGUI Cmake
codeplay9800 Sep 21, 2022
5000086
Update INSTRUCTION.md
shehzan10 Sep 21, 2022
98f098c
Fix a bug that causes MouseOverImGuiWindow() to not work
dw218192 Sep 22, 2022
16d6638
Merge pull request #1 from dw218192/patch-1
codeplay9800 Sep 22, 2022
fa270e5
Update INSTRUCTION.md
HummaWhite Sep 16, 2023
0f1be54
Added a section for optimization
wayne-wu Sep 18, 2023
c5d6523
Update INSTRUCTION.md
HummaWhite Sep 19, 2023
39431af
Finished diffuse and frensel specular
bdwhst Sep 22, 2023
0e5accb
wavefront obj basic support
bdwhst Sep 23, 2023
5e72881
SAH-BVH stackless traverse
bdwhst Sep 25, 2023
2aebec4
microfacet importance sampling, bsdf refactor
bdwhst Sep 26, 2023
46b31b4
texture mapping, skybox
bdwhst Sep 27, 2023
6b0660d
obj texture loading, skybox lighting
bdwhst Sep 28, 2023
71b70e9
Update README.md
bdwhst Sep 28, 2023
ce24ad4
MTBVH, bug fix
bdwhst Sep 29, 2023
db6dda8
dof
bdwhst Sep 30, 2023
793c9b7
basic gltf loading, metallic workflow pbr, per-vertex normal
bdwhst Oct 1, 2023
d5e4b12
gltf hierachy transform
bdwhst Oct 2, 2023
a23fbe5
Update README.md
bdwhst Oct 2, 2023
1d1b3c8
normal mapping fixed
bdwhst Oct 4, 2023
a83a9d8
updated img
bdwhst Oct 4, 2023
90b8607
readme
bdwhst Oct 4, 2023
8848510
readme
bdwhst Oct 4, 2023
52a4a01
readme
bdwhst Oct 4, 2023
b95cac6
fixed pbr
bdwhst Oct 6, 2023
80e351c
brdf bug fix
bdwhst Oct 6, 2023
cf5e7b9
MIS
bdwhst Oct 8, 2023
27923ae
bug fix
bdwhst Oct 8, 2023
59e0bad
added denoiser
bdwhst Oct 9, 2023
8d054ab
add files for open denoiser
bdwhst Oct 9, 2023
32ea36d
bug fix
bdwhst Oct 9, 2023
abba242
write up
bdwhst Oct 11, 2023
d27f15b
write up
bdwhst Oct 11, 2023
40c7bd1
fixed formula, added stat
bdwhst Oct 11, 2023
7ed5430
minor editing
bdwhst Oct 11, 2023
2d9b03f
atrous and svgf
bdwhst Oct 23, 2023
3849e46
added gifs
bdwhst Oct 23, 2023
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
28 changes: 28 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,44 @@ set(headers
src/intersections.h
src/glslUtility.hpp
src/pathtrace.h
src/denoise.h
src/scene.h
src/sceneStructs.h
src/preview.h
src/utilities.h
src/bvh.h
src/ImGui/imconfig.h

src/ImGui/imgui.h
src/ImGui/imconfig.h
src/ImGui/imgui_impl_glfw.h
src/ImGui/imgui_impl_opengl3.h
src/ImGui/imgui_impl_opengl3_loader.h
src/ImGui/imgui_internal.h
src/ImGui/imstb_rectpack.h
src/ImGui/imstb_textedit.h
src/ImGui/imstb_truetype.h
)

set(sources
src/main.cpp
src/stb.cpp
src/image.cpp
src/glslUtility.cpp
src/denoise.cu
src/pathtrace.cu
src/scene.cpp
src/preview.cpp
src/utilities.cpp
src/bvh.cpp
src/mikktspace/mikktspace.c
src/ImGui/imgui.cpp
src/ImGui/imgui_demo.cpp
src/ImGui/imgui_draw.cpp
src/ImGui/imgui_impl_glfw.cpp
src/ImGui/imgui_impl_opengl3.cpp
src/ImGui/imgui_tables.cpp
src/ImGui/imgui_widgets.cpp
)

list(SORT headers)
Expand All @@ -92,10 +115,15 @@ list(SORT sources)
source_group(Headers FILES ${headers})
source_group(Sources FILES ${sources})

#add_subdirectory(src/ImGui)
#add_subdirectory(stream_compaction) # TODO: uncomment if using your stream compaction

link_directories(${PROJECT_SOURCE_DIR}/external/lib)
cuda_add_executable(${CMAKE_PROJECT_NAME} ${sources} ${headers})

target_link_libraries(${CMAKE_PROJECT_NAME}
${LIBRARIES}
#stream_compaction # TODO: uncomment if using your stream compaction
OpenImageDenoise
)

196 changes: 101 additions & 95 deletions INSTRUCTION.md

Large diffs are not rendered by default.

143 changes: 135 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,140 @@
CUDA Path Tracer
================
CUDA Denoiser For CUDA Path Tracer
==================================

**University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 3**
* **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 4**
* Han Yang
* [LinkedIn](https://www.linkedin.com/in/han-yang-0031231a3/), [personal website](https://bdwhst.wixsite.com/portfolio), etc.
* Tested on: Windows 11, i9-12900HX @ 2.30GHz 16GB, RTX4080 laptop 12GB

* (TODO) YOUR NAME HERE
* Tested on: (TODO) Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab)
## Final Result

### (TODO: Your README)
![](./img/sponza-svgf.gif)

*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.
## Principles and Analysis

Path tracing, a type of ray tracing, involves shooting rays from the camera into the scene to simulate the paths of rays of light. Because it's probabilistic and based on random sampling, this can lead to noisy images, especially with a limited number of samples per pixel. Denoising these images while preserving details is a significant challenge.

### Edge-Avoiding A-Trous Wavelet Filter

![](./img/atrous-fig.png)

- **Wavelet Transformation**: The term "à trous" is French for "with holes". In this method, the wavelet transformation is used to decompose an image into various frequency bands. By expanding the filter's support using a hole (or gap) between the samples, we can achieve different scales of wavelets.
- **Edge-Avoidance**: As with most image processing techniques, the primary challenge is to apply the desired effect without compromising the image's details. The Edge-Avoiding component of this filter ensures that while smoothing is applied generously in flat regions, it avoids areas with sharp intensity differences or edges, preserving details.
- **G-buffer Information**: When used in computer graphics, especially in a deferred shading context, the filter can benefit from the G-buffer's information. Elements like depth, normals, and albedo can guide the filtering process. For instance, a significant depth difference can indicate an object boundary, signaling the filter to avoid crossing that edge.
- **Stopping Function**: The stopping function, guided by the G-buffer data, determines how the filtering process should be modified in specific regions of the image. It helps in making informed decisions to preserve high-frequency details.

| 1spp Original (with MIS) | 1spp A-Trous Filtered (with MIS) |
| :------------------------: | :------------------------------: |
| ![](./img/origin-1spp.png) | ![](./img/eaw-1spp.png) |

#### Latency

Here we choose three scenes with the same resolution(1080*1080), they have similar delay with the A-Trous filter.

| | cornell-sphere.txt | cornell-sphere-vase.txt | sponza.txt |
| ----------------- | ------------------ | ----------------------- | ---------- |
| Latency Added(ms) | ~4 | ~3 | ~4 |

For the same resolution, the delay of filtering remain the same, irrelevant of the scene. This is intuitive because filtering is basically a screen space post processing.

#### Appearance with different iteration

| 1 iteration | 2 iteration |
| ------------------------- | ------------------------- |
| ![](./img/eaw-1spp-1.png) | ![](./img/eaw-1spp-2.png) |

| 3 iteration | 4 iteration |
| ------------------------- | ------------------------- |
| ![](./img/eaw-1spp-3.png) | ![](./img/eaw-1spp-4.png) |

| 5 iteration | 6 iteration |
| ------------------------- | ------------------------- |
| ![](./img/eaw-1spp-5.png) | ![](./img/eaw-1spp-6.png) |

Every time we increase filter iteration by one, filter size will multiply by two. In "one-spp Cornell box" case we need at least 6 iterations to get a smooth result.

#### Runtime with different resolution

Here we choose the Cornell sphere scene file as a benchmark, with filter iteration=6.

| | 600*600 | 700*700 | 800*800 | 900*900 | 1000*1000 |
| ----------- | ------- | ------- | ------- | ------- | --------- |
| Latency(ms) | ~8.7 | ~11.6 | ~14.1 | ~18.9 | ~21.8 |

As the image size increases, the latency also increases. This suggests that processing larger images takes more time, which is expected as there are more pixels to handle in bigger images.

#### Runtime with different filter size

Filter size of A-Trous filtering is actually controlled by number of filter iterations, and it will exponentially growth with the increase of filter iterations. Here we choose the Cornell sphere scene file as a benchmark, with filter iteration=6, resolution=1000x1000.

| | 5*5 (1 iteration) | 10*10 (2 iteration) | 20*20 (3 iteration) | 40*40 (4 iteration) | 80*80 (5 iteration) |
| ----------- | ----------------- | ------------------- | ------------------- | ------------------- | ------------------- |
| Latency(ms) | 18.5 | 19.1 | 19.3 | 20.7 | 22.2 |

We can see that increase the filter size will slightly increase the latency, although it is not significant.

### GBuffer Optimization

Here I used linear depth reconstruction and octahedral normal encoding to reduce GPU memory bandwidth cost.

#### Depth Reconstruction

Use the depth in view space to reconstruct world position, relatively easy.

#### Octahedral Normal Encoding

Normals are vectors that are perpendicular to a surface and are used extensively in computer graphics to determine how light interacts with surfaces. Storing and transmitting these normals efficiently, especially in contexts like deferred rendering or texture storage, is crucial for performance. Octahedral normal encoding is a technique that encodes 3D unit vectors (normals) into 2D vectors without significant loss of precision, making them more compact and suitable for storage.

Advantages of Octahedral Normal Encoding:

1. **Memory Efficiency**: Reduces the storage requirements for normals, which can lead to significant memory savings.
2. **Performance**: Encoded normals can be efficiently decoded in shaders, leading to faster rendering times.
3. **Precision**: Provides a good balance between memory usage and precision, ensuring that the normals' accuracy is maintained.

Implementation:

1. First, the 3D normal is mapped onto an octahedron.
2. This octahedron is then unfolded into a 2D space, producing a 2D vector.
3. The 2D vector can be stored in a regular texture, reducing memory bandwidth requirements.
4. During rendering, the 2D encoded normal is read from the texture and decoded back into a 3D normal for lighting calculations.

*Latency before and after octahedral normal encoding is almost the same, since it requires little computation to encode and decode the normals, but with octahedral encoding, we can get a 4 byte (one float) of bandwidth saving with each stored normal.*

### Spatial-temporal Variance Guided Filter

![](./img/svgf-fig.png)

1. **Input Images**:
- The data for the current frame (produced at a rate of 1 path per pixel) consists of:
- **Motion**: Information about the movement in the scene.
- **Color**: The color values captured for each pixel.
- **Normal**: Vector data depicting the orientation of surfaces.
- **Depth & Mesh ID**: Represents the distance of objects/surfaces from the camera and identifiers for the objects.
- The previous frame data, stored for reference, includes:
- **Color History**: Past color data for temporal coherence.
- **Moments History**: Statistical moments of previous frames.
- **Depth & Mesh ID**: Previous data about object depth and identification.
2. **Temporal Accumulation**:
- This phase integrates or accumulates data over time (both from the current frame and historical data) to improve the signal-to-noise ratio.
- The result is the **Integrated Color**, which is the temporally smoothed color, and **Integrated Moments**, which are statistical summaries of the image data.
3. **Variance Estimation**:
- Using the Integrated Moments, the pipeline estimates the variance (a measure of noise or uncertainty) in the image data. This is depicted as **Variance** in the red box.
4. **Wavelet Filtering**:
- The process applies a wavelet filter on the Integrated Color data, guided by the variance, to denoise and enhance the image.
- The first iteration produces the **Filtered Color**.
- This process can be iteratively repeated for 'l' times (where 'l' is a positive integer) to continually refine the output. After each iteration, the variance is updated and used for the next filtering iteration.
5. **Update Histories**:
- After filtering, the histories (Color and Moments) are updated with the new filtered data for use in subsequent frames.
6. **Output Image**:
- The final enhanced and denoised image is produced after the filtering iterations are completed.

#### Comparison

| A-Trous | SVGF |
| :---------------------------: | :-------------------------: |
| ![](./img/atrous-cornell.gif) | ![](./img/svgf-cornell.gif) |

| A-Trous | SVGF |
| :------------------------: | :----------------------: |
| ![](./img/atrous-vase.gif) | ![](./img/svgf-vase.gif) |

8 changes: 5 additions & 3 deletions cmake/CUDAComputesList.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ IF( CUDA_COMPUTE_20
OR CUDA_COMPUTE_70
OR CUDA_COMPUTE_72
OR CUDA_COMPUTE_75
OR CUDA_COMPUTE_80
OR CUDA_COMPUTE_86
)
SET(FALLBACK OFF)
ELSE()
Expand All @@ -70,8 +72,8 @@ LIST(LENGTH COMPUTES_DETECTED_LIST COMPUTES_LEN)
IF(${COMPUTES_LEN} EQUAL 0 AND ${FALLBACK})
MESSAGE(STATUS "You can use -DCOMPUTES_DETECTED_LIST=\"AB;XY\" (semicolon separated list of CUDA Compute versions to enable the specified computes")
MESSAGE(STATUS "Individual compute versions flags are also available under CMake Advance options")
LIST(APPEND COMPUTES_DETECTED_LIST "30" "50" "60" "70")
MESSAGE(STATUS "No computes detected. Fall back to 30, 50, 60 70")
LIST(APPEND COMPUTES_DETECTED_LIST "30" "50" "60" "70" "80")
MESSAGE(STATUS "No computes detected. Fall back to 30, 50, 60, 70, 80")
ENDIF()

LIST(LENGTH COMPUTES_DETECTED_LIST COMPUTES_LEN)
Expand All @@ -90,7 +92,7 @@ MACRO(SET_COMPUTE VERSION)
ENDMACRO(SET_COMPUTE)

# Iterate over compute versions. Create variables and enable computes if needed
FOREACH(VER 20 30 32 35 37 50 52 53 60 61 62 70 72 75)
FOREACH(VER 20 30 32 35 37 50 52 53 60 61 62 70 72 75 80 86)
OPTION(CUDA_COMPUTE_${VER} "CUDA Compute Capability ${VER}" OFF)
MARK_AS_ADVANCED(CUDA_COMPUTE_${VER})
IF(${CUDA_COMPUTE_${VER}})
Expand Down
96 changes: 48 additions & 48 deletions cmake/FindGLFW.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,66 @@
include(FindPackageHandleStandardArgs)

if (WIN32)
# Find include files
find_path(
GLFW_INCLUDE_DIR
NAMES GLFW/glfw3.h
PATHS
$ENV{PROGRAMFILES}/include
${GLFW_ROOT_DIR}/include
DOC "The directory where GLFW/glfw.h resides")
# Find include files
find_path(
GLFW_INCLUDE_DIR
NAMES GLFW/glfw3.h
PATHS
$ENV{PROGRAMFILES}/include
${GLFW_ROOT_DIR}/include
DOC "The directory where GLFW/glfw.h resides")

# Use glfw3.lib for static library
if (GLFW_USE_STATIC_LIBS)
set(GLFW_LIBRARY_NAME glfw3)
else()
set(GLFW_LIBRARY_NAME glfw3dll)
endif()
# Use glfw3.lib for static library
if (GLFW_USE_STATIC_LIBS)
set(GLFW_LIBRARY_NAME glfw3)
else()
set(GLFW_LIBRARY_NAME glfw3dll)
endif()

# Find library files
find_library(
GLFW_LIBRARY
NAMES ${GLFW_LIBRARY_NAME}
PATHS
$ENV{PROGRAMFILES}/lib
${GLFW_ROOT_DIR}/lib)
# Find library files
find_library(
GLFW_LIBRARY
NAMES ${GLFW_LIBRARY_NAME}
PATHS
$ENV{PROGRAMFILES}/lib
${GLFW_ROOT_DIR}/lib)

unset(GLFW_LIBRARY_NAME)
unset(GLFW_LIBRARY_NAME)
else()
# Find include files
find_path(
GLFW_INCLUDE_DIR
NAMES GLFW/glfw.h
PATHS
/usr/include
/usr/local/include
/sw/include
/opt/local/include
DOC "The directory where GL/glfw.h resides")
# Find include files
find_path(
GLFW_INCLUDE_DIR
NAMES GLFW/glfw.h
PATHS
/usr/include
/usr/local/include
/sw/include
/opt/local/include
DOC "The directory where GL/glfw.h resides")

# Find library files
# Try to use static libraries
find_library(
GLFW_LIBRARY
NAMES glfw3
PATHS
/usr/lib64
/usr/lib
/usr/local/lib64
/usr/local/lib
/sw/lib
/opt/local/lib
${GLFW_ROOT_DIR}/lib
DOC "The GLFW library")
# Find library files
# Try to use static libraries
find_library(
GLFW_LIBRARY
NAMES glfw3
PATHS
/usr/lib64
/usr/lib
/usr/local/lib64
/usr/local/lib
/sw/lib
/opt/local/lib
${GLFW_ROOT_DIR}/lib
DOC "The GLFW library")
endif()

# Handle REQUIRD argument, define *_FOUND variable
find_package_handle_standard_args(GLFW DEFAULT_MSG GLFW_INCLUDE_DIR GLFW_LIBRARY)

# Define GLFW_LIBRARIES and GLFW_INCLUDE_DIRS
if (GLFW_FOUND)
set(GLFW_LIBRARIES ${OPENGL_LIBRARIES} ${GLFW_LIBRARY})
set(GLFW_INCLUDE_DIRS ${GLFW_INCLUDE_DIR})
set(GLFW_LIBRARIES ${OPENGL_LIBRARIES} ${GLFW_LIBRARY})
set(GLFW_INCLUDE_DIRS ${GLFW_INCLUDE_DIR})
endif()

# Hide some variables
Expand Down
Loading