Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198
» CS 488: Lecture 10 – Camera

teaching machines

CS 488: Lecture 10 – Camera

March 1, 2021 by . Filed under
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
graphics-3d, lectures, spring-2021.

Dear students:

All the renderers we’ve written so far have put a single object at the origin. The only thing we’ve done is spin that object around. It’s time to build up a larger world that the user can move around in. What new features of our graphics API do we need to construct an immersive world? None. We will use transforms to move the portion of the world that we want to see in the unit cube that WebGL expects. To make those transformations feel a little more natural, we will introduce the notion of a camera into our scenes.

Updated Pipeline

As we introduce the notion of a mobile viewer in our 3D scene, we will find some benefit in adding one more stage to our transformation pipeline. That stage is called eye space or camera space or view space. All told, our models will go through this sequence of spaces:

When we talk about perspective, clip space and NDC space will be distinguished. With orthographic projections, they are the same thing.

In eye space, the eye is at the center and the rest of the world is situated around it. Some of the calculations we’ll need for advanced lighting are easier and cheaper to do in eye space. The benefit we’ll see today is purely logical. It’s convenient to think about the camera being an entity in our scene.

In adding a space, we also need another matrix. To take a vertex from model coordinates all the way to clip/NDC space in the vertex shader, we’ll write:

gl_Position = eyeToClip * worldToEye * modelToWorld * vec4(position, 1.0);

In the future, I think I’ll swap the order of names in my matrices. This is easier to spot check:

gl_Position = clipFromEye * eyeFromWorld * worldFromModel * vec4(position, 1.0);

The modelToWorld matrix situates a model within the larger 3D world. Each model in our scene might have its own modelToWorld matrix. The worldToEye matrix transforms world coordinates into a coordinate system where the eye is at the origin and looking along the negative z-axis. The eyeToClip matrix transforms eye space coordinates relative to the unit cube that WebGL expects.

Camera

To ease the generation of the worldToEye matrix, we will create an abstraction representing a camera or viewer in our scene. It will turn properties of the viewer into a transformation matrix that orients the world around the camera. What properties do we need to know about a viewer in our scenes? At least these:

But these are not enough to uniquely identify a camera’s view of the world. There are an infinite number of possible viewers at some location looking in some direction. What more information do we need to know?

With these three pieces of information, we can form our matrix. The construction process relies on these amazing and not obvious properties of rotation matrices:

Let’s work through the interface of our Camera class.

Constructor

When a client creates a camera, we’ll ask them to provide these parameters, which we’ll use to determine our three axes:

Right off the bat we can figure out one of our axes. Surprisingly, it’s not the up axis. The world’s up vector is not always the same as the camera’s. As we walk up and down slopes, we tilt our view. Rather, it’s the z-axis we can figure out.

Recall that in WebGL the positive z-axis points out of the screen. What vector in world space points out at us? This one: (from - to).normalize(). That’s the vector that starts at the focal point and points at the camera’s location.

Later we’re going to find it useful to have the inverse of this vector lying around, so we are going to store a flipped version of this direction:

this.forward = (to - from).normalize()

The forward vector is our focal direction. It tells us which way the camera is pointing. Its inverse -this.forward is the third row of our matrix. There are still two more rows to figure out. We push the remaining logic into the orient helper method, which we’ll call from the constructor and from some of our other methods that move the camera around the world.

Orient

In the orient helper method, we know the camera’s location and forward direction and the world’s up axis. This information is enough figure out the other axes for our matrix.

What axis in the world points right and should become the x-axis in eye space? We cross our two vectors to figure that out:

this.right = this.forward.cross(this.worldUp).normalize();

What axis in the world do we want to become the y-axis? Well, we know two of the axes. And we know that all of the axes should be indepedent. We cross the two known axes to get the camera’s up vector:

this.up = this.right.cross(this.forward);

With all three axes figured out, we can construct a rotation matrix:

rotation = |this.right.x     this.right.y     this.right.z     0|
           |this.up.x        this.up.y        this.up.z        0|
           |-this.forward.x  -this.forward.y  -this.forward.z  0|
           |0                0                0                1|

Orienting the camera is not just a matter of rotation. We must also put the camera at the origin, which we achieve by subtracting off the camera’s location. Our complete orienting matrix is the product of a rotation and translation:

this.matrix = rotation * Matrix4.translate(-this.from.x, -this.from.y, -this.from.z)

The renderer will access this matrix and use it to convert world coordinates into eye space coordinates.

Strafe

Once we have a camera, we might as well give it some operations for moving around in the world. A common action is to strafe left or right, which we achieve by moving the camera’s location along its right axis:

this.from = this.from.add(this.right.scalarMultiply(distance))

After updating the camera’s location, we reorient the camera by calling this.orient again.

Advance

Another common action is to move the camera forward or backward, which we achieve by moving the camera’s location along the forward axis:

this.from = this.from.add(this.forward.scalarMultiply(distance))

After updating the camera’s location, we reorient the camera.

Yaw

Besides moving the camera, we also turn it. In an airplane, a turn left or right is called a yaw. We yaw the camera by rotating the forward direction around the world’s up vector:

this.forward = Matrix4.rotateAroundAxis(this.worldUp, degrees) * this.forward;

In your implementation, make sure that your types match. If rotateAroundAxis expects a Vector4, you’ll need to convert this.forward.

After updating the camera’s forward vector, we reorient the camera.

Pitch

In an airplane, a turn up or down is called a pitch. We pitch the camera by rotating the forward direction around the right vector:

this.forward = Matrix4.rotateAroundAxis(this.right, degrees) * this.forward;

After updating the camera’s forward vector, we reorient the camera.

Renderer

Inside our renderer, we make an instance of Camera and initialize it. We need to update our vertex shader and uniforms to reflect our new matrix. To get the camera moving around, we’ll need event handlers for both the keyboard and mouse.

Keyboard

In our renderer, we implement WASD inputs to strafe and advance the camera:

window.addEventListener('keydown', event => {
  if (event.key === 'a') {
    camera.strafe(-0.1);
    render();
  } else if (event.key === 'd') {
    camera.strafe(0.1);
    render();
  } else if (event.key === 'w') {
    camera.advance(0.1);
    render();
  } else if (event.key === 's') {
    camera.advance(-0.1);
    render();
  }
});

Some games use Q and E to yaw the camera, but we’ll use the mouse for that.

Mouse

First-person games that use the mouse typically hide the mouse. Otherwise the mouse runs into the walls of the viewport or monitor. JavaScript provides the PointerLock API to turn the mouse cursor off. When the cursor is hidden, the mouse events report the relative movement between events rather than the mouse’s absolute location. We use the x-delta to yaw the camera and the y-delta to pitch.

window.addEventListener('mousedown', () => {
  document.body.requestPointerLock();
});

window.addEventListener('mousemove', event => {
  if (document.pointerLockElement) {
    camera.yaw(-event.movementX * 0.1);
    camera.pitch(-event.movementY * 0.1);
    render();
  }
});

TODO

Here’s your TODO list:

See you next time.

Sincerely,

P.S. It’s time for a haiku!

Adam and Eve gamed
Guess their favorite genre
First-person shooters