Rotate camera left and right

Description

We can move the camera around, but we're always looking forward down the +Z axis. We want to be able to rotate the camera so we can look at different angles.

With this change we introduce the camera heading. We're going to use heading as defined [here]:

The heading value is in radians, so incrementing the heading by 2 * PI = 6.28 will return the camera to the original value.

To use the heading, we fold it into our world to camera transformation matrix. First we move the world so that the camera is at the origin, then we rotate the world so that our camera is facing forward again. Since we're looking left and right, we're effectively spinning the world around the Y axis.

To change the heading from user input, we're now listening for mouse movement events. These events include the amount the mouse moved in the X and Y directions, and for now we only care about the X value.

The units of the values are unspecified (maybe pixels?), but adding the X-delta to the heading value causes the camera to spin way too fast. We can add in a factor that represents the mouse sensitivity to bring it down to a reasonable rotation speed.

Screenshot

Commands

git clone git@github.com:atsheehan/iridium
cd iridium
git checkout 0203708acf5bb7be8647a6e946a16402d236c6e3
cargo run --release

Code Changes

Modified shaders/cube.vertGitHub

@@ -4,6 +4,7 @@
44 const float FAR = 10000.0;
55
66 uniform vec3 camera_position;
7+ uniform float camera_heading;
78 uniform vec3 position;
89 uniform float aspect_ratio = 1.0;
910
@@ -83,12 +84,20 @@
8384 texture_coordinates[5] = vec2(0.0, 0.0);
8485 vertex_tex_coord = texture_coordinates[gl_VertexID % 6];
8586
86- mat4 world_to_camera_transform =
87+ mat4 camera_translation =
8788 mat4(1.0, 0.0, 0.0, -camera_position.x,
8889 0.0, 1.0, 0.0, -camera_position.y,
8990 0.0, 0.0, 1.0, -camera_position.z,
9091 0.0, 0.0, 0.0, 1.0);
9192
93+ mat4 camera_heading_rotation =
94+ mat4(cos(camera_heading), 0.0, -sin(camera_heading), 0.0,
95+ 0.0, 1.0, 0.0, 0.0,
96+ sin(camera_heading), 0.0, cos(camera_heading), 0.0,
97+ 0.0, 0.0, 0.0, 1.0);
98+
99+ mat4 world_to_camera_transform = camera_translation * camera_heading_rotation;
100+
92101 mat4 camera_to_clip_transform =
93102 mat4(1.0, 0.0, 0.0, 0.0,
94103 0.0, aspect_ratio, 0.0, 0.0,
@@ -4,6 +4,7 @@
4 const float FAR = 10000.0;
5
6 uniform vec3 camera_position;
 
7 uniform vec3 position;
8 uniform float aspect_ratio = 1.0;
9
@@ -83,12 +84,20 @@
83 texture_coordinates[5] = vec2(0.0, 0.0);
84 vertex_tex_coord = texture_coordinates[gl_VertexID % 6];
85
86- mat4 world_to_camera_transform =
87 mat4(1.0, 0.0, 0.0, -camera_position.x,
88 0.0, 1.0, 0.0, -camera_position.y,
89 0.0, 0.0, 1.0, -camera_position.z,
90 0.0, 0.0, 0.0, 1.0);
91
 
 
 
 
 
 
 
 
92 mat4 camera_to_clip_transform =
93 mat4(1.0, 0.0, 0.0, 0.0,
94 0.0, aspect_ratio, 0.0, 0.0,
@@ -4,6 +4,7 @@
4 const float FAR = 10000.0;
5
6 uniform vec3 camera_position;
7+ uniform float camera_heading;
8 uniform vec3 position;
9 uniform float aspect_ratio = 1.0;
10
@@ -83,12 +84,20 @@
84 texture_coordinates[5] = vec2(0.0, 0.0);
85 vertex_tex_coord = texture_coordinates[gl_VertexID % 6];
86
87+ mat4 camera_translation =
88 mat4(1.0, 0.0, 0.0, -camera_position.x,
89 0.0, 1.0, 0.0, -camera_position.y,
90 0.0, 0.0, 1.0, -camera_position.z,
91 0.0, 0.0, 0.0, 1.0);
92
93+ mat4 camera_heading_rotation =
94+ mat4(cos(camera_heading), 0.0, -sin(camera_heading), 0.0,
95+ 0.0, 1.0, 0.0, 0.0,
96+ sin(camera_heading), 0.0, cos(camera_heading), 0.0,
97+ 0.0, 0.0, 0.0, 1.0);
98+
99+ mat4 world_to_camera_transform = camera_translation * camera_heading_rotation;
100+
101 mat4 camera_to_clip_transform =
102 mat4(1.0, 0.0, 0.0, 0.0,
103 0.0, aspect_ratio, 0.0, 0.0,

Modified src/main.rsGitHub

@@ -8,7 +8,7 @@
88 use render::Renderer;
99 use time::FrameCounter;
1010 use winit::{
11- event::{ElementState, Event, KeyEvent, WindowEvent},
11+ event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
1212 event_loop::{ControlFlow, EventLoop},
1313 keyboard::{KeyCode, PhysicalKey},
1414 };
@@ -94,6 +94,12 @@
9494 _ => {}
9595 };
9696 }
97+ Event::DeviceEvent {
98+ event: DeviceEvent::MouseMotion { delta: (dx, dy) },
99+ ..
100+ } => {
101+ world.update_camera_direction(dx as f32, dy as f32);
102+ }
97103 Event::WindowEvent {
98104 event: WindowEvent::RedrawRequested,
99105 window_id,
@@ -8,7 +8,7 @@
8 use render::Renderer;
9 use time::FrameCounter;
10 use winit::{
11- event::{ElementState, Event, KeyEvent, WindowEvent},
12 event_loop::{ControlFlow, EventLoop},
13 keyboard::{KeyCode, PhysicalKey},
14 };
@@ -94,6 +94,12 @@
94 _ => {}
95 };
96 }
 
 
 
 
 
 
97 Event::WindowEvent {
98 event: WindowEvent::RedrawRequested,
99 window_id,
@@ -8,7 +8,7 @@
8 use render::Renderer;
9 use time::FrameCounter;
10 use winit::{
11+ event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
12 event_loop::{ControlFlow, EventLoop},
13 keyboard::{KeyCode, PhysicalKey},
14 };
@@ -94,6 +94,12 @@
94 _ => {}
95 };
96 }
97+ Event::DeviceEvent {
98+ event: DeviceEvent::MouseMotion { delta: (dx, dy) },
99+ ..
100+ } => {
101+ world.update_camera_direction(dx as f32, dy as f32);
102+ }
103 Event::WindowEvent {
104 event: WindowEvent::RedrawRequested,
105 window_id,

Modified src/render.rsGitHub

@@ -186,6 +186,8 @@
186186 pub(crate) fn set_camera(&mut self, camera: &Camera) {
187187 self.cube_program
188188 .set_uniform_vec3("camera_position", camera.position());
189+ self.cube_program
190+ .set_uniform_f32("camera_heading", &camera.heading());
189191 }
190192 }
191193
@@ -186,6 +186,8 @@
186 pub(crate) fn set_camera(&mut self, camera: &Camera) {
187 self.cube_program
188 .set_uniform_vec3("camera_position", camera.position());
 
 
189 }
190 }
191
@@ -186,6 +186,8 @@
186 pub(crate) fn set_camera(&mut self, camera: &Camera) {
187 self.cube_program
188 .set_uniform_vec3("camera_position", camera.position());
189+ self.cube_program
190+ .set_uniform_f32("camera_heading", &camera.heading());
191 }
192 }
193

