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
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ build
.LSOverride

# Icon must end with two \r
Icon
Icon


# Thumbnails
._*
Expand Down Expand Up @@ -558,3 +559,6 @@ xcuserdata
*.xccheckout
*.moved-aside
*.xcuserstate

*.txt
scenes*
16 changes: 14 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)


# Enable C++11 for host code
set(CMAKE_CXX_STANDARD 11)
if(NOT DEFINED CMAKE_CUDA_STANDARD)
Expand Down Expand Up @@ -61,6 +62,14 @@ set(headers
src/sceneStructs.h
src/preview.h
src/utilities.h
src/Bounds3.hpp
src/BVH.h
src/material.h
src/tiny_obj_loader.hpp
src/lightSample.h
src/distribution1D.h
src/distribution2D.h
src/ImGui/imconfig.h
)

set(sources
Expand All @@ -69,11 +78,14 @@ set(sources
src/image.cpp
src/glslUtility.cpp
src/pathtrace.cu
src/intersections.cu
src/interactions.cu
src/scene.cpp
src/preview.cpp
src/utilities.cpp
src/BVH.cpp
src/material.cpp
src/tiny_obj_loader.cpp
src/distribution1D.cpp
src/distribution2D.cpp
)

set(imgui_headers
Expand Down
86 changes: 81 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,87 @@ 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)
* Zhiyi Zhou
* Tested on: Windows 11, i9-13900H @ 2.6GHz 64GB, RTX4060 Laptop

### (TODO: Your README)
![](img2024/glassbunny2.png)
![](img2024/camera_mis.png)
![](img2024/gundam.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.
## Features
- OBJ & glTF Loading
- BVH(MTBVH) Acceleration
- Stochastic Sampled Anti-Aliasing
- Physically-Based Materials
- Texture Mapping & Normal Mapping
- Environment Mapping
- Multiple Importance Sampling of different kind of light(sphere, cube, mesh and environment light)

### OBJ & glTF Loading
The glTF (GL Transmission Format) file is a streamlined, efficient format for transmitting 3D models and scenes, designed specifically for real-time rendering applications such as 3D games, web platforms, and virtual reality. Developed by the Khronos Group, the same organization behind OpenGL and Vulkan, it is lightweight and optimized for performance. A glTF file can encompass an entire scene, including meshes, materials, animations, cameras, and lights. However, I focus on utilizing only its meshes and materials in my projects.

![Avocado](./img2024/Avocado.png)
<p align="center">Avocado</p>

glTF file: [Avocado](https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/Avocado/glTF)

### BVH(MTBVH) Acceleration
[BVH](https://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies)
[MTBVH](https://cs.uwaterloo.ca/~thachisu/tdf2015.pdf)

Computing intersections is the most time consuming part in our path tracer.
A BVH organizes a set of geometric objects, like triangles or meshes, into a hierarchy of nested bounding volumes, which allows for efficient spatial queries and reduces the computational cost of determining which objects are relevant in a given operation.

<table>
<tr>
<th>with BVH</th>
<th>without BVH</th>
</tr>
<tr>
<th><img src="./img2024/BVH.png"/></th>
<th><img src="./img2024/noBVH.png"/></th>
</tr>
</table>

My computer struggles to traverse millions of triangles, but thanks to BVH, the speedup is significant.

### Stochastic Sampled Anti-Aliasing

<table>
<tr>
<th>with Anti-Aliasing</th>
<th>without Anti-Aliasing</th>
</tr>
<tr>
<th><img src="./img2024/AA1.png"/></th>
<th><img src="./img2024/noAA1.png"/></th>
</tr>
</table>

### Physically-Based Materials
I implemented Microfacet material and metallic workflow in my path tracer.
Here, the ball with high metallic and high roughness looks weird, this is because in microfacet model high roughness will cause energy loss.
![](./img2024/metallic.png)

### Texture Mapping & Normal Mapping
To improve the visual quality of the scene, texture mapping is crucial. Normal mapping can further enhance surface detail, adding greater realism.

### Multiple Importance Sampling
| Direct Light sample(20spp) | BSDF Sample (20spp) | MIS(20spp) |
| :-----------------------: | :----------------------: | --------------------------- |
| ![](./img2024/Direct20.png) | ![](./img2024/BSDF20.png)| ![](./img2024/MIS20.png) |

| Direct Light sample(2000spp) | BSDF Sample (2000spp) | MIS(2000spp) |
| :-----------------------: | :----------------------: | --------------------------- |
| ![](./img2024/Direct2000.png) | ![](./img2024/BSDF2000.png)| ![](./img2024/MIS2000.png) |


## Third-party code
1. [tinygltf](https://github.com/syoyo/tinygltf)
2. [Tinyobjloader](https://github.com/tinyobjloader/tinyobjloader)

## Reference

1. [glTF™ 2.0 Specification](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#appendix-b-brdf-implementation).
2. [Poly Haven](https://polyhaven.com/)
3. [Gundam Mk-II](https://sketchfab.com/3d-models/gundam-rx-178-mk-ii-8d4bf8343fd346e4a18bde44fb129fd1)
102 changes: 102 additions & 0 deletions cmake/CUDAComputesList.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
## CUDA Compute detection code
## Sourced from ArrayFire https://github.com/arrayfire/arrayfire/
## Under BSD-3 Clause License https://github.com/arrayfire/arrayfire/blob/devel/LICENSE

#Disables running cuda_compute_check.c when build windows using remote
OPTION(CUDA_COMPUTE_DETECT "Run autodetection of CUDA Architecture" ON)
MARK_AS_ADVANCED(CUDA_COMPUTE_DETECT)

IF(CUDA_COMPUTE_DETECT AND NOT DEFINED COMPUTES_DETECTED_LIST)
#############################
#Sourced from:
#https://raw.githubusercontent.com/jwetzl/CudaLBFGS/master/CheckComputeCapability.cmake
#############################
# Check for GPUs present and their compute capability
# based on http://stackoverflow.com/questions/2285185/easiest-way-to-test-for-existence-of-cuda-capable-gpu-from-cmake/2297877#2297877 (Christopher Bruns)

IF(CUDA_FOUND)
MESSAGE(STATUS "${CMAKE_MODULE_PATH}/cuda_compute_capability.cpp")

TRY_RUN(RUN_RESULT_VAR COMPILE_RESULT_VAR
${PROJECT_BINARY_DIR}
${CMAKE_MODULE_PATH}/cuda_compute_capability.cpp
CMAKE_FLAGS
-DINCLUDE_DIRECTORIES:STRING=${CUDA_TOOLKIT_INCLUDE}
-DLINK_LIBRARIES:STRING=${CUDA_CUDART_LIBRARY}
COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT_VAR
RUN_OUTPUT_VARIABLE RUN_OUTPUT_VAR)

MESSAGE(STATUS "COMPILE_OUTPUT_VAR: ${COMPILE_OUTPUT_VAR}")
MESSAGE(STATUS "CUDA Compute Detection Output: ${RUN_OUTPUT_VAR}")
MESSAGE(STATUS "CUDA Compute Detection Return: ${RUN_RESULT_VAR}")

# COMPILE_RESULT_VAR is TRUE when compile succeeds
# Check Return Value of main() from RUN_RESULT_VAR
# RUN_RESULT_VAR is 0 when a GPU is found
# RUN_RESULT_VAR is 1 when errors occur

IF(COMPILE_RESULT_VAR AND RUN_RESULT_VAR EQUAL 0)
MESSAGE(STATUS "CUDA Compute Detection Worked")
# Convert output into a list of computes
STRING(REPLACE " " ";" COMPUTES_DETECTED_LIST ${RUN_OUTPUT_VAR})
SET(CUDA_HAVE_GPU TRUE CACHE BOOL "Whether CUDA-capable GPU is present")
ELSE()
MESSAGE(STATUS "CUDA Compute Detection Failed")
SET(CUDA_HAVE_GPU FALSE CACHE BOOL "Whether CUDA-capable GPU is present")
ENDIF()
ENDIF(CUDA_FOUND)
ENDIF()

IF( CUDA_COMPUTE_20
OR CUDA_COMPUTE_30
OR CUDA_COMPUTE_32
OR CUDA_COMPUTE_35
OR CUDA_COMPUTE_37
OR CUDA_COMPUTE_50
OR CUDA_COMPUTE_52
OR CUDA_COMPUTE_53
OR CUDA_COMPUTE_60
OR CUDA_COMPUTE_61
OR CUDA_COMPUTE_62
OR CUDA_COMPUTE_70
OR CUDA_COMPUTE_72
OR CUDA_COMPUTE_75
OR CUDA_COMPUTE_80
OR CUDA_COMPUTE_86
)
SET(FALLBACK OFF)
ELSE()
SET(FALLBACK ON)
ENDIF()

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" "80")
MESSAGE(STATUS "No computes detected. Fall back to 30, 50, 60, 70, 80")
ENDIF()

LIST(LENGTH COMPUTES_DETECTED_LIST COMPUTES_LEN)
MESSAGE(STATUS "Number of Computes Detected = ${COMPUTES_LEN}")

FOREACH(COMPUTE_DETECTED ${COMPUTES_DETECTED_LIST})
SET(CUDA_COMPUTE_${COMPUTE_DETECTED} ON CACHE BOOL "" FORCE)
ENDFOREACH()

MACRO(SET_COMPUTE VERSION)
SET(CUDA_GENERATE_CODE_${VERSION} "-gencode arch=compute_${VERSION},code=sm_${VERSION}")
SET(CUDA_GENERATE_CODE ${CUDA_GENERATE_CODE} ${CUDA_GENERATE_CODE_${VERSION}})
LIST(APPEND COMPUTE_VERSIONS "${VERSION}")
ADD_DEFINITIONS(-DCUDA_COMPUTE_${VERSION})
MESSAGE(STATUS "Setting Compute ${VERSION} to ON")
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 80 86)
OPTION(CUDA_COMPUTE_${VER} "CUDA Compute Capability ${VER}" OFF)
MARK_AS_ADVANCED(CUDA_COMPUTE_${VER})
IF(${CUDA_COMPUTE_${VER}})
SET_COMPUTE(${VER})
ENDIF()
ENDFOREACH()
60 changes: 60 additions & 0 deletions cmake/cuda_compute_capability.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2011 Florian Rathgeber, [email protected]
*
* This code is licensed under the MIT License. See the FindCUDA.cmake script
* for the text of the license.
*
* Based on code by Christopher Bruns published on Stack Overflow (CC-BY):
* http://stackoverflow.com/questions/2285185
*/

#include <stdio.h>
#include <cuda_runtime.h>
#include <iterator>
#include <set>

int main() {
int deviceCount;
int gpuDeviceCount = 0;
struct cudaDeviceProp properties;

if (cudaGetDeviceCount(&deviceCount) != cudaSuccess)
{
printf("Couldn't get device count: %s\n", cudaGetErrorString(cudaGetLastError()));
return 1;
}

std::set<int> computes;
typedef std::set<int>::iterator iter;

// machines with no GPUs can still report one emulation device
for (int device = 0; device < deviceCount; ++device) {
int major = 9999, minor = 9999;
cudaGetDeviceProperties(&properties, device);
if (properties.major != 9999) { // 9999 means emulation only
++gpuDeviceCount;
major = properties.major;
minor = properties.minor;
if ((major == 2 && minor == 1)) {
// There is no --arch compute_21 flag for nvcc, so force minor to 0
minor = 0;
}
computes.insert(10 * major + minor);
}
}
int i = 0;
for(iter it = computes.begin(); it != computes.end(); it++, i++) {
if(i > 0) {
printf(" ");
}
// IF CMAKE CAN NOT CAPTURE THE OUTPUT OF printf, USE fprintf INSTEAD
fprintf(stderr, "%d", *it);
// printf("%d", *it);
}
/* don't just return the number of gpus, because other runtime cuda
errors can also yield non-zero return values */
if (gpuDeviceCount <= 0 || computes.size() <= 0) {
return 1; // failure
}
return 0; // success
}
Loading