Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
JinxiangW committed Oct 10, 2024
1 parent 5b8d361 commit 2c2fa0a
Show file tree
Hide file tree
Showing 14 changed files with 167 additions and 70 deletions.
41 changes: 33 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@

![](results/Cover/CoverDenoisedMIS.png)

<p align="center">A large mineway castle shade with one Disney BRDF material (6,490,766 triangles)</p>
<p align="center">A large mineway castle shaded with one Disney BRDF material (6,490,766 triangles, 300 spp, 6.1 fps)</p>

![](results/RefractDragon.png)

<p align="center">A stanford dragon shade with refract material</p>
<p align="center">A stanford dragon shaded with refract material</p>

## Features Implemented:

Expand All @@ -27,11 +27,36 @@

# Bounding Volume Hierarchy

The base code shoot rays out from the camera and intersect each object in the scene.
The base code shoots rays out from the camera and compute intersections with all objects in the scene.

After mesh loading feature is implemented, the way of light-scene intersection is changed from calculating
intersection with implicit geometries to calculating it with all triangles! Which gives an extremely low frame rate
when model with many faces is loaded:
This works fine when we can implicitly define intersecting method for each geometry (just like SDF). But after mesh loading feature is implemented, the way of light-scene intersection is changed from calculating
intersection with implicit geometries to calculating it with all triangles!

This gives an extremely low performance when model with many faces is loaded:
| < 200 faces (~60 fps) | ~6000 faces (< 10 fps) |
| -------------------------------------|-------------------------- |
| ![](results/cornerEmittanceFixed.png)| ![](results/BeforeBVH.png)|
| -------------------------------- | ------------------------------- |
| ![](results/BVH/simplescene.png) | ![](results/BVH/marioScene.png) |

To effectively reduce the amout of intersection computation, we could use BVH, Bounding Volume Hierarchy, which construct
a tree-like structure to store scene primitives.

The efficiency of BVH depends on how we build the tree. There are many ways to segment triangls, for this project, I used HLBVH, which is a combination of Surface Area Heuristic (SAH) and morton code based Linear BVH. For more reference, check [PBRT 4.3 Bounding Volume Hierarchy](https://pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies).

And this gives a very good speed up

| Before BVH (< 10 fps) | After BVH (~30 fps) |
| ------------------------------- | ----------------------------- |
| ![](results/BVH/marioScene.png) | ![](results/BVH/AfterBVH.png) |

We can go even further with a stanford dragon (2,349,078 triangles)

| Dragon ( 15 fps) |
| --------------------------- |
| ![](results/BVH/Dragon.png) |

Visualizer:
| Wahoo | Dragon | Mineway Castle |
| ------------------------------- | ----------------------------- | --------------------------- |
| 5117 triangles | 2,349,078 triangles | 6,490,766 triangles |
| ------------------------------- | ----------------------------- | --------------------------- |
| ![](results/BVH/BVH.png) | ![](results/BVH/DragonBVH.png) | ![](results/BVH/CoverBVH.png) |
Binary file added results/BVH/AfterBVH.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 results/BVH/Dragon.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 results/BVH/marioScene.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 results/BVH/simplescene.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions scenes/cornellNoSphere.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"TYPE":"Emitting",
"RGB":[1.0, 1.0, 1.0],
"EMITTANCE":5.0
"EMITTANCE":10.0
},
"diffuse_white":
{
Expand All @@ -26,7 +26,16 @@
{
"TYPE":"Specular",
"RGB":[0.98, 0.98, 0.98],
"ROUGHNESS":0.0
"METALLIC":1,
"SUBSURFACE":0.0,
"SPECULAR":1,
"ROUGHNESS":0.0,
"SPECULARTINT":0.0,
"ANISOTROPIC":0.0,
"SHEEN":0.0,
"SHEENTINT":0.0,
"CLEARCOAT":0.0,
"CLEARCOATGLOSS":0.0
}
},
"Camera":
Expand Down Expand Up @@ -83,6 +92,15 @@
"TRANS":[5.0,5.0,0.0],
"ROTAT":[0.0,0.0,0.0],
"SCALE":[0.005,5.0,5.0]
},
{
"TYPE":"mesh",
"MATERIAL":"specular_white",
"FILENAME":"D:\\Fall2024\\CIS5650\\Project3-CUDA-Path-Tracer\\scenes\\objs\\Dragon.obj",
"TRANS":[0.0,4.6,0.0],
"ROTAT":[-85.0,0.0,0.0],
"SCALE":[0.04, 0.04, 0.04]
}
]

}
3 changes: 2 additions & 1 deletion src/PTDirectives.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ __inline__ __host__ __device__ float hash13(glm::vec3 v) {
float hash = glm::fract(sin(dotProduct) * 43758.5453f);

return hash;
}
}