Modified src/world.rsGitHub

@@ -1,5 +1,6 @@
11 use crate::math::Vec3;
22
3+ const MOUSE_SENSITIVITY: f32 = 0.001;
34 const MOVE_SPEED: f32 = 0.5;
45
56 pub(crate) struct World {
@@ -13,6 +14,7 @@
1314 let camera = Camera {
1415 position: Vec3(0.0, 0.0, 0.0),
1516 velocity: Vec3(0.0, 0.0, 0.0),
17+ heading: 0.0,
1618 };
1719
1820 Self {
@@ -84,6 +86,10 @@
8486 self.camera.velocity = self.camera.velocity.set_y(0.0);
8587 }
8688
89+ pub(crate) fn update_camera_direction(&mut self, dx: f32, _dy: f32) {
90+ self.camera.heading += dx * MOUSE_SENSITIVITY;
91+ }
92+
8793 pub(crate) fn camera(&self) -> &Camera {
8894 &self.camera
8995 }
@@ -92,10 +98,15 @@
9298 pub(crate) struct Camera {
9399 position: Vec3,
94100 velocity: Vec3,
101+ heading: f32,
95102 }
96103
97104 impl Camera {
98105 pub(crate) fn position(&self) -> &Vec3 {
99106 &self.position
100107 }
108+
109+ pub(crate) fn heading(&self) -> f32 {
110+ self.heading
111+ }
101112 }
@@ -1,5 +1,6 @@
1 use crate::math::Vec3;
2
 
3 const MOVE_SPEED: f32 = 0.5;
4
5 pub(crate) struct World {
@@ -13,6 +14,7 @@
13 let camera = Camera {
14 position: Vec3(0.0, 0.0, 0.0),
15 velocity: Vec3(0.0, 0.0, 0.0),
 
16 };
17
18 Self {
@@ -84,6 +86,10 @@
84 self.camera.velocity = self.camera.velocity.set_y(0.0);
85 }
86
 
 
 
 
87 pub(crate) fn camera(&self) -> &Camera {
88 &self.camera
89 }
@@ -92,10 +98,15 @@
92 pub(crate) struct Camera {
93 position: Vec3,
94 velocity: Vec3,
 
95 }
96
97 impl Camera {
98 pub(crate) fn position(&self) -> &Vec3 {
99 &self.position
100 }
 
 
 
 
101 }
@@ -1,5 +1,6 @@
1 use crate::math::Vec3;
2
3+ const MOUSE_SENSITIVITY: f32 = 0.001;
4 const MOVE_SPEED: f32 = 0.5;
5
6 pub(crate) struct World {
@@ -13,6 +14,7 @@
14 let camera = Camera {
15 position: Vec3(0.0, 0.0, 0.0),
16 velocity: Vec3(0.0, 0.0, 0.0),
17+ heading: 0.0,
18 };
19
20 Self {
@@ -84,6 +86,10 @@
86 self.camera.velocity = self.camera.velocity.set_y(0.0);
87 }
88
89+ pub(crate) fn update_camera_direction(&mut self, dx: f32, _dy: f32) {
90+ self.camera.heading += dx * MOUSE_SENSITIVITY;
91+ }
92+
93 pub(crate) fn camera(&self) -> &Camera {
94 &self.camera
95 }
@@ -92,10 +98,15 @@
98 pub(crate) struct Camera {
99 position: Vec3,
100 velocity: Vec3,
101+ heading: f32,
102 }
103
104 impl Camera {
105 pub(crate) fn position(&self) -> &Vec3 {
106 &self.position
107 }
108+
109+ pub(crate) fn heading(&self) -> f32 {
110+ self.heading
111+ }
112 }