@@ -8,32 +8,40 @@ CUDA Path Tracer
88* Tested on: Tested on: Windows 10, i3-10100F @ 3.6GHz 16GB, GeForce 1660 Super 6GB
99
1010### Features
11- ![ nice image or two showing off some features] ( )
11+ ![ Stanford tryranosaurus] ( img/trex.png )
12+ ![ DOF example] ( img/DOF.png )
1213
1314This is a GPU based forward path tracer, which renders scenes by calculating "camera rays" bouncing around the scene,
1415simulating individual light rays. The renderer supports the following features:
1516- Arbitrary mesh loading using .obj file format
1617- Multiple shader BSDFs, including refraction
1718- Anti-aliasing
1819- Depth of Field
19- - Minor optimizations
20+ - Several optimizations
2021- Adaptive Sampling* (not fully implemented)
2122- Bloopers
2223
2324### Arbitrary mesh loading
24- The renderer supports loading arbitrary meshes via .obj files.
25+ The renderer supports loading arbitrary meshes via .obj files using
26+ (` tinyobjloader ` )[ https://github.com/tinyobjloader/tinyobjloader ] .
2527
26- One issue discovered was that the triangle intersection detection function
27- initially used (` glm::intersectRayTriangle() ` ) does not compute intersections
28- with the "back" of faces. This caused problems for open meshes like the Newell Teapot
29- or for meshes assigned a refraction shader, as can be seen here:
30- ![ newell teapot hole image] ( )
31- ![ incomplete refraction image] ( )
3228A bounding box is calculated at load time and used to optimize ray intersection
3329detection. The bounding box is a mesh itself, consisting of tris. Each ray in
3430the scene is initially tested for intersection with these tris, and only if an
3531intersection is found will the ray be checked against the mesh's tris.
3632
33+ One issue discovered was that the triangle intersection detection function
34+ initially used (` glm::intersectRayTriangle() ` ) does not compute intersections
35+ with the "back" of faces. This caused problems for open meshes like the Newell Teapot
36+ or for meshes assigned a refraction shader, as can be seen here:
37+ ![ newell teapot hole image] ( img/back_face_cull_issue.png )
38+ Also somewhat visible through the noise is a secondary effect of this back face
39+ issue: collisions with the bounding box are not detected from within the
40+ bounding box. Notice the sharp lines on the floor cutting off the diffuse
41+ bounce close to the teapot. This is from rays on the floor near the teapot
42+ casting outward and missing the teapot bounding box, and therefore not
43+ checking for collisions with any of the teapots actual tris.
44+
3745Performance impacts:
3846- no spatial optimizations are made (other than the bounding box), so each
3947ray that hits the bounding box is checked against every triangle in the mesh.
@@ -48,25 +56,27 @@ inherit whatever memory value the normals were initialized to.
4856BSDFs are implemented to allow for pure diffuse objects, objects with diffuse
4957and reflections, as well as objects with both reflection and refraction. The
5058Fresnel effect is calculated using Schlick's approximation
51- ![ image showing off different materials] ( )
5259
5360Since physically correct models do not always provide the preferred result, the
5461Fresnel effect is tuneable via a user parameter. Note this is separate from the
5562index of refraction (also tuneable), this is an additional parameter which controls
5663the power used in Schlick's approximation.
57- ![ image showing different Fresnel powers] ( )
58-
59- Performance impacts:
60- - TODO: compare scene performance with/without some shader types
64+ ![ Fresnel power comparison] ( img/fresnel_comparison.png )
65+ The sphere on the right has a Fresnel power of 1, which dramatically changes
66+ the reflect/refract ratio in favor of reflection. The sphere in the middle has
67+ a Fresnel power of 3, which is only a subltle change from the (standard)
68+ Fresnel power of 5 on the rightmost sphere.
6169
6270Known limitations:
6371- objects with refractions are assumed to have reflection. An object can be reflective without
6472refraction, but not vice-versa.
6573
6674### Anti-aliasing
6775anti-aliasing was accomplished by jittering the origin of camera rays for the initial bounce.
68- ![ image without anti-aliasing] ( )
69- ![ image with anti-aliasing] ( )
76+ ![ image without anti-aliasing] ( img/antialias_off.png )
77+ ![ image with anti-aliasing] ( img/antialias_on.png )
78+ The first image has no antialiasing and has jagged pixelated edges along
79+ horizontal lines. The second image has cleaner lines with no notable "jaggies".
7080
7181Performance impacts:
7282- An unnoticeable impact to the time it takes each pixel to converge as a result of adding some small randomness.
@@ -76,15 +86,19 @@ slightly varied camera ray origins each iteration.
7686### Depth of Field
7787Depth of field can optionally be simulated, with tuneable parameters for aperture size, lens radius,
7888and focal distance.
79- ![ image showing off depth of field] ( )
89+ ![ DOF example] ( img/DOFOFF.png )
90+ ![ DOF example] ( img/DOF.png )
91+ The first image shows a scene with no simulated depth of field. The second
92+ image has depth of field turned on, simulating the blur according to distance
93+ in the same way a physical camera lens would.
8094
8195Performance impacts:
8296- Using DOF requires a greater number of iterations to produce a clean image. The blur is a result
8397of a stochastic process, and as a result the greater the blur the larger the variance of each blurred pixel
8498Known limitations:
8599- This can not be combined with the "first bounce cache" optimization as it depends on
86100slightly varied camera rays each iteration.
87- ### Minor optimizations
101+ ### Optimizations
88102- first bounce cache
89103An option is provided to cache the first bounce of each camera ray from iteration 1, and use that cache
90104for each subsequent iteration (until the camera is moved, generating a new iteration 1 and a new cache).
@@ -94,13 +108,25 @@ different code paths as a result of conditionals), rays can optionally be
94108sorted by their material id. This manimizes the number of warps with different
95109materials, which may take different amounts of time as a result of calculated
96110differing BSDFs.
97- - cull dead bounces
111+ - use stream compaction to cull dead bounces
98112Bounces that do not hit an object (i.e. which go off into space) are culled every iteration.
99113
114+ The following data was gathered from a single test scene using multiple
115+ shaders, across all available BRDFS. All renders were run to 100 iterations at
116+ a resolution of 720x480. Here is the test scene at 5000 iterations:
117+ ![ test scene full render] ( img/test_scene.png )
118+
119+ ![ optimization comparison] ( img/optimization_comparison.png )
100120Performance impacts:
101- - first bounce cache provides a noticeable improvement (TODO add a metric for this)
102- - Sorting materials is noteably worse. (TODO provide a metric)
103- - culling dead bounces (I think?) has a relatively neutral impact (TODO confirm and add metric)
121+ - notably, all optimizations are slightly worse for a trace depth of 8, when
122+ the benefit of these optimizations has not yet outweighed their overhead.
123+ - first bounce cache provides a steady, but minor improvement.
124+ - Stream compaction provides the most dramatic improvement, even in a scene
125+ that is mostly filled by collideable objects.
126+ - sorting materials provides a notable decrease in render times which increases
127+ slightly as the trace depth increases.
128+ - All optimizations provide a performance increase of approximately 2x!
129+
104130Known limitations:
105131- As noted above, first bounce cache cannot be combined with DOF or anti-aliasing.
106132### Adaptive Sampling* (incomplete)
@@ -128,11 +154,29 @@ pixels of the image are always culled instead of the pixels which have
128154converged. This is likely due to a sorting mismatch, or using the wrong number
129155of paths when calling some relevant function. This has not been fixed in time.
130156
131- ![ accurate heatmap showing incorrect sampling] ( )
157+ ![ Disfunctional adaptive sampling] ( img/adaptiveSampleBug.png )
158+ ![ accurate heatmap showing incorrect sampling] ( img/heatmap.png )
159+ White represents pixels which required the maximum number of iterations, black
160+ indicates immediate culling. Note the gradient in the heat map showing that
161+ pixels are culled from the bottom up as iterations increase. This reflects what
162+ can be seen in the render itself: the bottom is noisy and gets less noisy
163+ towards the top.
132164### Bloopers
133- ![ several] ( )
134- ![ bloopers] ( )
135- ![ here] ( )
136- ![ with] ( )
137- ![ explanations] ( )
165+ ![ stream compaction VHS] ( img/stream_compaction_blooper.png )
166+ This image is the result of a stream compaction issue. The VHS-like look of it
167+ is amplified by the banded noise at the top, which is the result of a race
168+ condition when sorting paths.
169+ ![ refraction hall of horrors] ( img/refraction_mesh_blooper.png )
170+ ![ refraction hall of horrors] ( img/refraction_mesh_blooper2.png )
171+ These works of art were created while attempting to fix the mesh back face
172+ collision detection issue described above.
173+
174+ ### Notable Sources
175+ - As noted above,
176+ (` tinyobjloader ` )[ https://github.com/tinyobjloader/tinyobjloader ] was used for
177+ mesh loading.
178+ - As noted in the comments, Stack Exchange and Stack overflow
179+ provided the math for two vector manipulation methods
180+ - Matt Pharr & Grep Humphreys Physically Based Rendering Texbook provided useful context
181+
138182
0 commit comments