34 changes: 6 additions & 28 deletions src/interactions.cu
Original file line number Diff line number Diff line change
Expand Up @@ -59,50 +59,28 @@ __device__ void scatterRay(
Light* dev_lights,
cudaTextureObject_t envMap)
{
glm::vec3 normal = intersection.surfaceNormal;
glm::vec2 uv = intersection.uv;

glm::vec3 wo = -pathSegment.ray.direction;
glm::vec3 wi = glm::vec3(0.0f);
glm::vec3 col = glm::vec3(1.0f);
Material mat = m;

// TODO: implement PBR model
thrust::uniform_real_distribution<float> u01(0, 1);
glm::vec2 xi = glm::vec2(u01(rng), u01(rng));
glm::mat3 ltw = LocalToWorld(normal);
glm::mat3 wtl = glm::transpose(ltw);

glm::vec3 wi = calculateRandomDirectionInHemisphere(intersection.surfaceNormal, rng);
float pdf = 0.f;

glm::vec3 bsdf = Sample_disneyBSDF(m, wo, xi, wi, ltw, wtl, pdf, rng);
if (pdf <= 0) {
pathSegment.remainingBounces = 0;
return;
}
col = bsdf * AbsDot(wi, normal) / pdf;
glm::vec3 bsdf = m.color;
pathSegment.remainingBounces--;

#ifdef DEBUG_NORMAL
col = glm::vec3(1.f);
pathSegment.color = DEBUG_NORMAL ? (normal + 1.0f) / 2.0f : normal;
pathSegment.accumLight = DEBUG_NORMAL ? (normal + 1.0f) / 2.0f : normal;
pathSegment.remainingBounces = 0;
#elif defined(DEBUG_WORLD_POS)
col = glm::vec3(1.f);
pathSegment.color = glm::clamp(intersect, glm::vec3(0), glm::vec3(1.0f));
pathSegment.accumLight = glm::clamp(intersect, glm::vec3(0), glm::vec3(1.0f));
pathSegment.remainingBounces = 0;
#elif defined(DEBUG_UV)
col = glm::vec3(1.f);
pathSegment.color = glm::vec3(uv, 0);
pathSegment.accumLight = glm::vec3(uv, 0);
pathSegment.remainingBounces = 0;
#endif
//pathSegment.color = glm::vec3(col);
//col = glm::vec3(1.0);
//pathSegment.remainingBounces = 0;

pathSegment.ray.origin = intersect;
pathSegment.ray.direction = glm::normalize(wi);
pathSegment.throughput *= col;
pathSegment.throughput *= bsdf;
}

__device__ void MIS(
Expand Down
4 changes: 2 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ int iteration;
int width;
int height;
OIDNDevice oidnDevice;

bool shadeSimple = false;
//-------------------------------
//-------------MAIN--------------
//-------------------------------
Expand Down Expand Up @@ -257,7 +257,7 @@ void runCuda()
// execute the kernel
int frame = 0;

pathtrace(pbo_dptr, pbo_post_dptr, frame, iteration);
pathtrace(pbo_dptr, pbo_post_dptr, frame, iteration, shadeSimple);
// unmap buffer object
cudaGLUnmapBufferObject(pbo);
cudaGLUnmapBufferObject(pbo_post);
Expand Down
2 changes: 1 addition & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extern int iteration;

extern int width;
extern int height;

extern bool shadeSimple;

void runCuda();
void keyCallback(GLFWwindow *window, int key, int scancode, int action, int mods);
Expand Down
123 changes: 96 additions & 27 deletions src/pathtrace.cu
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,65 @@ __global__ void computeIntersections(
}
}

__global__ void shadeMaterialSimple(
int iter,
int num_paths,
ShadeableIntersection* shadeableIntersections,
PathSegment* pathSegments,
Material* materials,
cudaTextureObject_t envMap,
int num_lights,
LinearBVHNode* dev_nodes,
Triangle* dev_triangles,
Light* dev_lights,
int depth,
bool firstBounce)
{
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < num_paths)
{
ShadeableIntersection intersection = shadeableIntersections[idx];
PathSegment pathSegment = pathSegments[idx];
#ifdef DEBUG_BVH
//scatterRay(pathSegment, getPointOnRay(pathSegment.ray, intersection.t), intersection.t, intersection.surfaceNormal, intersection.uv, material, rng);
pathSegment.accumLight += glm::vec3(intersection.hitBVH);
pathSegment.throughput = glm::vec3(1.0);
pathSegment.remainingBounces = 0;
#else

if (intersection.t > 0.0f) // if the intersection exists...
{
thrust::default_random_engine rng = makeSeededRandomEngine(iter, idx, 0);
thrust::uniform_real_distribution<float> u01(0, 1);

Material material = materials[intersection.materialId];
glm::vec3 materialColor = material.color;

pathSegment.distTraveled += intersection.t;
// If the material indicates that the object was a light, "light" the ray
if (material.emittance > 0.0f) {
pathSegment.remainingBounces = 0;
pathSegment.accumLight += pathSegment.throughput * materialColor * material.emittance;
}
else
{
scatterRay(pathSegment, intersection, getPointOnRay(pathSegment.ray, intersection.t), material, rng, num_lights, dev_nodes, dev_triangles, dev_lights, envMap);
//MIS(pathSegment, intersection, getPointOnRay(pathSegment.ray, intersection.t), material, rng, num_lights, dev_nodes, dev_triangles, dev_lights, envMap, depth, firstBounce);
}

}
else {
//pathSegment.color += getEnvironmentalRadiance(pathSegment.ray.direction, envMap);
glm::vec3 radiance = getEnvironmentalRadiance(pathSegment.ray.direction, envMap);
pathSegment.accumLight += pathSegment.throughput * radiance;
pathSegment.remainingBounces = 0;
}
#endif

pathSegments[idx] = pathSegment;
}
}


