A. Neural Volume Rendering
0. Transmittance Calculation
1. Differentiable Volume Rendering
Question 1.5: Volume rendering
2. Optimizing a basic implicit volume
Question 2.3: Visualization
3. Optimizing a Neural Radiance Field (NeRF)
4. NeRF Extras
Question 4.1: View Dependence
The way I implemented this is similar to how it is descibed in the original NeRF paper. An MLP takes the spatial coordinate embedding as input and spits out features that are input to a density head to predict the density. For the view-independent case, these features are input to a color head to predict the color. In the view-dependent case, the features are concatenated with the direction embedding to and feed to the color head to predict color. Clearly, the view-depedent case exhibits view-depedence, and is particularly prominent in the red ball (2nd row, 2nd column).
B. Neural Surface Rendering
Question 5: Sphere Tracing
I started with initializing a mask (to zeros) to keep track of all pixels for which we can find valid intersections. Also initialized a set of points for each pixel (to the origin). At every iteration, We evaluate the SDF at the points corresponding to each pixel and update the points by adding the SDF value times the direction for the ray corresponding to that pixel. This ensures that the point for each pixel never crosses the surface because as the point approaches the surface, the distance (SDF value) would approach zero.
points = origins.clone()
mask = torch.zeros_like(origins[:, :1], dtype=torch.bool)
for _ in range(self.max_iters):
distances = implicit_fn(points)
points = points + distances * directions
mask = torch.abs(distances) < 0.001
return points, mask
Question 6: Optimizing a Neural SDF
For the MLP, I used the same standard MLP with skip connections I used for the NeRF section. The only thing worth noting is that I did not use an activation function for the output layer as the distance value does not have a specific range.
The eikonal loss ensures that the neural function we optimize is approximately a signed-distance function by enforcing that the magnitude of the gradients are equal to unity everywhere. This is how I implemented it:
def eikonal_loss(gradients):
# TODO (Q6): Implement eikonal loss
return torch.square(torch.norm(gradients, dim=-1) - 1.0).mean()
Question 7: VolSDF
The alpha parameter governs the nearly constant density within a body and the beta parameter governs how fast the density falls from within the body to outside the body (near the surface).
1. A high beta leads to a fuzzier SDF, whereas a low beta leads to a sharper SDF. This makes sense intuitively because beta governs the rate of falloff of the density on the surface. A steeper falloff (low beta) would lead to sharper boundaries and being able to resove finer structures at the cost of increased floaters.
2. In general, both high and low beta values have tradeoffs as discussed in the previous part. If we are okay with a fuzzier, but cleaner geometry without floaters, a high beta would work well. If we want to resolve fine structures, but at the cost of floaters, a low beta works well. Although, reducing beta too much could possibly lead to too many floaters (or very noisy reconstructions).
3. As discussed in the previous two parts, a low beta leads to a more accurate surface (finer structures can be resolved better) because the density falls off steeply with a low beta.
I chose alpha = 10.0 because any value of alpha less that that led to fuzzy reconstructions and any value of alpha above that did not seem to make a noticeable difference. I chose beta = 0.07 because any value of beta above that led to too many floater and any value of beta above that led to reconstructions that were too fuzzy.
8. Neural Surface Extras
Question 8.2: Fewer Training Views
VolSDF Geometry - 5 views