Add more to the writeup

This commit is contained in:
Michael Zhang 2023-02-02 02:34:51 -06:00
parent 00000230ee
commit 000002404e
7 changed files with 128 additions and 25 deletions

View file

@ -36,7 +36,7 @@ examples/%.ppm: examples/%.txt
examples/%.png: examples/%.ppm
convert $< $@
writeup.pdf: writeup.md
writeup.pdf: writeup.md $(EXAMPLES_PNG)
$(PANDOC) -o $@ $<
clean:

View file

@ -0,0 +1,21 @@
imsize 640 480
eye 0 0 15
viewdir 0 0 -1
hfov 60
updir 0 1 0
bkgcolor 0.1 0.1 0.1
mtlcolor 0 0.5 0.5
sphere -1 -2 -5 2
sphere 3 -5 -1 0.5
mtlcolor 0.5 0.5 1
sphere 1 2 -3 3
sphere -6 3 -4 1
mtlcolor 0.5 0 0.5
sphere 5 5 -1 1
sphere -6 -4 -8 7
mtlcolor 0.5 1 0.5
cylinder 5 1 -2 1 -2 1 1 2

View file

@ -0,0 +1,21 @@
imsize 640 480
eye 0 0 15
viewdir 0 0 -1
hfov 30
updir 0 1 0
bkgcolor 0.1 0.1 0.1
mtlcolor 0 0.5 0.5
sphere -1 -2 -5 2
sphere 3 -5 -1 0.5
mtlcolor 0.5 0.5 1
sphere 1 2 -3 3
sphere -6 3 -4 1
mtlcolor 0.5 0 0.5
sphere 5 5 -1 1
sphere -6 -4 -8 7
mtlcolor 0.5 1 0.5
cylinder 5 1 -2 1 -2 1 1 2

View file

@ -0,0 +1,9 @@
imsize 640 480
eye 0 0 8
viewdir 0 0 -1
hfov 60
updir 0 1 0
bkgcolor 1 1 1
mtlcolor 0.5 1 0.5
cylinder 0 0 -4 1 0 0 1 8

View file

@ -0,0 +1,9 @@
imsize 640 480
eye 0 0 8
viewdir 0 0 -1
hfov 60
updir 1 1 0
bkgcolor 1 1 1
mtlcolor 0.5 1 0.5
cylinder 0 0 -4 1 0 0 1 8

View file

@ -57,7 +57,7 @@ impl ObjectKind for Cylinder {
let discriminant = b * b - 4.0 * a * c;
let mut solutions = match discriminant {
let possible_side_solutions = match discriminant {
// Discriminant < 0, means the equation has no solutions.
d if d < 0.0 => vec![],
@ -76,6 +76,16 @@ impl ObjectKind for Cylinder {
_ => unreachable!("Invalid determinant value: {discriminant}"),
};
// Filter out solutions that don't have a valid Z position.
let side_solutions = possible_side_solutions.into_iter().filter(|t| {
let ray_point = ray.eval(*t);
let rotated_ray_point = rotation_matrix * ray_point;
let z = rotated_ray_point.z - rotated_cylinder_center.z;
// Check to see if z is between -len/2 and len/2
z.abs() < self.length / 2.0
});
// We also need to add solutions for the two ends of the cylinder, which
// uses a similar method except backwards: check intersection points
// with the correct z-plane and then see if the points are within the
@ -96,26 +106,19 @@ impl ObjectKind for Cylinder {
(o.z + c.z - self.length / 2.0) / r.z,
]
};
// Filter out all the solutions where the z does not lie in the circle
solutions.extend(possible_z_intersections.into_iter().filter(|t| {
let end_solutions = possible_z_intersections.into_iter().filter(|t| {
let ray_point = ray.eval(*t);
ray_point.x.powi(2) + ray_point.y.powi(2) <= self.radius.powi(2)
}));
});
// Filter out solutions that don't have a valid Z position.
let solutions = solutions
.into_iter()
.filter(|t| {
let ray_point = ray.eval(*t);
let rotated_ray_point = rotation_matrix * ray_point;
let z = rotated_ray_point.z - rotated_cylinder_center.z;
// Check to see if z is between -len/2 and len/2
z.abs() < self.length / 2.0
})
.filter_map(|t| NotNan::new(t).ok());
let solutions = side_solutions.into_iter().chain(end_solutions.into_iter());
// Return the minimum solution
solutions.min().map(|t| t.into_inner())
solutions
.filter_map(|t| NotNan::new(t).ok())
.min()
.map(|t| t.into_inner())
}
}

