In this project we are rendering 3d objects while optmizing their rendering, adding lighting, and overall learning about algorithims used to render lighting, coloring, and 3d models.
Part 1: Ray Generation and Scene Intersection
Ray Generation
Convert (x,y) to 2x2 matrix
Map to camera space at z = -1
Rotate/translate to world space
Clip by [min_t, max_t]
Primitive Intersection
Use a BVH to prune
Loop through triangles and use Möller–Trumbore to find a valid intersection
Loop through sphere and solve the quadratic for r^2
Update ray.max_t and record intersection in an Intersection class if valid
Möller–Trumbore finds the triangle's intersection using barycentric coordinates using cross and dot products of the ray direction and triangle edges. It relys on checking if intersection point is in the triangle and ultimetely returns the parametric distance of the ray if its valid.
Rendering of the CBempty.dae file.
Rendering of the CBspheres_lambertian.dae file.
Part 2: Bounding Volume Hierarchy
BVH construction algorithm:
After finding the bounding box, we check if the number of primitives is less than the max_leaf_size, and if it is we store them as leaf nodes
If the number of primitives is more than the max_leaf_size, we find the bounding box optmizing for splitting
Then we split the primitives based on the midpoint, comparing if the center is greater than or less than the mid point. Finally recursively find the left and right subtrees to create a BVH
Rendering of the Maxplanck.dae file.
Rendering of the peter.dae file.
At the beganinng of this part of the assignment, I was seeing rendering times taking up to 30 seconds. This typically isn't wildly for a loading complicated files on my laptop, however for us to be able to render things like 3D modals or other large files, this isn't optimized. When using the BVH algorithm, my loading times became almost instantious, but the proof is in the pudding:
First we need to change how this function opperates in order to test the difference between BVH and no BVH: BVHAccel::BVHAccel(const std::vector &_primitives,
size_t max_leaf_size). By changing the max max_leaf_size, to simply be the size of the prematives, we can test this more seemlessly.
Using the cow.dae file, we are able to deduce that without BVH: 6.8851s for rendering (which takes up the most time)
When using BVH, rendering is cut down to 0.0465s.
When using a larger file like peter.dae. we get 61.6721s without BVH. With BVH, we get 0.0531s for rendering which is a mind blowing difference.
Overall, we can see there is a big difference when using BVH.
Part 3: Direct Illumination
Implementations of the direct lighting function:
Uniform hemisphere sampling: Sampled directions equally over the hemisphere, casted shadow rays in the sampled direction, unless blocked - in which case we accumulate light contribution
Light sampling: For each light, we sampled it's direction and casted it's shadow rays.
Comparision:
Generally speaking, in terms of just methodology, with Uniform sampling, we can see that there is a lot more noise in the photos. We can also see more inconsitences in terms of where the light is going rather than a more focused approach of light on the bunny. For example, with uniform sampling, the bunny's ear is a lot more bright than compared to light sampling. This is because the light sampling is basically taking the lights actual ray direction, distance, etc. into account in showing how much lighting is actually applied to the object. Overall, it can be pretty clearly seen that the light sampling leads to better lighting quality.
Part 4: Global Illumination
Implementation of the indirect lighting:
initialize local coordinates
We then use sample_f to sample an incoming direction and get the pdf
Next we flip a coin to decide whether to stop this recursive loop, returning direct lighting if we do stop it
Finally we take the direction and transform it to fit in the world. Then we create a new bounce ray with less depth and keep recruisively doing this proccess till there is no more depth
Recursive Accumulation
CBBunny Global (direct and indirect) illuminationEmpty Global (direct and indirect) illumination
Clearly, when looking at these pictures the indirect lighting is brighter and overall looks better than direct lighting. The direct lighting has too many darkly shaded shadows that don't look natural.
direct
indirect
m = 0, isAccumBounces = false
m=1, isAccumBounces = false
m=2, isAccumBounces = false
m=3, isAccumBounces = false
m=4, isAccumBounces = false
m=5, isAccumBounces = false
m=0, isAccumBounces = true
m=1, isAccumBounces = true
m=2, isAccumBounces = true
m=3, isAccumBounces = true
m=4, isAccumBounces = true
m=5, isAccumBounces = true
m = 100, isAccumBounces = true
When looking at the different sample-per-pixel rates, that as we go up in value, the edges get smoother.
s = 1
s = 2
s= 4
s= 8
s= 16
s= 64
s= 1024
Part 5: Adaptive Sampling
Explain adaptive sampling. Walk through your implementation of the adaptive sampling.
Adaptive Sampling: This is an algorithm that basically adjusts how many samples we are using for every pixel we render vs using a fixed number in the Monte Carlo Path tracing function.
Implementation:
Initialize accumulators to track sum and sum squared of s1 and s2 respectively.
Then For each pixel gen a camera ray with a rand. offset, and trace through scene to update s1 and s2
Next use batches of samples & their illuminaces to calculate confidence intervals
If the confidence intervals is less than the max tollarance, we stop converging and calcuate the color of pixel.