<- back
hw04 help hours | codebase | docs | notes | glow

🚨 please pull a fresh copy of the codebase before starting this homework!--it has the starter code in main.cpp :) 🗣️ you are highly encouraged to collaborate on homework provided you follow the spirit of the 50 ft rule 👥 additionally, for this lab you may optionally choose to work with a partner - only one of you is required to submit on glow (please add a glow comment saying who your partner is)
goals. - understand cameras - get experience diving into an unfamiliar program
non-optional reading. coordinate systems a coordinate system is an x-axis $\mathbf{\hat x}$, a y-axis $\mathbf{\hat y}$, a z-axis $\mathbf{\hat z}$, and an origin $\mathbf{o}$ the axes are vectors and the origin is a point note: here it is implied that the quantities $\mathbf{\hat x}, \mathbf{\hat y}, \mathbf{\hat z}, \mathbf{o}$ are written in world coordinates - they have to be written in something! note: *unlike the slides from class,* here it is implied that everything is written in homogeneous coordinates - this makes the derivation way cleaner it is convenient to pack a coordinate system into a 4x4 matrix like this $\mathbf{C} = \begin{bmatrix}\mathbf{\hat x} & \mathbf{\hat y} & \mathbf{\hat z} & \mathbf{o} \end{bmatrix} = \begin{bmatrix} {\hat x}_x & {\hat y}_x & {\hat z}_x & {o}_x \\ {\hat x}_y & {\hat y}_y & {\hat z}_y & {o}_y \\ {\hat x}_z & {\hat y}_z & {\hat z}_z & {o}_z \\ 0 & 0 & 0 & 1 \end{bmatrix}$ to do this in code, you could call mat4 M4_xyzo(vec3 x, vec3 y, vec3 z, vec3 o); note: observe that the world coordinate system (in world coordinates) is the 4x4 identity matrix consider a point written in coordinate system $\mathbf{C}$ as $\mathbf{p_C} = (x_\mathbf{C}, y_\mathbf{C}, z_\mathbf{C}, 1)^T$ note that $\mathbf{p}_\text{world} = \mathbf{C}\mathbf{p_C} = \mathbf{o} + x_\mathbf{C} \mathbf{\hat x} + y_\mathbf{C} \mathbf{\hat y} + z_\mathbf{C} \mathbf{\hat z}$ i.e., we start at the origin $\mathbf{o}$ and then walk along each axes by the prescribed amounts (just like plotting points in Algebra 1) to do this in code, you could call vec3 transformPoint(mat4 C, vec3 p_C); a similar fact is true for vectors; considering vector $\mathbf{v_C}$ written in coordinate system $\mathbf{C}$, we have $\mathbf{v}_\text{world} = \mathbf{C}\mathbf{v_C}$ to do this in code, you could call vec3 transformVector(mat4 C, vec3 v_C);
there are a few special coordinate systems that we give names model coordinates (aka modeling coordinates, aka object coordinates) - this is the coordinate system of a single mesh -- if you ever sculpt/model in Blender or zBrush, you're working in model coordinates -- when you were scripting up the cylinder by hand, you were working in model coordinates camera coordinates - this is the coordinate system of a virtual camera -- by convention, the camera's x-axis (red) points its right, and y-axis (green) points up --- assuming a right-handed coordinate system, this means the camera's z-axis (blue) points out its back world coordinates - this is the coordinate system of the world (scene) -- when you place an object in the scene, you're working in world coordinates normalized device coordinates (NDC) - when OpenGL actually draws, it assumes vertices are in NDC - the lower left is $(-1.0, -1.0)^T$ and the upper right is $(1.0, 1.0)^T$ - this is a very useful coordinate system for getting (usefully scaled) mouse movement! -- globals.mouse_change_in_position_NDC is a vec2 telling you how far the user moved their mouse since the last frame (in NDC!)
there are a few special matrices that we give names model matrix $\mathbf{M}$ - it takes us to world coordinates from model coordinates, i.e. $\mathbf{p}_\text{world} = \mathbf{M}\mathbf{p}_\text{model}$ - a convenient way to build a model matrix is $\mathbf{TRS}$ -- this is a 3D scaling by $\mathbf{s}$, followed by a 3D rotation $\mathbf{R}$, followed by a 3D translation by $\mathbf{t}$ i.e., $\begin{bmatrix}\mathbf{R}\text{diag}(\mathbf{s}) & \mathbf{t} \\ \mathbf{0}^T & 1\end{bmatrix}$ camera matrix $\mathbf{C}$ - the camera matrix is the camera's axes and origin packed into a 4x4 homogeneous matrix $\mathbf{C} = \begin{bmatrix}\mathbf{\hat x} & \mathbf{\hat y} & \mathbf{\hat z} & \mathbf{o} \end{bmatrix}$ - the camera matrix takes us to world coordinates from camera coordinates, i.e., $\mathbf{p}_\text{world} = \mathbf{C}\mathbf{p}_\text{camera}$ - we can see a (typical) camera matrix as a rotation followed by a translation $\begin{bmatrix}\mathbf{R} & \mathbf{t} \\ 0 & 1 \end{bmatrix}$ -- this is NOT how we'll actually *construct* every camera matrix in this homework!--though it is how we'll construct at least one of them :) view matrix $\mathbf{V} = \mathbf{C}^{-1}$ - the view matrix is the inverse of the camera matrix - the view matrix takes us to camera coordinates from world coordinates, i.e., $\mathbf{p}_\text{camera} = \mathbf{V}\mathbf{p}_\text{world}$ projection matrix $\mathbf{P}$ - the projection matrix takes us to NDC from camera coordinates, i.e., $\mathbf{p}_\text{NDC} = \mathbf{P}\mathbf{p}_\text{camera}$ "transform" $\mathbf{PVM}$ - it takes us alllll the way to NDC from model coordinates - soup_draw(mat4 PVM, ...); takes this
special camera matrix constructions sometimes sort of get names - you're welcome to google (e.g. "orbit camera graphics") for help, but please first try them without googling -- all of these cameras can be figured out from first principles (i.e., that a camera points along its negative z-axis) --- also a little trial and error i call the ORBIT_CAMERA an OrbitCamera - it always points at the world origin - it has a notion of the world y-axis being "up" i call the HUMAN_CAMERA an FPSCamera (first person shooter camera) - it has a notion of where the payer is standing - it has a notion of the world y-axis being "up" i call the TRACK_CAMERA a TrackingCamera, but it is more typically known as a "lookAt" camera (after some long forgotten function in an old OpenGL helper library) - its origin is at a fixed position in space (never changes) - it "looks at" a target (note that internally TrackingCamera stores this target as a pointer; you can just dereference this pointer and use it inside of get_C) - it has a notion of the world y-axis being "up" i call the PLANE_CAMERA an ArbitraryCamera - by this i just mean that we specify the rotation and translation directly (the consturction really isn't much) -- for (very inefficient) convenience, we'll specify the rotation as a mat4
sidenote: sometimes we talk about pixel coordinates (especially when writing a GUI) - e.g., the lower left is $(0, 0)^T$ and the upper right is $(W, H)^T,$ where $W$ and $H$ are the screen width and height in pixels -- note: another common choice would be to make $(0, 0)^T$ the upper left and $(W, H)^T$ to lower right. sidenote: $\mathbf{VM}$ is sometimes called the model-view matrix (reading right to left) - note $\mathbf{p}_\text{camera} = \mathbf{VM}\mathbf{p}_\text{model}$ sidenote: instead of writing $\mathbf{M}, \mathbf{C}, \mathbf{V}, \mathbf{P}$, we could adopt a more explicit and general-purpose notation using left and right subscripts, where the $\mathbf{F}$ is read as "from" - model matrix ${}_\text{world}\mathbf{F}_\text{model} $, where $\mathbf{p}_\text{world} = {}_\text{world}\mathbf{F}_\text{model} \ \mathbf{p}_\text{model}$ - camera matrix ${}_\text{world}\mathbf{F}_\text{camera}$, where $\mathbf{p}_\text{world} = {}_\text{world}\mathbf{F}_\text{camera} \ \mathbf{p}_\text{camera}$ - view matrix ${}_\text{camera}\mathbf{F}_\text{world}$, where $\mathbf{p}_\text{camera} = {}_\text{camera}\mathbf{F}_\text{world} \ \mathbf{p}_\text{world}$ - projection matrix ${}_\text{NDC}\mathbf{F}_\text{camera} $, where $\mathbf{p}_\text{NDC} = {}_\text{NDC}\mathbf{F}_\text{camera} \ \mathbf{p}_\text{camera}$ in this notation, it is clear that, e.g., $\mathbf{p}_\text{NDC} = {}_\text{NDC}\mathbf{F}_\text{camera} \ {}_\text{camera}\mathbf{F}_\text{world} \ {}_\text{world}\mathbf{F}_\text{model} \ \mathbf{p}_\text{model}$ in code you could write mat4 NDC_from_camera;, etc.
a. mini world note: i expect questions marked with a * to be substantially trickier note: when i say, e.g., "very similar to MineCraft," i mean that the mouse controls (in this case, moving the mouse) should "feel" the same - don't forget to check extreme cases!--what happens if you zoom in real far?--what happens if you drag down really far?
HINT (what to expect) - correct answers can be *short* :) - 🚨 you should NOT have to call trig functions directly (unless you want to) -- my solution never calls sin or cos - ✨ the M4_*(...) functions are your friends here -- you can think about, e.g. M4_RotationAboutXAxis(alpha) as being $\mathbf{R}^{x}_{\alpha} = \mathbf{R}^{x}_{\alpha} \mathbf{I}$ --- i.e. our construction starts with the camera matrix being the identity (origin at the world origin, axes at the world axes) and then rotates this coordinate system about the world x-axis by angle $\alpha$ - transformVector(...) can be nice too, but is less crucial - take your time to make the controls feel good (check extreme cases!--what happens when you zoom really far, drag the mouse really far down, etc.)
HINT (useful functions and fields) CLAMP(...) cross(...) M4_Translation(...) M4_Rotation*(...) M4_xyzo(...) globals.mouse_change_in_position_NDC globals.mouse_wheel_offset normalized(...) transformVector(...)