View file

@ -5,6 +5,10 @@ output: pdf_document
# Raycaster
#### Michael Zhang \<zhan4854@umn.edu\>
---
Determining the viewing window for the raycaster for this assignment involved
creating a "virtual" screen in world coordinates, mapping image pixels into that
virtual screen, and then casting a ray through each pixel's world coordinate to
@ -22,7 +26,15 @@ how many degrees the screen should take up.
Changing the angle of the field of view would result in a wider or narrower
screen, which when paired with the aspect ratio (width / height), would produce
a bigger or smaller viewing screen, like the orange box in the above diagram
shows. Simply put, FOV affects how _much_ of the frame you're able to see.
shows. Simply put, FOV affects how _much_ of the frame you're able to see. An
example is shown here:
![](examples/fov-demo-1.png){width=180px}\ ![](examples/fov-demo-2.png){width=180px}
The left image uses an FOV of 60, while the right image uses an FOV of 30. As
you can see, the left side has a wider range of vision, which allows it to see
more of the world. (both images can be found in the `examples` directory of the
handin zip)
Curiously, distance from the eye actually doesn't really affect the viewing
screen very much. The reason is the screen is only used to determine how to
@ -34,10 +46,16 @@ dimensions)
The up-direction vector controls the rotation of the scene. Without the
up-direction, it would not be possible to tell which rotation the screen should
be in:
be in.
![Rotation determined by up direction](doc/rot.jpg){width=240px}
To see what this looks like, consider the following images, where the left side
uses an up direction of $(0, 1, 0)$, while the right side uses $(1, 1, 0)$ (both
images can be found in the `examples` directory of the handin zip)
![](examples/up-dir-demo-1.png){width=180px}\ ![](examples/up-dir-demo-2.png){width=180px}
Together, all of these parameters can uniquely determine a virtual screen
location, that we can use to cast rays through and fill pixels. We can change
any of these to produce an image with a more exaggerated view of the scene for
@ -76,7 +94,8 @@ $\frac{\Delta x + \Delta y}{2}$ to the point to get that)
Because of the way I implemented parallel projection, it's recommended to
either put the eye much farther back, or use `--distance` to force a much bigger
distance from the eye for the raycaster. This is due to the size of the image.
distance from the eye for the raycaster. See the `--help` to see how this option
is used.
### Cylinder Intersection Notes
@ -84,9 +103,30 @@ First, we will transform the current point into the vector space of the
cylinder, so that the cylinder location is $(0, 0, 0)$ and the direction vector
is normalized into $(0, 0, 1)$.
Then it's a matter of determining if the $x$ and $y$ coordinates fall into the
space constrained by the equation $(ox + t\times rx - cx)^2 + (oy + t\times ty -
cy)^2 = r^2$ and if $z \le L$.
This can be done by using a rotation matrix (since we are sure this
transformation is just a rotation). This rotation is actually a 2D rotation,
around the normal between the cylinder direction and $(0, 0, 1)$. We then
rotate everything we are working with (the cylinder and the ray) into this
coordinate system to make calculations easier.
See the comments in the code for a more detailed explanation of the equation
used.
Then it's a matter of determining if the $x$ and $y$ coordinates fall into the
space constrained by the equation $(o_x + t\times r_x - c_x)^2 + (o_y + t\times
r_y - c_y)^2 = r^2$ and if $z \le L$. I can solve this using the quadratic
formula the same way as the sphere case.
We want a quadratic equation of the form $At^2 + Bt + C = 0$. The values for
$A$, $B$, and $C$ are:
- $A = r_x^2 + r_y^2$
- $B = 2(r_x(o_x - c_x) + r_y(o_y - c_y))$
- $C = (c_x - o_x)^2 + (c_y - o_y)^2 - r^2$
Solving this for $t$ yields 0-2 solutions depending on if the equation was
satisfied or not. Then, we can plug any solutions we get back into the ray
equation and determine if the $z$-coordinate is in the range of the cylinder
that we want.
We will also have to do this for the ends of the cylinder, but just backwards.
So we would start with the $z$-coordinate, solve for $t$s where the ray hits
that $z$-plane, and then check the $x$ and $y$ values to see if they satisfy the
ray equation as well.