__global__ void shadeMaterialNaive(
int iter,
Expand Down Expand Up @@ -350,18 +409,6 @@ __global__ void shadeMaterialNaive(
pathSegment.distTraveled += intersection.t;
// If the material indicates that the object was a light, "light" the ray
if (material.emittance > 0.0f) {
// glm::vec3 radiance(0);
// if (material.isLight)
// {
// float pdf = 0;
//radiance = Evaluate_Li(pathSegment.ray.direction, pathSegment.ray.origin, pdf, intersection.lightId, num_lights, envMap, dev_nodes, dev_triangles, dev_lights);
//radiance = radiance / pdf;
// }
// else
// {
//radiance = material.emittance * materialColor;
// }
// pathSegment.accumLight += pathSegment.throughput * radiance;
pathSegment.remainingBounces = 0;
pathSegment.accumLight += pathSegment.throughput * materialColor * material.emittance;
}
Expand Down Expand Up @@ -435,7 +482,7 @@ struct sortByIsectDIMat
* Wrapper for the __global__ call that sets up the kernel calls and does a ton
* of memory management
*/
void pathtrace(uchar4* pbo, uchar4* pbo_post, int frame, int iter)
void pathtrace(uchar4* pbo, uchar4* pbo_post, int frame, int iter, bool shadeSimple)
{


Expand Down Expand Up @@ -502,20 +549,42 @@ void pathtrace(uchar4* pbo, uchar4* pbo_post, int frame, int iter)


cudaEventRecord(gpuInfo->start);
shadeMaterialNaive << <numblocksPathSegmentTracing, blockSize1d >> > (
iter,
curr_paths,
dev_intersections,
dev_paths,
dev_materials,
envMap,
envMap == NULL ? hst_scene->lights.size() : hst_scene->lights.size() + 1,
dev_nodes,
dev_triangles,
dev_lights,
depth,
depth == 1
);

if (shadeSimple)
{
shadeMaterialSimple << <numblocksPathSegmentTracing, blockSize1d >> > (
iter,
curr_paths,
dev_intersections,
dev_paths,
dev_materials,
envMap,
envMap == NULL ? hst_scene->lights.size() : hst_scene->lights.size() + 1,
dev_nodes,
dev_triangles,
dev_lights,
depth,
depth == 1
);
}
else
{
shadeMaterialNaive << <numblocksPathSegmentTracing, blockSize1d >> > (
iter,
curr_paths,
dev_intersections,
dev_paths,
dev_materials,
envMap,
envMap == NULL ? hst_scene->lights.size() : hst_scene->lights.size() + 1,
dev_nodes,
dev_triangles,
dev_lights,
depth,
depth == 1
);
}

cudaEventRecord(gpuInfo->stop);
cudaEventSynchronize(gpuInfo->stop);
float elapsedTime = 0.0f;
Expand Down
2 changes: 1 addition & 1 deletion src/pathtrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
void InitDataContainer(GuiDataContainer* guiData);
void pathtraceInit(Scene *scene);
void pathtraceFree();
void pathtrace(uchar4 *pbo, uchar4* pbo_post, int frame, int iteration);
void pathtrace(uchar4 *pbo, uchar4* pbo_post, int frame, int iteration, bool shadeSimple);
5 changes: 5 additions & 0 deletions src/preview.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,11 @@ void RenderImGui()
}
}

// add a checkbox for shade simple and check if its status changed
if (ImGui::Checkbox("Shade Simple", &shadeSimple))
{
iteration = 0;
}

// LOOK: Un-Comment to check the output window and usage
//ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
Expand Down
1 change: 1 addition & 0 deletions src/scene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ void Scene::loadFromJSON(const std::string& jsonName)
if (p.contains("CLEARCOAT")) newMaterial.clearcoat = p["CLEARCOAT"];
if (p.contains("CLEARCOATGLOSS")) newMaterial.clearcoatGloss = p["CLEARCOATGLOSS"];
if (p.contains("IOR")) newMaterial.ior = p["IOR"];
if (p.contains("EMITTANCE")) newMaterial.emittance = p["EMITTANCE"];
if (p.contains("TYPE"))
{
std::string matType(p["TYPE"]);
Expand Down

0 comments on commit 2c2fa0a

Please sign in to comment.