i. compute $\mathbf{C}$ for the various cameras (more details in the code) - complete orbit_camera_get_C(...) - complete fps_camera_get_C(...) * complete tracking_camera_get_C(...) - complete arbitrary_camera_get_C(...) ii. implement controls for the various camera (more details in the code) - complete orbit_camera_move(...) // should feel very similar to Camera3D (except that the world origin should remain in the exact center of your screen--i.e. we have no notion of offsetting the origin) * complete fps_camera_move(...) // should feel ~exactly like MineCraft * complete arbitrary_camera_move(...) // should feel like you're flying a plane (ish) iii. complete draw_basic_box_with_fake_shadows(...) // should look like the sun is directly overhead iv. creative coding (choose 3+ of the following suggestions or equivalent) - add your bouncy castle from hw03 - let the human sprint (run faster) when holding shift - let the human jump with the spacebar * let the human shoot bullets with the left mouse * make the tracking camera shoot bullets automatically * let the plane shoot rockets with the left mouse; the rockets could explode on impact with the ground? * add an AI to make the human walk when selected_camera != HUMAN_CAMERA * add an AI to make the plane fly around when selected_camera != PLANE_CAMERA - add mesh(es) for the human (perhaps a teapot could be nice to start) - add mesh(es) for the plane (maybe with a spinning propellor?) - add mesh(es) for the tracking camera (eye of sauron could be cool) * let the human jump onto obstacles (implement box-box collisions)
note: emoji problems are extra credit and are *not* required to get an A on this homework
🎱. the arcball Implement the matrix-version (not quaternions, unless you really want to) of the rather interesting arcball camera controls. Explanation is in Section 2 here. note: the arcball is an "arbitrary camera" with no translation; it has NO notion of the world y-axis being "up"